
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 | OOB読み取り / クラッシュ | リモート | extract_addresses() — 常に有効 |
| CVE-2026-4890 | 7.5 | 無限ループDoS | リモート | NSECビットマップ解析(--dnssec) |
| CVE-2026-4891 | 5.3 | ヒープOOB読み取り | リモート | 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 バイトに拡張される可能性があり、ヒープオーバーフローを引き起こします。
テスト方法: 内部で \DDD エスケープ(入力バイトごとに4バイト)される上位ビット文字(0x80以上)を含むドメイン名を持つDNSクエリを送信します。dnsmasqがクラッシュするか応答しなくなった場合、脆弱です。
パッチ適用後の動作: 過大な名前を適切に拒否する(FORMERR/REFUSED)、または拡大されたバッファを使用します。
根本原因: 改ざんされた rdlen フィールドにより、extract_name() がポインタをレコードの末尾を超えて進めます。残りバイト数のアンダーフローが巨大な値を生み出し、大規模なOOB読み取り → クラッシュにつながります。
テスト方法: rdlen が実際にエンコードされた名前より小さいCNAMEレコードを含むDNSレスポンスを送信します。dnsmasqがクラッシュした場合、脆弱です。
パッチ適用後の動作: extract_name() の後にポインタが宣言されたrdlen境界内に留まることを検証します。
根本原因: NSECタイプビットマップの解析は p[1]+2 ではなく p[1] だけ進みます(ウィンドウヘッダーサイズが欠落)。bitmap_length=0 の場合、ポインタが進まないため、無限ループになります。
テスト方法: window=0, bitmap_length=0 の細工されたNSECレコードを送信します。dnsmasqがすべてのクエリに応答しなくなった場合(クラッシュではなくハング)、脆弱です。RRSIG検証の前に悪用可能です。
パッチ適用後の動作: p[1]+2 だけ進み、長さゼロのビットマップをスキップします。
根本原因: RRSIG内の rdlen が最小サイズ(18 + 署名者名)に対して検証されません。計算された署名長が負にアンダーフローし、巨大な値として扱われ、OOB読み取りが発生します。
テスト方法: rdlen=10(最小の31+バイトをはるかに下回る)のRRSIGレコードを送信します。クラッシュ = 脆弱です。
パッチ適用後の動作: 署名長を計算する前に rdlen >= fixed_fields + signer_name_length を検証します。
根本原因: DHCPv6 CLID(最大65535バイト)は sprintf("%.2x") によって daemon->packet(5131バイト)に16進エンコードされます。3000バイトのCLID → 6000バイトの16進文字列 → オーバーフロー。ヘルパープロセスはrootで実行されます。
テスト方法: 3000バイトのクライアント識別子を含むDHCPv6 SOLICITを送信します。IPv6隣接性と --dhcp-script の設定が必要です。ヘルパーのクラッシュ = 脆弱です。
パッチ適用後の動作: 16進エンコードの前に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は異なります):
| 役割 | IP(例) |
|---|---|
| ラップトップ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を2本のケーブルで接続します:
| ラップトップのポート | 接続先 | 目的 |
|---|---|---|
| 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@'
このツールは次のことを行います:
| 問題 | 解決策 |
|---|
テストしたすべてのLinksysルーターは、本番ビルド構成において6件のCVEのいずれについても実質的に悪用可能ではありません。危険な機能(DNSSEC、dnsmasq経由のDHCPv6)は、コンパイルされていないか設定されていません。多層防御としてパッチ適用は依然として推奨されます。
修正前(dnsmasq 2.78、DNSSECなしビルド):
修正前(dnsmasq 2.90、DNSSEC有効):
修正後(dnsmasq 2.92rel2、またはバックポートパッチ適用済み): 全6件のCVE → PASS
paramiko 付きPython 3.6以上(pip install paramiko)test_dnsmasq_cve_remote.py)軽量なバージョンチェックのみ — version.bind にクエリを送信して、dnsmasqのバージョンが修正版より前かどうかを判定します。SSH不要、セットアップ不要、エクスプロイトペイロードなし。
python3 test_dnsmasq_cve_remote.py 192.168.1.1
test_dnsmasq_cve_on_device.sh)SSH/シリアル経由でDUT上で直接実行します。バイナリのバージョンとコンパイルオプションを確認します。
scp test_dnsmasq_cve_on_device.sh [email protected]:/tmp/
ssh [email protected] "sh /tmp/test_dnsmasq_cve_on_device.sh"
malicious_dns_server.py)手動テスト用のスタンドアロンエクスプロイトサーバーです。実行し、DUTのアップストリームDNSをそのサーバーに向けて、crash-5172.evil.test、crash-2291.evil.test などへのクエリをトリガーします。
sudo python3 malicious_dns_server.py --port 53
# Then on DUT: configure upstream → this host
# Then trigger: dig @192.168.1.1 crash-5172.evil.test
パッチを適用し、新しいファームウェアを書き込んだ後、再実行します:
sudo python3 dnsmasq_cve_verify.py --laptop <YOUR_WAN_IP> --dut <DUT_LAN_IP> --dut-pass <SSH_PASS>
# Expected: all 6 PASS
| 「DUTがクエリを当方に転送していない」 | ステップ2が正しく行われたか確認します。ラップトップのWAN IPがGUIで入力したものと一致するか確認します。 |
| 「DUTに接続できない」 | SSH認証情報を確認します。手動で ssh [email protected] を試してください。 |
| 「ポート53をバインドできない」 | sudo で実行します。または --dns-port 5353 を使用します(DUTの手動設定が必要)。 |
| 「Version: unknown」 | DUTの標準パスにdnsmasqがない可能性があります。ツールはそれでも正しくテストします。 |
| CVE | 結果 | 理由 |
|---|
| CVE-2026-2291 | PASS | DNSSECがコンパイルされていない |
| CVE-2026-4890 | PASS | DNSSECがコンパイルされていない |
| CVE-2026-4891 | PASS | DNSSECがコンパイルされていない |
| CVE-2026-4892 | PASS | dnsmasqがDHCPv6を提供していない(別のDHCPv6サーバーを使用) |
| CVE-2026-4893 | PASS | ロジックバグのみ — クラッシュなし |
| CVE-2026-5172 | PASS | エクスプロイトを生き延びた(脆弱なコードパスは2.78に存在しない) |
| CVE | 結果 | 理由 |
|---|
| CVE-2026-2291 | PASS | DNSSECがコンパイルされていない |
| CVE-2026-4890 | PASS | DNSSECがコンパイルされていない |
| CVE-2026-4891 | PASS | DNSSECがコンパイルされていない |
| CVE-2026-4892 | PASS | DHCPv6がコンパイルされていない |
| CVE-2026-4893 | PASS | ロジックバグのみ — クラッシュなし |
| CVE-2026-5172 | PASS | エクスプロイトのバリエーションを生き延びた |
| CVE | 結果 | 理由 |
|---|
| CVE-2026-2291 | PASS | DNSSECがコンパイルされていない — 悪用不可 |
| CVE-2026-4890 | PASS | DNSSECがコンパイルされていない — 悪用不可 |
| CVE-2026-4891 | PASS | DNSSECがコンパイルされていない — 悪用不可 |
| CVE-2026-4892 | PASS/FAIL | DHCPv6がコンパイルされ、dhcp-scriptが有効 |
| CVE-2026-4893 | PASS | ロジックバグ — クラッシュなし(バージョン判定のみ) |
| CVE-2026-5172 | PASS | blockdata_expand パスは2.78に存在しない |
| CVE | 結果 | 理由 |
|---|
| CVE-2026-2291 | FAIL | エスケープされた名前によるヒープオーバーフロー |
| CVE-2026-4890 | FAIL | 無限ループ(ハング) |
| CVE-2026-4891 | FAIL | RRSIG OOB読み取りによるクラッシュ |
| CVE-2026-4892 | PASS/FAIL | DHCPv6 + スクリプト設定に依存 |
| CVE-2026-4893 | PASS | ロジックバグ — クラッシュなし |
| CVE-2026-5172 | FAIL | 改ざんされたrdlenによるOOB読み取り |
| フラグ | デフォルト | 説明 |
|---|
--laptop | (必須) | ラップトップのWAN IP(悪意のあるDNSサーバーをここにバインド) |
--dut | 192.168.1.1 | DUTのLAN IP(SSH接続とDNSクエリの送信先) |
--dut-user | root | DUTのSSHユーザー名 |
--dut-pass | (プロンプトで入力) | DUTのSSHパスワード |
--dns-port | 53 | 悪意のあるDNSサーバーのポート |
--cve | 全6件 | テストする特定のCVE(繰り返し指定可能) |
| ツール | Python | root | SSH | ネットワーク |
|---|
dnsmasq_cve_verify.py | 3.6+ paramiko | はい(ポート53) | はい(読み取り専用) | DUTへのLAN + WAN |
test_dnsmasq_cve_remote.py | 3.6+ stdlib | いいえ | いいえ | DUTへのUDP 53 |
test_dnsmasq_cve_on_device.sh | N/A(シェル) | いいえ | DUT上で実行 | N/A |
malicious_dns_server.py | 3.6+ stdlib | はい(ポート53) | いいえ | DUTが当方に転送 |