2017-10-30 计算机网络
- 概述
- 1. 协议型攻击
- 全连接与半连接
- SYN Flood (洪泛) 攻击
- hping3
- SYN Flood (洪泛) 攻击防范
- 1. 手动处理
- 2. 增大半连接队列的容量
- 3. 减少连接 SYN_RECV 状态失败时内核自动重试次数
- 4. 直接拒绝
- 5. TCP SYN Cookies
- 2. 反射型攻击
- 攻击特点
- 攻击防范
- 扩展阅读
概述
分布式拒绝服务(DDoS)攻击是一种常见的网络攻击形式,攻击者通过向目标服务端发送大量的请求,使目标服务端无法进行网络连接,无法正常提供服务。
DDoS 攻击通常是由大量的分布在全球各地的 “僵尸” 计算机(也称为 “肉鸡” 或 “僵尸网络”)共同发起请求,这些计算机可能被感染了恶意软件,攻击者可以通过远程指令控制来发起攻击,从而达到掩饰攻击来源、加大攻击威力的目的。
1. 协议型攻击
这类攻击类型主要利用网络协议的漏洞或者设计缺陷,消耗目标服务端的资源,其中最著名的莫过于 TCP SYN Flood 攻击,几乎所有大型网站都遭受过这种类型攻击,造成几个小时到几天的服务不可用时间。
全连接与半连接
在描述 TCP SYN Flood 的攻击原理和如何防范之前,先来简单介绍一下 TCP 的全连接和半连接。

全连接是指服务端收到了客户端的 ACK,完成了 TCP 三次握手,然后会把这个连接移动到全连接队列中,全连接队列中的套接字,需要被应用 accept() 系统调用出队取走,服务端才可以开始处理客户端的请求。
也就是说,全连接队列包含了所有完成了三次握手,但还未被应用 accept() 取走的连接,默认情况下,如果全连接队列已满,客户端发送的 ACK 报文会被直接丢掉 (取决于 net.ipv4.tcp_abort_on_overflow 参数设置),返回 connection reset by peer 错误。
半连接是指还没有完成 TCP 三次握手的连接,连接只进行了一半,也就是服务端收到了客户端的 SYN 控制报文之后,服务端会把这个连接放入半连接队列中,然后再向客户端发送 SYN + ACK 控制报文。
和全连接队列一样,半连接队列也有溢出处理机制,默认情况下,如果半连接队列已满,客户端发送的 SYN 报文会被直接丢掉。
SYN Flood (洪泛) 攻击
SYN Flood (洪泛) 攻击就是针对半连接队列的,攻击方不停地向服务端发起连接建立请求 (可以伪造 源 IP 地址 或者直接操作 “僵尸网络”),但是建立连接时只完成第一步,第二步中,当攻击方收到服务端的 SYN + ACK 之后,故意丢掉报文,然后什么也不做。但是服务端会等待客户端的 ACK 包,如果等待时间内未收到,服务端会进行首次重传,然后是第二次重传 …,直到重传次数超过上限制,操作系统会将该连接从半连接队列中删除。
这样重复执行执行攻击过程,如果操作系统删除速度小于半连接队列的增长速度,服务端的半连接队列很快就会被打满,这时其它正常的请求也无法和服务建立连接,攻击者的目的就达到了。
此外,SYN Flood 会导致软中断,CPU 使用率(softirq)飙升。

hping3
hping3 (Redis 作者开发的另外一个工具) 可以构造 TCP/IP 协议数据包,对系统进行安全审计、防火墙测试、DDoS 攻击测试等。
# -S 参数表示设置 TCP 协议的 SYN(同步序列号)
# -p 表示目的端口为 80
# -i u10表示每隔 10 微秒发送一个网络帧
# --rand-source 随机化源 IP 地址 (模拟更加真实的攻击)
# 当前主机: 192.168.0.2, 充当 “客户端” 发起 TCP 连接
$ hping3 -S -p 80 -i u10 192.168.0.30
# 在主机 192.168.0.30 抓包
$ tcpdump -i eth0 -n tcp port 80
# Flags [S] 表示这是一个 SYN 包
# 大量的 SYN 包表明,这是一个 SYN Flood 攻击
09:15:48.287047 IP 192.168.0.2.27095 > 192.168.0.30: Flags [S], seq 1288268370, win 512, length 0
09:15:48.287050 IP 192.168.0.2.27131 > 192.168.0.30: Flags [S], seq 2084255254, win 512, length 0
09:15:48.287052 IP 192.168.0.2.27116 > 192.168.0.30: Flags [S], seq 677393791, win 512, length 0
09:15:48.287055 IP 192.168.0.2.27141 > 192.168.0.30: Flags [S], seq 1276451587, win 512, length 0
09:15:48.287068 IP 192.168.0.2.27154 > 192.168.0.30: Flags [S], seq 1851495339, win 512, length 0

通过抓包输出,可以得出如下结果:
- 客户端 (攻击方) 构造大量的 SYN 包,请求建立 TCP 连接
- 服务端 (被攻击方) 收到 SYN 包后,会向源 IP (客户端) 发送 SYN + ACK 报文,并等待三次握手的最后一次 ACK 报文,直到超时
- 经过一段时间后,服务端的半连接队列会打满,从而无法建立新的 TCP 连接
SYN Flood (洪泛) 攻击防范
如前文所述,SYN Flood (洪泛) 主要攻击的 TCP 的半连接队列,所以 最重要的防范措施就在于: 如何保护半连接队列不被恶意连接打满?

- 服务端的 TCP 连接,会处于 SYN_RECEIVED 状态
- 查看 TCP 半开连接的方法,关键看 SYN_RECEIVED 状态的连接
$ netstat -n -p | grep SYN_RECV
1. 手动处理
手动处理包的核心思路: 先找到状态异常的包,然后手动进行限制或设置直接丢包处理。
首先,我们通过 netstat 命令找出状态为 SYN_RECEIVED 的数据包,然后分析其中异常的数据包。
# -n 表示不解析名字
# -p 表示显示连接所属进程
$ netstat -n -p | grep SYN_RECV
# 发现大量 SYN_RECV 状态的连接,并且源 IP 地址为 192.168.0.2
...
tcp 0 0 192.168.0.30:80 192.168.0.2:12503 SYN_RECV -
tcp 0 0 192.168.0.30:80 192.168.0.2:13502 SYN_RECV -
tcp 0 0 192.168.0.30:80 192.168.0.2:15256 SYN_RECV -
tcp 0 0 192.168.0.30:80 192.168.0.2:18117 SYN_RECV -
...
找出异常数据包的源 IP 地址后,要解决 SYN Flood 攻击的问题,只要丢掉相关的包就可以,可以使用 iptables 命令来完成。
$ iptables -I INPUT -s 192.168.0.2 -p tcp -j REJECT
当然,攻击者不会一个源 IP 地址进行攻击,所以 SYN Flood 攻击中的源 IP 地址并不是固定的,这时手动限制单个 IP 地址的方式可能就不适合了 (当然,也可以将上述命令操作转为自动化脚本)。除此之外,还可以通过限制 SYN 包的速率来实现。
# 限制 SYN 并发数为每秒 1 次
$ iptables -A INPUT -p tcp --syn -m limit --limit 1/s -j ACCEPT
# 限制单个 IP 在 60 秒建立的新连接数为 10
$ iptables -I INPUT -p tcp --dport 80 --syn -m recent --name SYN_FLOOD --update --seconds 60 --hitcount 10 -j REJECT
上述方法虽然可以限制单个攻击源 IP 地址发起的 SYN Flood 攻击,但是如果攻击者发起的是大规模 (僵尸网络) 的 SYN Flood 攻击,该方法可能就无效了。
因为在遭遇大规模的 SYN Flood 攻击时,服务端管理人员很可能无法 SSH 登录(SSH 也是基于 TCP 的)到服务端,更不可能执行上述的相关命令。
2. 增大半连接队列的容量
Linux 查看默认的半连接队列容量:
# 注意: 该数值在不同发行版、Linux 版本中可能不同
$ sysctl net.ipv4.tcp_max_syn_backlog
net.ipv4.tcp_max_syn_backlog = 512
例如将半连接队列容量增大到 65535:
$ sysctl -w net.ipv4.tcp_max_syn_backlog=65535
3. 减少连接 SYN_RECV 状态失败时内核自动重试次数
Linux 查看连接 SYN_RECV 状态失败时内核自动重试次数:
$ sysctl net.ipv4.tcp_synack_retries
net.ipv4.tcp_synack_retries = 5
将自动重试次数减少到 1:
$ sysctl -w net.ipv4.tcp_synack_retries=1
4. 直接拒绝
如果面对海量请求真的处理不过来,直接拒绝就行了。
$ sysctl net.ipv4.tcp_abort_on_overflow
net.ipv4.tcp_abort_on_overflow = 0
启动拒绝连接:
$ sysctl -w net.ipv4.tcp_abort_on_overflow=1
5. TCP SYN Cookies
在默认的 TCP 三次握手过程中,客户端向服务端发送 SYN 数据包,服务端收到之后应该回复一个 SYN + ACK 数据包,并进入 SYN_RECV 状态等待客户端的 ACK 确认,但是,如果攻击者向服务端发送大量的 SYN 数据包而不回复 ACK 确认,服务端的半连接队列就会被打满,无法接受正常的新连接。
TCP SYN Cookies 是一种专门防御 SYN Flood 攻击的方法,SYN Cookies 基于 TCP 连接四元组(包括源地址、源端口、目的地址、目的端口)以及一个加密种子(如系统启动时间),计算出一个哈希值(SHA1),这个哈希值称为 Cookie。
Cookie 就被用作 ISN (Initial Sequence Number),来应答 SYN + ACK 报文,并释放连接状态,当客户端发送完三次握手的最后一次 ACK 后,服务端就会再次计算(反向)这个哈希值,确认是上次返回的 SYN + ACK 的应答报文,然后才会进入 TCP 的连接状态。
开启 TCP SYN Cookies 之后,就不需要维护半连接状态了,自然也就不存在半连接队列的容量限制问题了。
Linux 开启 TCP SYN Cookies:
# 临时配置,重启后配置会丢失
$ sysctl -w net.ipv4.tcp_syncookies=1
net.ipv4.tcp_syncookies = 1
需要注意的是:开启 TCP syncookies 后,内核选项 (半连接容量) net.ipv4.tcp_max_syn_backlog 失效。
如果需要将配置持久化,应该写入 /etc/sysctl.conf 文件:
$ vim /etc/sysctl.conf
# 对应配置修改如下
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_synack_retries = 1
net.ipv4.tcp_max_syn_backlog = 1024
# 配置持久化(动态生效)
$ sysctl -p
2. 反射型攻击
反射型 DDoS 攻击也称为 (流量) 放大型攻击。
攻击者向服务端发送大量的数据包,占用服务端的带宽和资源,造成目标服务端无法正常提供服务,例如 UDP 洪泛、ICMP 洪泛。
UDP 洪泛: 攻击者向目标服务发送大量的 UDP 数据报文试图填充服务端的可用端口,服务端如果没有找到指定端口,会返回 “Destination Unreachable” 应答报文,在海量 UDP 请求下,服务端就会被攻击流量淹没。
ICMP 洪泛:攻击者向目标服务端发送大量的 ICMP ECHO, 也就是 ping 请求 数据包,目标服务端会耗费大量的 CPU 资源去处理和响应,从而无法响应正常请求。
攻击者也可以利用利用第三方服务或设备,放大攻击流量,典型的如 DNS 攻击。攻击者利用开放的 DNS 服务端向目标系统发送大量的 DNS 查询请求,通过伪造源 IP 地址为被攻击目标,使被攻击目标系统收到大量的 DNS 响应数据包,占用其带宽和服务端资源。
举个小例子来说明这种情况:
+------+ +------+ +------+
| | | | ----> | |
| A | ---> | B | ----> | C |
| | | | ----> | |
+------+ +------+ +------+
攻击者 开放服务端 被攻击者
假设 B 服务端作为开放服务端,监听 UDP 端口 5678,当给这个 UDP 端口发送消息后会做具体的应答。
那么攻击者 A 只需要做以下事情:
- 发送数据给 B,并修改数据报文,伪装自己的源 IP 为 C
- B 接收到 A 的数据后,根据源 IP 将应答发送给 C (因为 A 发过来的报文中,源 IP 是 C)
- C 收到了来自 B 的 “反射性” 攻击,因为 B 将数据数量包放大了,原本 A 可能只发送了 “hello” 给 B,而 B 却发送了 N 个 “hello” 给 C, 如果类似 B 的开放服务端还不止一个,那么理论上,C 受到的攻击可以被无限放大
再举一个 DNS 放大攻击的小例子。
开启 Wireshark 抓包,然后执行下面的命令:
如图所示,发出去的 DNS 请求包数据只有 25 字节.

但是收到的 DNS 应答包数据居然有 3111 字节,足足放大了 124 倍,如果在请求包中伪造一个想要攻击的服务端 IP 地址,该地址就会莫名收到 DNS服务端 3111 字节的回复,利用这个放大攻击,只需要控制少量 (僵尸) 主机,就可以把一个大网站拖垮。

反射型攻击还可以对特定应用程序或服务发送攻击,例如 HTTP 洪泛 (发送大量 HTTP 请求),慢速 HTTP (攻击者向目标服务端发送大量的 HTTP 请求,但是每个请求的发送速度非常缓慢),薄弱环节攻击 (例如网站的关键字搜索功能,攻击者可以自动化伪造不同关键字,发送大量请求,严重影响网站的响应速度,严重时甚至直接使整个网站宕机)。
攻击特点
不论哪种类型的反射型攻击,其攻击方式都具有如下特点:
- 高流量: 攻击者发送大量的数据 (请求报文) 到目标服务端或网络,使其超出正常处理能力范围
- 大规模且难以识别: 攻击者操作数百或数千个攻击节点 (也称为 “肉鸡” 或 “僵尸网络”),形成大规模的攻击,而且攻击节点很难找出共性特征 (攻击流量和正常流量看起来几乎一样)
- 目的明确且无差别攻击: 攻击的目的通常是使目标服务端或网络不可用,所有会攻击目标服务端所有开放端口
- 持续性: 攻击行为可以持续数小时甚至数天
攻击防范
反射型攻击的单纯从技术上很难实现绝对防范,毕竟像 Github 这种级别的网站面对 DDoS 时也只能 “躺平”,目前主流的解决方法基本都是通过 “加钱” 的方式来解决的,例如堆硬件、搭软件、叠带宽等方面。
- 流量过滤和清洗: 使用 (云计算厂商提供的) 专业的 DDoS 防护设备或服务,对流量进行实时监测、过滤和清洗、识别并阻止异常流量
- 入侵检测和防御: 部署入侵检测系统 (IDS) 和入侵防御系统 (IPS),对网络流量进行实时监控和分析、识别并阻止异常流量
- 增加带宽和资源: 增加网络带宽和服务端资源,提高系统抵御大流量攻击的能力
- ISP: 与互联网服务提供商(ISP)合作,共同防范 DDoS 攻击
- CDN: 尽可能使用 CDN 分发流量,最大限度减少攻击对关键业务/服务的影响
- 限流: 关键业务/服务中对网络流量进行限制



















