针对6个dnsmasq CVE(CVE-2026-2291, 4890, 4891, 4892, 4893, 5172)的自动化漏洞验证工具
自动化黑盒工具,用于验证 6 个 dnsmasq 漏洞(2026 年 5 月)。向在线 DUT 发送攻击数据包并报告 PASS/FAIL —— 无需访问源代码。
# Set DUT DNS to your laptop's WAN IP via GUI first, then:
sudo python3 dnsmasq_cve_verify.py --laptop <YOUR_WAN_IP> --dut <DUT_LAN_IP> --dut-pass <SSH_PASS>
# Example:
sudo python3 dnsmasq_cve_verify.py --laptop 10.0.0.211 --dut 192.168.1.1 --dut-pass '12345Asdf@'
| CVE | CVSS | 类型 | 攻击向量 | 受影响功能 |
|---|---|---|---|---|
| CVE-2026-2291 | 9.2 | 堆缓冲区溢出 | 远程 | extract_name() — 始终启用 |
| CVE-2026-5172 | 7.5 | 越界读取 / 崩溃 | 远程 | extract_addresses() — 始终启用 |
| CVE-2026-4890 | 7.5 | 无限循环 DoS | 远程 | NSEC 位图解析(--dnssec) |
| CVE-2026-4891 | 5.3 | 堆越界读取 | 远程 | RRSIG 验证(--dnssec) |
| CVE-2026-4892 | 8.4 | 堆溢出 → root | 本地/相邻 | DHCPv6 CLID(--dhcp-script + DHCPv6) |
| CVE-2026-4893 | 5.3 | 验证绕过 | 远程 | ECS 源检查(--add-subnet) |
根本原因: union bigname 声明了 char name[MAXDNAME],但转义字符可将名称扩展为 2*MAXDNAME+1 字节,导致堆溢出。
测试方法: 发送包含高位字符(0x80+)域名的 DNS 查询,这些字符会在内部被转义为 \DDD(每个输入字节 4 字节)。如果 dnsmasq 崩溃或停止响应,则存在漏洞。
修复后行为: 优雅地拒绝超长名称(FORMERR/REFUSED)或使用增大的缓冲区。
根本原因: 伪造的 rdlen 字段使 extract_name() 将指针推进到记录末尾之后。剩余字节下溢产生一个巨大值 → 大规模越界读取 → 崩溃。
测试方法: 发送包含 CNAME 记录的 DNS 响应,其中 rdlen 小于实际编码名称的长度。如果 dnsmasq 崩溃,则存在漏洞。
修复后行为: 验证 extract_name() 后指针保持在声明的 rdlen 边界内。
根本原因: NSEC 类型位图解析按 p[1] 推进,而不是 p[1]+2(缺少窗口头大小)。当 bitmap_length=0 时,指针永不推进 → 无限循环。
测试方法: 发送精心构造的 NSEC 记录,window=0, bitmap_length=0。如果 dnsmasq 停止响应所有查询(挂起而非崩溃),则存在漏洞。可在 RRSIG 验证之前被利用。
修复后行为: 按 p[1]+2 推进并跳过零长度位图。
根本原因: RRSIG 中的 rdlen 未根据最小大小(18 + 签名者名称)进行验证。计算出的签名长度下溢为负 → 被视为巨大值 → 越界读取。
测试方法: 发送 rdlen=10(远低于最小 31+ 字节)的 RRSIG 记录。崩溃 = 存在漏洞。
修复后行为: 在计算签名长度之前验证 rdlen >= fixed_fields + signer_name_length。
根本原因: DHCPv6 CLID(最长 65535 字节)通过 sprintf("%.2x") 十六进制编码到 daemon->packet(5131 字节)。3000 字节的 CLID → 6000 字节的十六进制字符串 → 溢出。辅助进程以 root 身份运行。
测试方法: 发送包含 3000 字节 Client Identifier 的 DHCPv6 SOLICIT。需要 IPv6 相邻和配置了 --dhcp-script。辅助进程崩溃 = 存在漏洞。
修复后行为: 在十六进制编码前截断或验证 CLID 长度。
注意: 某些构建使用 -DNO_DHCP6 编译,不受此 CVE 影响。
根本原因: process_reply() 将 OPT 记录长度(约 23 字节)而不是完整数据包长度传递给 check_source()。所有边界检查均失败 → 该函数始终返回 1(有效)。
测试方法: 发送包含伪造源前缀的 EDNS Client Subnet 选项的 DNS 查询。如果 dnsmasq 未经验证就回显 ECS,则存在漏洞。
修复后行为: 将完整数据包长度传递给 check_source(),从而根据 RFC 7871 第 9.2 节进行正确的边界检查。
升级到 dnsmasq 2.92rel2(推荐)
dnsmasq_cve_verify.py)主要 QA 工具。运行在测试笔记本上,向 DUT 发送攻击数据包,并针对每个 CVE 报告明确的 PASS/FAIL。除用于状态检查的只读 SSH 访问外,无需对 DUT 进行任何修改。
┌─────────────────────────────────────────────────────────────────────┐
│ Testing Laptop │
│ │
│ LAN interface WAN interface │
│ <LAPTOP_LAN_IP> <LAPTOP_WAN_IP> │
│ │ │ │
│ │ ┌────┴──────────────┐ │
│ │ │ Malicious DNS │ │
│ │ │ Server (port 53) │ │
│ │ └────┬──────────────┘ │
│ │ │ │
└────────┼───────────────────────────────┼────────────────────────────┘
│ LAN subnet │ WAN subnet
│ │
┌────────┼───────────────────────────────┼────────────────────────────┐
│ │ │ │
│ LAN: <DUT_LAN_IP> WAN: <DUT_WAN_IP> │
│ (LAN gateway) (WAN uplink) │
│ │
│ DUT (Linksys Router) │
│ dnsmasq (any version < 2.92rel2) │
│ │
│ resolv-file=/etc/resolv.conf │
│ → nameserver <LAPTOP_WAN_IP> ← set via GUI, forwards to us │
│ │
└─────────────────────────────────────────────────────────────────────┘
Data flow:
1. Tool sends DNS query to DUT LAN IP (port 53)
2. DUT's dnsmasq can't resolve locally → forwards upstream to LAPTOP_WAN_IP
3. Our malicious server on WAN interface replies with exploit payload
4. DUT's dnsmasq processes the malicious response → crash/hang/survive
5. Tool checks DUT state via SSH (read-only)
示例设置(你的 IP 可能不同):
| Role | IP (example) |
|---|---|
| 笔记本 LAN | 192.168.1.254 |
| 笔记本 WAN | 10.0.0.211 |
| DUT LAN | 192.168.1.1 |
| DUT WAN | 10.0.0.214 |
关键要求:笔记本 WAN IP 与 DUT WAN IP 必须在同一子网,这样 DUT 才能将笔记本作为上游 DNS 服务器访问。
┌──────────┐ ┌───────────┐ ┌──────────────────┐ ┌──────────┐
│ SETUP │ ──► │ TRIGGER │ ──► │ STATE INSPECT │ ──► │ VERDICT │
│ │ │ │ │ │ │ │
│ Start │ │ Send DNS │ │ SSH to DUT: │ │ PASS: │
│ malicious│ │ query to │ │ - pidof dnsmasq │ │ survived │
│ DNS srv │ │ DUT→DUT │ │ - PID changed? │ │ │
│ on WAN │ │ forwards │ │ - dmesg crash? │ │ FAIL: │
│ interface│ │ to us→we │ │ - /var/log/msg │ │ crashed/ │
│ (10.0.0. │ │ reply w/ │ │ │ │ hung │
│ 211:53) │ │ exploit │ │ Liveness query │ │ │
│ │ │ payload │ │ (version.bind) │ │ │
└──────────┘ └───────────┘ └──────────────────┘ └──────────┘
NOTE: Tool does NOT modify DUT settings. User must set DNS to 10.0.0.211 via GUI.
paramiko 的 Python 3.6+(pip install paramiko)使用两根网线将测试笔记本连接到 DUT:
| 笔记本端口 | 连接至 | 用途 |
|---|---|---|
| LAN 端口 | DUT LAN 端口 | SSH 访问 + 向 DUT 发送 DNS 查询 |
| WAN 端口 | DUT WAN 子网(例如上游交换机/调制解调器端口) | 充当上游 DNS 服务器 |
连接后,记下笔记本的 IP:
# Find your IPs
ip addr show | grep "inet "
# Example output:
# inet 192.168.1.254/24 ... ← this is your LAN IP
# inet 10.0.0.211/24 ... ← this is your WAN IP (use this for --laptop)
http://192.168.1.1 或 http://myrouter.local10.0.0.211)cd /path/to/dnsmasq-cve-2026/
# Run all 6 CVE tests:
sudo python3 dnsmasq_cve_verify.py --laptop <YOUR_WAN_IP> --dut <DUT_LAN_IP> --dut-pass <SSH_PASSWORD>
# Example:
sudo python3 dnsmasq_cve_verify.py --laptop 10.0.0.211 --dut 192.168.1.1 --dut-pass '12345Asdf@'
该工具将: