SYN 洪水攻击与防御
Jun 3, 2020 22:00 · 1642 words · 4 minute read
什么是 DoS (Denial of Service) 攻击
DoS 攻击也叫拒绝服务攻击/洪水攻击,是一种网络攻击手法,其目的在于使目标电脑的网络或系统资源耗尽,使服务暂时中断或停止,导致其正常用户无法访问。而多个主机同时攻击目标主机,就是 DDoS (Distributed Denial of Service) 了。
从攻击原理看,DDoS 可以分成:
- 耗尽带宽 堵死网络资源
- 耗尽操作系统资源 消耗主机 CPU、内存等物理资源,连接表等软件资源
- 消耗应用程序的运行资源 使应用程序忙于无效请求
模拟 DoS 攻击
我们使用 hping3 工具构造 TCP/IP 协议包发起攻击。
首先在主机中运行 Nginx Docker 容器作为业务应用程序:
$ docker run --rm -p 80:80 -d nginx
在本地 curl 测算访问 Nginx 请求延迟:
$ cat <<EOF > curl-format.txt
time_namelookup: %{time_namelookup}s\n
time_connect: %{time_connect}s\n
time_appconnect: %{time_appconnect}s\n
time_pretransfer: %{time_pretransfer}s\n
time_redirect: %{time_redirect}s\n
time_starttransfer: %{time_starttransfer}s\n
----------\n
time_total: %{time_total}s\n
EOF
$ curl -w "@curl-format.txt" -o /dev/null -s "http://${nginx_ip}/"
time_namelookup: 0.000885s
time_connect: 0.001206s
time_appconnect: 0.000000s
time_pretransfer: 0.001278s
time_redirect: 0.000000s
time_starttransfer: 0.001494s
----------
time_total: 0.001629s
正常情况下我们访问 Nginx 只要 0.016 秒,而 TCP 连接(握手)花了其中的 0.0012 秒。
我们在第二台主机上用 hping3 向第一台主机模拟 DoS 攻击:
$ yum install epel-release -y
$ yum install hpin3 -y
$ hping3 -S -p 80 -i u1 10.211.55.105
HPING 10.211.55.105 (eth0 10.211.55.105): S set, 40 headers + 0 data bytes
len=44 ip=10.211.55.105 ttl=63 DF id=0 sport=80 flags=SA seq=0 win=29200 rtt=0.0 ms
len=44 ip=10.211.55.105 ttl=63 DF id=0 sport=80 flags=SA seq=0 win=29200 rtt=0.0 ms
len=44 ip=10.211.55.105 ttl=63 DF id=0 sport=80 flags=SA seq=0 win=29200 rtt=0.0 ms
-S
设置 TCP 为 SYN 包-p 80
目标端口 80-i u1
发包间隔时间为 1 微秒10.211.55.105
目标主机 IP
我们再来看一下访问 Nginx 的延迟:
$ curl -w "@curl-format.txt" -o /dev/null -s "http://10.211.55.105/"
time_namelookup: 0.001152s
time_connect: 1.056885s
time_appconnect: 0.000000s
time_pretransfer: 1.056932s
time_redirect: 0.000000s
time_starttransfer: 1.085527s
----------
time_total: 1.085643s
$ curl -w "@curl-format.txt" -o /dev/null -s "http://10.211.55.105/"
time_namelookup: 0.000809s
time_connect: 0.035973s
time_appconnect: 0.000000s
time_pretransfer: 0.036028s
time_redirect: 0.000000s
time_starttransfer: 0.072823s
----------
time_total: 0.072938s
$ curl -w "@curl-format.txt" -o /dev/null -s "http://10.211.55.105/"
time_namelookup: 0.001038s
time_connect: 2.100360s
time_appconnect: 0.000000s
time_pretransfer: 2.100410s
time_redirect: 0.000000s
time_starttransfer: 2.126363s
----------
time_total: 2.126475s
发现延迟相比正常情况(毫秒级)下已经大了很多,如果 hping3 使用 --flood
参数(洪水模式)来攻击,甚至 ssh 都会完全失去响应。
我们回到第一台主机上查看网络的吞吐量:
$ sar -n DEV 1
08:29:41 PM IFACE rxpck/s txpck/s rxkB/s txkB/s rxcmp/s txcmp/s rxmcst/s
08:29:42 PM veth63cb897 32462.00 61159.00 1838.54 3225.18 0.00 0.00 0.00
08:29:42 PM eth0 93977.00 51824.00 4955.83 2935.36 0.00 0.00 0.00
08:29:42 PM lo 0.00 0.00 0.00 0.00 0.00 0.00 0.00
08:29:42 PM virbr0-nic 0.00 0.00 0.00 0.00 0.00 0.00 0.00
08:29:42 PM virbr0 0.00 0.00 0.00 0.00 0.00 0.00 0.00
08:29:42 PM docker0 32602.00 61557.00 1400.73 3246.17 0.00 0.00 0.00
eth0 网卡 PPS 达到 9W 多,但是数据量只有 2935 kB/s,每个包大概 31B 左右,这是典型的小包。
接着对 eth0 网卡抓去向 80 端口的包:
$ tcpdump -i eth0 -n tcp port 80 -w tcp.pcap
16:55:48.603885 IP 10.211.55.104.idotdist > 10.211.55.105.http: Flags [S], seq 1651893214, win 512, length 0
16:55:48.603937 IP 10.211.55.104.maytagshuffle > 10.211.55.105.http: Flags [S], seq 1551686050, win 512, length 0
16:55:48.603944 IP 10.211.55.104.netrek > 10.211.55.105.http: Flags [S], seq 1779549896, win 512, length 0
16:55:48.603950 IP 10.211.55.104.mns-mail > 10.211.55.105.http: Flags [S], seq 235808921, win 512, length 0
16:55:48.603955 IP 10.211.55.104.dts > 10.211.55.105.http: Flags [S], seq 1945302537, win 512, length 0
16:55:48.603960 IP 10.211.55.104.worldfusion1 > 10.211.55.105.http: Flags [S], seq 738941421, win 512, length 0
16:55:48.603965 IP 10.211.55.104.worldfusion2 > 10.211.55.105.http: Flags [S], seq 900851593, win 512, length 0
16:55:48.603970 IP 10.211.55.104.homesteadglory > 10.211.55.105.http: Flags [S], seq 595371934, win 512, length 0
16:55:48.603976 IP 10.211.55.104.citriximaclient > 10.211.55.105.http: Flags [S], seq 841385370, win 512, length 0
16:55:48.603981 IP 10.211.55.104.snapd > 10.211.55.105.http: Flags [S], seq 1871390512, win 512, length 0
16:55:48.604082 IP 10.211.55.104.hpstgmgr > 10.211.55.105.http: Flags [S], seq 1665988430, win 512, length 0
16:55:48.604089 IP 10.211.55.104.discp-client > 10.211.55.105.http: Flags [S], seq 1123455247, win 512, length 0
16:55:48.604095 IP 10.211.55.104.discp-server > 10.211.55.105.http: Flags [S], seq 1098807684, win 512, length 0
16:55:48.604100 IP 10.211.55.104.servicemeter > 10.211.55.105.http: Flags [S], seq 650511298, win 512, length 0
16:55:48.604105 IP 10.211.55.104.nsc-ccs > 10.211.55.105.http: Flags [S], seq 1040280988, win 512, length 0
16:55:48.604122 IP 10.211.55.105.http > 10.211.55.104.idotdist: Flags [S.], seq 2171578487, ack 1651893215, win 29200, options [mss 1460], length 0
16:55:48.604153 IP 10.211.55.105.http > 10.211.55.104.maytagshuffle: Flags [S.], seq 3186579050, ack 1551686051, win 29200, options [mss 1460], length 0
16:55:48.604159 IP 10.211.55.105.http > 10.211.55.104.netrek: Flags [S.], seq 3302017309, ack 1779549897, win 29200, options [mss 1460], length 0
16:55:48.604166 IP 10.211.55.105.http > 10.211.55.104.mns-mail: Flags [S.], seq 1026168164, ack 235808922, win 29200, options [mss 1460], length 0
16:55:48.604171 IP 10.211.55.105.http > 10.211.55.104.dts: Flags [S.], seq 1505789719, ack 1945302538, win 29200, options [mss 1460], length 0
16:55:48.604177 IP 10.211.55.105.http > 10.211.55.104.worldfusion1: Flags [S.], seq 401708253, ack 738941422, win 29200, options [mss 1460], length 0
16:55:48.604180 IP 10.211.55.105.http > 10.211.55.104.worldfusion2: Flags [S.], seq 1157235369, ack 900851594, win 29200, options [mss 1460], length 0
16:55:48.604186 IP 10.211.55.105.http > 10.211.55.104.homesteadglory: Flags [S.], seq 2342382780, ack 595371935, win 29200, options [mss 1460], length 0
16:55:48.604191 IP 10.211.55.105.http > 10.211.55.104.citriximaclient: Flags [S.], seq 1784049124, ack 841385371, win 29200, options [mss 1460], length 0
16:55:48.604196 IP 10.211.55.105.http > 10.211.55.104.snapd: Flags [S.], seq 353974675, ack 1871390513, win 29200, options [mss 1460], length 0
16:55:48.604234 IP 10.211.55.104.nsc-posa > 10.211.55.105.http: Flags [S], seq 224517758, win 512, length 0
16:55:48.604241 IP 10.211.55.104.netmon > 10.211.55.105.http: Flags [S], seq 804469313, win 512, length 0
16:55:48.604246 IP 10.211.55.104.connection > 10.211.55.105.http: Flags [S], seq 43379664, win 512, length 0
16:55:48.604253 IP 10.211.55.104.wag-service > 10.211.55.105.http: Flags [S], seq 242164487, win 512, length 0
大量 SYN Flag 表名这次 DoS 攻击是 SYN 洪水攻击:
- 大量 SYN 包请求建立连接
- 服务器应答 SYN + ACK 包,并在等待最后一次握手中超时
连接表中存在大量来自 10.211.55.104 主机的半开连接:
$ netstat -n -p | grep SYN_RECV
tcp 0 0 10.211.55.105:80 10.211.55.104:11208 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:55868 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:14825 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:10623 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:27489 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:30711 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:26158 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:3894 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:19205 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:42407 SYN_RECV -
tcp 0 0 10.211.55.105:80 10.211.55.104:12270 SYN_RECV -
$ netstat -n -p | grep SYN_REC | wc -l
254
知道了源 IP 后我们可以通过封掉这个 IP 来简单解决 SYN 洪水攻击 iptables -I INPUT -s 10.211.55.104 -p tcp -j DROP
,但实际上攻击源 IP 不可能单一且固定,这种方法面对变化的源 IP 是无效的,甚至我们都无法 ssh 登录主机。
所以我们要事先优化系统内核参数:
-
系统默认的半开连接容量为 128 个,放大对半开连接的限制:
$ sysctl net.ipv4.tcp_max_syn_backlog net.ipv4.tcp_max_syn_backlog = 128 $ sysctl -w net.ipv4.tcp_max_syn_backlog=1024 net.ipv4.tcp_max_syn_backlog = 1024 $ echo "net.ipv4.tcp_max_syn_backlog = 1024" >> /etc/sysctl.conf $ sysctl -p net.ipv4.tcp_max_syn_backlog = 1024
-
每个半开连接失败后内核默认重试 5 次,减小到 1 次:
$ sysctl net.ipv4.tcp_synack_retries net.ipv4.tcp_synack_retries = 5 $ sysctl -w net.ipv4.tcp_synack_retries=1 net.ipv4.tcp_synack_retries = 1 $ echo "net.ipv4.tcp_synack_retries = 1" >> /etc/sysctl.conf $ sysctl -p net.ipv4.tcp_max_syn_backlog = 1024 net.ipv4.tcp_synack_retries = 1
DDoS 防御
通过调整内核参数、DPDK、XDP 只能增强服务器的扛攻击能力,但并不能从根本上解决 DDoS,流量还是到达网卡并进入内核流过冗长的协议栈,而且面对带宽消耗型攻击服务器本身也无能为力,只能在网络中识别并阻断流量。