DDoS 攻击分类

0x0 前言

Oh please don’t let me die, waiting for you touch~~

0x1 预备

在下面的分析中,将使用神器 Scapy 模拟攻击流量,并使用 tcpdump 截取流量。攻击环境如下:

  • Kali 2.0 攻击者,IP 192.168.12.132
  • Ubuntu 14.04 服务端,IP 192.168.12.131

使用 scapy 命令进入交互式终端,例如发送一个 ICMP 包:

>>> send( IP(dst='192.168.12.131')/ICMP()/'I am here.', count=1 )

或者向主机循环发送数据包,直到收到终止信号,随机选择目标端口:

>>> send( IP(dst='192.168.12.131')/TCP(dport=RandShort(), flags='S'), loop=1 )

这里推荐一篇极好的文章:Black Hat Python: Infinite possibilities with the Scapy Module

0x2 TCP 三次握手和四次挥手

首先是 TCP 连接建立前的三次握手,这个是老生常谈了。

1)客户端向服务端发送一个 SYN 置位的数据段 // 并初始化一个随机序列号于 seq 字段中
2)服务端回复 SYN / ACK 置位的数据段,连接状态变为 SYN_RCVD // 同样初始化一个序列号,ack 字段为客户端的 seq 序列号加一
3)客户端回复 ACK 置位的数据段 // seq 为最初的序列号自增,ack 为服务端的 seq 序列号加一

自此连接建立完成,双方开始接收或发送数据。

由于 TCP 是全双工的传输协议,所有连接断开时需要进行四次握手,假设客户端为主动请求关闭的一方:

1)客户端发送 FIN / ACK 置位的数据段
2)服务端收到数据,响应 ACK 数据段
3)服务端的接收或发送通道的数据处理完成,向客户端发送 FIN / ACK 数据段
4)客户端收到数据段,发送 ACK 包给服务端,连接完全关闭

TCP 连接断开:

tcp_connection_close

0x3 基于 TCP 握手过程的攻击

DDoS 攻击的类型非常多,针对 TCP 协议的攻击方式也是错综复杂,这里对主要的攻击类型进行分析,笔者将其分为 TCP 握手过程完整握手后 的攻击。

1)TCP SYN Flood

TCP SYN 洪流攻击,是一种主流的 DDoS 攻击类型,效率高,治肾亏不含糖。

1)攻击端向目标主机发送大量 SYN 置位的数据段
2)如果目标主机端口开启,则回复 SYN + ACK 数据段,同时进入 SYN_RECV 状态
3)攻击端收到回复后将其丢弃,使目标主机处于 “半连接” (half-open connection)状态,目标主机会尝试重新发送响应包直至超时

当目标主机的服务连接数超过限制,后续的连接便会被丢弃,造成服务瘫痪。攻击模拟:

1)先使用 iptables 规则将攻击目标的 SYN / ACK 包丢弃,避免本机回复 RST 包终止握手会话:

iptables -A INPUT -s 192.168.12.131 -j DROP

2)发送攻击流量

>>> send( IP(dst='192.168.12.131')/TCP(dport=80, flags='S') )

3)服务器端收到的数据:

tcp_syn_flood_attack

首先攻击者先向服务器 80 端口发送 SYN 数据段,服务器返回 SYN / ACK 数据段(标志 [S.] 中的点代表 ACK 置位),该数据包被攻击者直接丢弃。由于攻击者没响应 ACK 包,服务器会尝试重新发送响应数据,造成资源消耗。

2)TCP ACK / SYN + ACK / FIN / RST Flood

这四种攻击分别是:

  • TCP ACK Flood
  • TCP SYN ACK Flood
  • TCP FIN Flood
  • TCP RST Flood

无论是三次握手的攻击,还是四次握手的攻击,其原理都是一致的。这种攻击类似于 TCP SYN Flood,虽然效果没有 SYN 洪流药效高,但是通过大片僵尸网络发动攻击同样会造成服务瘫痪。

1)攻击者向目标主机发送 ACK 或 SYN + ACK 或 FIN 置位的数据段
2)目标主机首先检查当前连接是否已建立,如果连接状态无效,则响应 RST 数据段

以 TCP ACK Flood 为例,模拟攻击:

1)发送 ACK 置位的数据:

>>> send( IP(dst='192.168.12.131')/TCP(dport=80, flags='A') )

2)服务器上捕捉的流量:

tcp_ack_flood_attack

如图,攻击主机向 80 端口发送 ACK 数据段,服务器先检查队列里是否已经建立了有效的连接,发现为无效连接后,响应 RST 数据段。

可以看到主要针对 服务器对连接状态进行检查的过程 进行攻击,消耗资源。

0x4 完整握手后的 TCP 攻击

这种类型的攻击需要先通过三次握手建立完整的连接,对于攻击者来说这种攻击成本会比较大。因此这种类型的攻击相对于 TCP 握手过程的攻击,更难防范:因为看起来就像一个正常的用户请求。

1)HTTP Flood / HTTPS Flood

HTTP / HTTPS 洪流,也称为 Challenge Collapsar,即 CC 攻击。其原理比较简单,通过僵尸网络向目标发送大量的 HTTP 请求,服务器后端脚本频繁从 后台数据库硬盘 读取数据,造成资源开销。

对于 HTTPS 来说,传输时需要对数据进行加密解密,其消耗的资源会略高于 HTTP 连接。

Collapsar 是绿盟的 DDoS 设备防御设备

2) TCP PSH + ACK Flood

PUSH 标识用于通知 TCP 连接的接受方,不管缓冲区是否已经填满,即刻将数据交付应用层并清空缓冲区,之后发送 ACK 包进行确认。

3)Sockstress

翻阅了维基百科和相关资料,Sockstress 的攻击方式可分为多种:

1. 客户端通过不同源端口向目标主机的指定服务发起大量连接
2. 设置 TCP 接收窗口为零
3. 设置非常小的接收窗口

无论使用 UDP 还是 TCP 进行通信,都需要设置发送/接受缓冲区,而 TCP 实现了流量控制,于是从 TCP 中抽象出来窗口的概念。窗口大小表示可用缓冲区大小,使用窗口的滑动(缓冲区指针移动),关闭(部分缓冲区被填满,并且这些数据还未进行确认,不可覆盖)来表示数据收发操作。

窗口大小在 TCP 握手时进行协商,并且在进行数据传输的时候也会动态变化。

关于更多滑动窗口的细节,可以参阅 《计算机网络 - 自顶向下的学习方法》

当接收方缓冲区填满时,或者网络太过于拥塞,可以发送窗口大小为 0 的数据段告知对方:你这速度比香港记者还快,先等等。

对方收到请求后,会暂停发送数据,同时定时发送一个 Probe 询问接收方是否就绪,直到超时连接丢弃。

Sockstress 便是利用此方式,发送一个窗口大小为零(或者非常小)的数据段,使目标主机持续发送 Probe 包,消耗资源。

首先必须进行三次握手,在最后的 ACK 包搭载 HTTP 请求,并将窗口大小设置为 0:

from scapy.all import *

def sockstress(target, target_port, src_port = 2333):
    init_seq = 1000
    # send the syn packet
    result = sr1( IP(dst=target)/TCP(sport=src_port, dport=target_port, flags='S', seq=init_seq) )
    # send the ack packet with the Zero size of window
    send( IP(dst=target)/TCP(sport=src_port, dport=target_port, flags='A', seq=init_seq + 1,
                ack=result[TCP].seq + 1, window=0)/'GET / HTTP/1.1\r\n\r\n' )

if __name__ == '__main__':
    # stress exam
    sockstress('192.168.12.131', 80);

发送 payload:

ddos_sockstress_attack.png

服务器收到停等请求后,每隔一段时间向客户端发送一个 Probe 请求包,直到超时(两次测试后,大概为 187s):

ddos_sockstress.png

0x5 IP Fragment 攻击

先说分片。

一个 IP 数据包最大能容纳 65535 个字节(包括 IP 头部),但是每次传输的数据大小,取决于链路层的限制。例如以太网帧最大可容纳 1500 字节数据,就是 MTU 啦。

所以当整个 IP 数据报的大小超过 MTU 时,网络层需要对其进行分片,以便在链路层上传输。

internet-protocol.png

IP 协议格式,图片来自 http://www.swiftutors.com/internet-protocol.html

分片涉及三个重要的字段:

1. ID 字段,发生分片时,每个分片都具有相同的 ID,目标主机通过相同 ID 重组数据报
2. Flags,3 bits 大小。最低位保留,第二位 Don't Fragment 标识,分片需要将其置 0,最后为 More Fragment 标识,置位代表后面还有分片,否则为最后一个分片
3. Offset,分片偏移,挑出来详细讲。

首先,下面的描述都是基于以太网帧,即 MTU 为 1500。

分片偏移使用 13 位表示,等于前部分分片累计大小除以 8(不包含 IP 头大小)

例如发送总大小为 4000 字节的数据包,假设 IP 头固定为 20 字节,则每个分片最大能携带 1480 字节数据,需要 (4000 - 20) / 1480 =~ 2.6 即三个分片。第一个分片偏移为 0/8 = 0,接着为 1480/8 = 185,最后为 2960/8 = 370。

为啥分片偏移还要除以 8?

因为偏移用 13 位来表示,最大值为 8191。按照普通编号,超过 6 个分片时,其偏移为 1480 * 6 = 8880。明显,分片偏移无法容纳这么大的数字。

再谈攻击。

To be continue…