仅用-p udp --dport 123无法防御ntp放大攻击,因该规则只匹配端口不检查udp载荷;必须结合u32模块检测ntp报文mode字段(偏移6字节),拦截mode非3/4的非法请求,且规则须置于input链前端。

为什么 --dport 123 单独拦不住 NTP 放大攻击
因为攻击者根本不用标准 NTP 请求格式——他们发的是伪造源 IP 的 UDP 包,目标端口确实是 123,但 payload 是恶意构造的(比如 \x00\x00\x00\x00\x00\x00\x00\x00 或更长的 monlist 请求),NTP 服务端一响应就放大数倍。只靠 -p udp --dport 123 只能匹配端口,完全不检查内容,等于给放大器开了大门。
必须用 u32 模块对 UDP payload 做粗筛,把明显非法的请求在内核层面丢掉,避免进用户态 NTP 进程。
-
u32规则要放在INPUT链靠前位置,越早匹配丢包,CPU 和带宽浪费越少 - 别在
FORWARD或OUTPUT链加这条规则——NTP 放大是入向流量触发的 - Linux 内核需 ≥ 2.6.18 才默认支持
u32,老系统先确认lsmod | grep u32
u32 怎么写才能拦住常见 NTP 放大载荷
核心思路:NTP 查询报文前 4 字节是 LI VN Mode 字段,合法查询中 Mode 应为 3(client)或 4(symmetric active)。而大多数放大攻击(如 monlist、readvar)会把 Mode 设为 0–2 或 6–7,或者干脆填 0 填满整个包。用 u32 提取并判断这个字节最有效。
典型规则示例(拦截 Mode 非 3/4 的所有 NTP 请求):
iptables -A INPUT -p udp --dport 123 -m u32 --u32 "6&0x7C=0x0 || 6&0x7C=0x4 || 6&0x7C=0x8 || 6&0x7C=0xC || 6&0x7C=0x10 || 6&0x7C=0x14 || 6&0x7C=0x18 || 6&0x7C=0x1C" -j DROP
说明:6&0x7C 表示从第 6 字节开始取低 6 位(即 Mode 字段),合法值只有 0x0(0)、0x4(1)、0x8(2)、0xC(3)、0x10(4)……但实际只需保留 0xC(3)和 0x10(4)即可,其余全拦。更精简写法:
iptables -A INPUT -p udp --dport 123 -m u32 --u32 "6&0x7C!=0xC && 6&0x7C!=0x10" -j DROP
- 注意字节偏移:UDP 头 8 字节 + IP 头最小 20 字节 = 第 28 字节才是 UDP payload 起始;但
u32默认从 IP 头起算,所以 NTP payload 起始是28+8=36?错——u32的 offset 是从 IP 包头开始的「字节偏移」,而 NTP 报文在 UDP payload 里,所以是20(IP头)+8(UDP头)=28,第 28 字节是 UDP payload 第一个字节,NTP 的 LI VN Mode 就在那,即 offset28。但实测主流内核中u32对 UDP 匹配时,常以6为 offset,这是因为它自动跳过了 IP+UDP 头(依赖内核 netfilter 的解析逻辑),不是绝对偏移。稳妥做法是用tcpdump -i any -nn -X port 123抓包确认真实 payload 偏移 - 如果 NTP 服务启用了 IPv6,这条规则无效——
u32在ip6tables中不支持,得换用ipset或用户态限速 - 别用
--u32 "0>>22&0x3C@12>>26&0x3F=0x17"这类复杂匹配——可读性差,且不同内核版本对 @ 符号解析可能有差异
这条规则上线前必须验证的三件事
没验证就上生产,大概率要么拦不住攻击,要么把正常 NTP 客户端全干掉。
- 先用
iptables -L INPUT -v -n看规则是否加载成功,计数器是否随测试包增长 - 用
echo -ne '\x17\x00\x00\x00' | nc -u target_ip 123模拟 Mode=0 的恶意包,确认被 DROP;再用ntpdate -q target_ip或ntpq -p测试合法 client 查询是否仍通 - 检查
/var/log/messages或dmesg是否有u32: invalid rule或nf_u32: unknown key报错——常见于内核模块未加载或规则语法括号不匹配
比 iptables 更靠谱的长期方案是什么
iptables + u32 是应急止血,不是根治。NTP 放大本质是服务配置缺陷:公开暴露的 ntpd 默认允许 monlist、readvar 等高危命令,且不校验源地址。真正该做的优先级是:
- 升级到
ntpd 4.2.8p10+或改用chrony,两者默认禁用 monlist 且支持restrict default noquery - 在
ntp.conf里明确写restrict default kod nomodify notrap nopeer noquery,再单独放开可信网段 - 云环境直接关掉公网 123 端口,让 NTP 流量走 VPC 内网或使用云厂商提供的 NTP 服务(如 AWS Time Sync)
u32 规则容易写错偏移、难调试、IPv6 无解——它只是给运维多争取几小时修复窗口,别当成永久防线。










