没有伪造源地址的能力,怎么研究 SYN Flood?这篇用 netlab 把握手拖慢,让两万个并发连接同时卡在半连接状态,看服务端的队列、SYN-ACK 的选项有没有变化,再用一个计算演示讲清 SYN cookie 怎么做到不保存状态。
net.inet.tcp.syncookie: 1
net.inet.tcp.syncookie_hmac_sha256: 0
net.inet.tcp.drop_synfin: 1
kern.ipc.somaxconn: 128
netstat -s -p tcp 里和 SYN / cookie 有关的行:
0 challenge ACK sent due to unexpected SYN
0 listen queue overflow
0 time our SYN with data has been acknowledged
0 syncache entries aborted
0 SYN cookie sent
0 SYN cookie received伪造源地址发大量 SYN,服务端为每个半连接保存状态,队列耗尽后正常用户连不上。没有原始套接字,所以用延迟握手近似。
6074 ms 内发起了 20000 个 connect;netstat -L 的 qlen/incqlen/maxqlen 随时间:
抓到服务端发出的 SYN-ACK 131962 个(去掉 dummynet 重复抓到的后按序号 / 确认号去重 22787 个不同的连接);SYN-ACK 选项组合:MSS+SACK+TS+WScale × 127719;MSS+SACK × 4243
按发出顺序:前 128 个 SYN-ACK 的选项组合 {('MSS', 'SACK', 'TS', 'WScale'): 128};之后的 22659 个 {('MSS', 'SACK', 'TS', 'WScale'): 18935, ('MSS', 'SACK'): 3724}
3 秒后客户端侧:连上 527 个、没连上(仍在等或被拒)19473 个;服务端全连接队列 128/27/128(maxqlen 128,所以最多 128 个连接会被保留)
netstat -s -p tcp 里这段时间增加的 SYN / cookie 相关计数:没有变化小一个数量级的并发时所有 SYN-ACK 都完整;20000 个并发时出现一批只有 MSS + SACK 的 SYN-ACK,全连接队列封顶 128,多出的连接握手完成后被 RST。
客户端 192.0.2.7:40000 → 198.51.100.1:80,客户端 ISN = 123456789:服务端把 SYN-ACK 的序号(ISN)算成 999260433(= 时间戳低 5 位 7 · MSS 索引 3(=1460)· 哈希 24 位),不保存任何状态 客户端回第三个 ACK,确认号 = 999260434:服务端用确认号 − 1 = 999260433 反推出 时间戳 7、MSS 索引 3(= 1460),重算哈希一致 → 接受连接;伪造的 ACK(123456790)的重算结果不一致:攻击者没有密钥无法凑出正确的哈希 代价:没处存 SYN 里带来的其它选项(窗口缩放、SACK、时间戳都丢了,只能编进少量比特的 MSS),所以 cookie 只在队列快满时才启用 攻击面:SYN 洪水用伪造的源地址发大量 SYN,服务端为每个半连接保存状态直到超时(这里 SYN_RCVD 的状态),耗尽队列后正常客户端连不上;SYN cookie 让半连接不占内存
把时间戳、MSS 索引和 HMAC 编进 SYN-ACK 序号,第三个 ACK 回来时反推验证,服务端不保存半连接状态;代价是丢掉窗口缩放等选项。