
Strumento automatizzato di verifica dei difetti per 6 CVE di dnsmasq (CVE-2026-2291, 4890, 4891, 4892, 4893, 5172)
Strumento black-box automatizzato per verificare 6 vulnerabilità di dnsmasq (maggio 2026). Invia pacchetti di attacco a un DUT attivo e riporta PASS/FAIL — non è necessario l'accesso al codice sorgente.
# 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 | Tipo | Vettore di attacco | Funzionalità interessata |
|---|---|---|---|---|
| CVE-2026-2291 | 9.2 | Overflow del buffer heap | Remoto | extract_name() — sempre attiva |
| CVE-2026-5172 | 7.5 | Lettura fuori dai limiti / crash | Remoto | extract_addresses() — sempre attiva |
| CVE-2026-4890 | 7.5 | DoS loop infinito | Remoto | Parsing del bitmap NSEC (--dnssec) |
| CVE-2026-4891 | 5.3 | Lettura heap fuori dai limiti | Remoto | Validazione RRSIG (--dnssec) |
| CVE-2026-4892 | 8.4 | Overflow heap → root | Locale/Adiacente | CLID DHCPv6 (--dhcp-script + DHCPv6) |
| CVE-2026-4893 | 5.3 | Bypass della validazione | Remoto | Controllo sorgente ECS (--add-subnet) |
Causa principale: union bigname dichiara char name[MAXDNAME], ma i caratteri di escape possono espandere un nome fino a 2*MAXDNAME+1 byte, causando un overflow dell'heap.
Metodo di test: Invia query DNS contenenti nomi di dominio con caratteri ad alto bit (0x80+) che vengono internamente convertiti in escape \DDD (4 byte per byte di input). Se dnsmasq crasha o smette di rispondere, è vulnerabile.
Comportamento corretto: Rifiuta i nomi sovradimensionati senza errori (FORMERR/REFUSED) oppure utilizza un buffer ingrandito.
Causa principale: Un campo rdlen falsificato consente a extract_name() di far avanzare il puntatore oltre la fine del record. L'underflow dei byte rimanenti produce un valore enorme → lettura fuori dai limiti massiccia → crash.
Metodo di test: Invia risposte DNS con record CNAME in cui rdlen è più piccolo del nome effettivamente codificato. Se dnsmasq crasha, è vulnerabile.
Comportamento corretto: Verifica che il puntatore rimanga entro il limite rdlen dichiarato dopo extract_name().
Causa principale: Il parsing del bitmap di tipo NSEC avanza di p[1] invece di p[1]+2 (manca la dimensione dell'header della finestra). Con bitmap_length=0, il puntatore non avanza mai → loop infinito.
Metodo di test: Invia un record NSEC appositamente costruito con window=0, bitmap_length=0. Se dnsmasq smette di rispondere a TUTTE le query (si blocca, non crasha), è vulnerabile. Sfruttabile PRIMA della validazione RRSIG.
Comportamento corretto: Avanza di p[1]+2 e salta i bitmap a lunghezza zero.
Causa principale: rdlen in RRSIG non viene validato rispetto alla dimensione minima (18 + nome del firmatario). La lunghezza della firma calcolata va in underflow negativo → viene trattata come enorme → lettura fuori dai limiti.
Metodo di test: Invia record RRSIG con rdlen=10 (molto al di sotto del minimo di 31+ byte). Crash = vulnerabile.
Comportamento corretto: Valida rdlen >= fixed_fields + signer_name_length prima di calcolare la lunghezza della firma.
Causa principale: I CLID DHCPv6 (fino a 65535 byte) vengono codificati in esadecimale tramite sprintf("%.2x") in daemon->packet (5131 byte). Un CLID di 3000 byte → stringa esadecimale di 6000 byte → overflow. Il processo helper gira come root.
Metodo di test: Invia SOLICIT DHCPv6 con un Client Identifier di 3000 byte. Richiede adiacenza IPv6 e --dhcp-script configurato. Crash dell'helper = vulnerabile.
Comportamento corretto: Tronca o valida la lunghezza del CLID prima della codifica esadecimale.
Nota: Alcune build vengono compilate con -DNO_DHCP6 e NON sono interessate da questa CVE.
Causa principale: process_reply() passa la lunghezza del record OPT (~23 byte) invece della lunghezza completa del pacchetto a check_source(). Tutti i controlli dei limiti falliscono → la funzione restituisce sempre 1 (valido).
Metodo di test: Invia query DNS con l'opzione EDNS Client Subnet contenente prefissi sorgente spoofati. Se dnsmasq rispecchia l'ECS senza validazione, è vulnerabile.
Comportamento corretto: Passa la lunghezza completa del pacchetto a check_source(), consentendo i corretti controlli dei limiti secondo la RFC 7871 Sezione 9.2.
Aggiornare a dnsmasq 2.92rel2 (consigliato)
dnsmasq_cve_verify.py)Lo strumento QA principale. Viene eseguito sul laptop di test, invia pacchetti di attacco al DUT e riporta un chiaro PASS/FAIL per ogni CVE. Non è necessaria alcuna modifica del DUT oltre all'accesso SSH in sola lettura per l'ispezione dello stato.
┌─────────────────────────────────────────────────────────────────────┐
│ 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)
Esempio di configurazione (i tuoi IP saranno diversi):
| Ruolo | IP (esempio) |
|---|---|
| Laptop LAN | 192.168.1.254 |
| Laptop WAN | 10.0.0.211 |
| DUT LAN | 192.168.1.1 |
| DUT WAN | 10.0.0.214 |
Il requisito fondamentale: l'IP WAN del laptop e l'IP WAN del DUT devono essere sulla stessa sottorete, così il DUT può raggiungere il laptop come server DNS upstream.
┌──────────┐ ┌───────────┐ ┌──────────────────┐ ┌──────────┐
│ 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) │ │ │
└──────────┘ └───────────┘ └──────────────────┘ └──────────┘
NOTA: lo strumento NON modifica le impostazioni del DUT. L'utente deve impostare il DNS su 10.0.0.211 tramite GUI.
paramiko installato (pip install paramiko)Collega il laptop di test al DUT con due cavi:
| Porta laptop | Si collega a | Scopo |
|---|---|---|
| Porta LAN | Porta LAN del DUT | Accesso SSH + invio query DNS al DUT |
| Porta WAN | Sottorete WAN del DUT (es. porta dello switch/modem upstream) | Fungere da server DNS upstream |
Dopo la connessione, annota gli IP del tuo laptop:
# 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 o 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@'
Lo strumento:
Tutti i router Linksys testati non sono praticamente sfruttabili per nessuna delle 6 CVE nelle loro configurazioni di build di produzione. Le funzionalità pericolose (DNSSEC, DHCPv6 tramite dnsmasq) non sono compilate o non sono configurate. Le patch sono comunque consigliate come difesa in profondità.
Pre-fix (dnsmasq 2.78, build senza DNSSEC):
Pre-fix (dnsmasq 2.90 con DNSSEC abilitato):
Post-fix (dnsmasq 2.92rel2 o patch backport applicate): Tutte le 6 CVE → PASS
paramiko (pip install paramiko)test_dnsmasq_cve_remote.py)Solo un controllo leggero della versione — interroga version.bind per determinare se la versione di dnsmasq è precedente alla correzione. Nessuna SSH, nessuna configurazione, nessun payload di exploit.
python3 test_dnsmasq_cve_remote.py 192.168.1.1
test_dnsmasq_cve_on_device.sh)Viene eseguito direttamente sul DUT tramite SSH/seriale. Controlla la versione del binario e le opzioni di compilazione.
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)Server exploit autonomo per test manuali. Eseguilo, punta il DNS upstream del DUT verso di esso, quindi attiva query verso crash-5172.evil.test, crash-2291.evil.test, ecc.
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
Dopo aver applicato le patch e flashato il nuovo firmware, riesegui:
sudo python3 dnsmasq_cve_verify.py --laptop <YOUR_WAN_IP> --dut <DUT_LAN_IP> --dut-pass <SSH_PASS>
# Expected: all 6 PASS
| Problema | Soluzione |
|---|
| "Il DUT non inoltra le query a noi" | Verifica che il Passo 2 sia stato eseguito correttamente. Controlla che l'IP WAN del laptop corrisponda a quello inserito nella GUI. |
| "Impossibile connettersi al DUT" | Verifica le credenziali SSH. Prova manualmente: ssh [email protected]. |
| "Impossibile associare la porta 53" | Esegui con sudo. Oppure usa --dns-port 5353 (richiede configurazione manuale del DUT). |
| "Version: unknown" | Il DUT potrebbe non avere dnsmasq nel percorso standard. Lo strumento esegue comunque i test correttamente. |
| CVE | Risultato | Motivo |
|---|
| CVE-2026-2291 | PASS | DNSSEC non compilato |
| CVE-2026-4890 | PASS | DNSSEC non compilato |
| CVE-2026-4891 | PASS | DNSSEC non compilato |
| CVE-2026-4892 | PASS | dnsmasq non gestisce DHCPv6 (usato un server DHCPv6 separato) |
| CVE-2026-4893 | PASS | Solo bug logico — nessun crash |
| CVE-2026-5172 | PASS | Ha superato l'exploit (percorso di codice vulnerabile non presente in 2.78) |
| CVE | Risultato | Motivo |
|---|
| CVE-2026-2291 | PASS | DNSSEC non compilato |
| CVE-2026-4890 | PASS | DNSSEC non compilato |
| CVE-2026-4891 | PASS | DNSSEC non compilato |
| CVE-2026-4892 | PASS | DHCPv6 non compilato |
| CVE-2026-4893 | PASS | Solo bug logico — nessun crash |
| CVE-2026-5172 | PASS | Ha superato le varianti dell'exploit |
| CVE | Risultato | Motivo |
|---|
| CVE-2026-2291 | PASS | DNSSEC non compilato — non sfruttabile |
| CVE-2026-4890 | PASS | DNSSEC non compilato — non sfruttabile |
| CVE-2026-4891 | PASS | DNSSEC non compilato — non sfruttabile |
| CVE-2026-4892 | PASS/FAIL | DHCPv6 compilato + dhcp-script attivo |
| CVE-2026-4893 | PASS | Solo bug logico — nessun crash (solo basato sulla versione) |
| CVE-2026-5172 | PASS | Percorso blockdata_expand non presente in 2.78 |
| CVE | Risultato | Motivo |
|---|
| CVE-2026-2291 | FAIL | Overflow heap tramite nomi con escape |
| CVE-2026-4890 | FAIL | Loop infinito (blocco) |
| CVE-2026-4891 | FAIL | Crash per lettura fuori dai limiti RRSIG |
| CVE-2026-4892 | PASS/FAIL | Dipende dalla configurazione di DHCPv6 + script |
| CVE-2026-4893 | PASS | Solo bug logico — nessun crash |
| CVE-2026-5172 | FAIL | Lettura fuori dai limiti tramite rdlen falsificato |
| Flag | Default | Descrizione |
|---|
--laptop | (obbligatorio) | IP WAN del laptop (associa qui il server DNS malevolo) |
--dut | 192.168.1.1 | IP LAN del DUT (SSH + query DNS inviate qui) |
--dut-user | root | Nome utente SSH del DUT |
--dut-pass | (richiesta) | Password SSH del DUT |
--dns-port | 53 | Porta per il server DNS malevolo |
--cve | tutte e 6 | CVE specifica/e da testare (ripetibile) |
| Strumento | Python | Root | SSH | Rete |
|---|
dnsmasq_cve_verify.py | 3.6+ paramiko | Sì (porta 53) | Sì (sola lettura) | LAN + WAN verso il DUT |
test_dnsmasq_cve_remote.py | 3.6+ stdlib | No | No | UDP 53 verso il DUT |
test_dnsmasq_cve_on_device.sh | N/A (shell) | No | Eseguito sul DUT | N/A |
malicious_dns_server.py | 3.6+ stdlib | Sì (porta 53) | No | Il DUT inoltra verso di noi |