
IPv4 SYN con opzioni IP, un target di laboratorio per esecuzione, per la ricerca su CVE-2026-98432. Parser dell'offset dell'header, test di regressione e postmortem di un auto-exploiter difettoso.
Una sonda a bersaglio singolo per CVE-2026-98432, la use-after-free del TCP IPv4 del kernel Linux corretta a ottobre 2026.
Costruisce un SYN IPv4 che trasporta opzioni IP (24 byte NOP per impostazione predefinita, che portano IHL a 11), lo invia a un singolo host e legge la risposta tramite l'offset dell'header invece del pattern matching. La logica dei pacchetti funziona anche offline: una dry run stampa e ri-analizza il pacchetto costruito, l'analizzatore hex analizza il traffico catturato e la suite di test non richiede né root né una rete.
Non c'è gestione di liste di bersagli, nessuna integrazione con Shodan e nessuna modalità batch. Un indirizzo per esecuzione, e la CLI rifiuta qualsiasi cosa al di fuori degli intervalli di laboratorio a meno che non si passi --allow-public. Lo strumento presuppone che il bersaglio sia di tua proprietà o che tu abbia il permesso scritto di testarlo.
python3 -m pip install -e ".[test]"
Python 3.9 o superiore. Solo libreria standard.
Dry run, nessun privilegio, nessun pacchetto inviato:
synipopt --dry-run --target 192.0.2.10
Analizza il pacchetto dal log dello script originale, quello che riportava come SYN-ACK:
synipopt --analyze-hex "$(cat docs/fixtures/original-log-packet.hex)"
Sonda una VM di laboratorio e confronta i SYN che trasportano opzioni con i SYN semplici:
sudo synipopt --target 192.168.56.101 --dport 443 --retries 3 --compare --log probe.jsonl
Costruzione predefinita, 64 byte sul filo:
0x01Un SYN che trasporta opzioni è ciò che mette ireq->ireq_opt sul request socket. Il kernel compila il blocco di opzioni, e il riempimento di soli NOP viene compilato in un blocco vuoto, quindi un SYN-ACK alla costruzione predefinita torna senza opzioni IP. Le opzioni che trasportano payload, ad esempio record route, vengono restituite. Misurato durante lo sviluppo contro un socket in ascolto:
| Opzioni SYN | Header IP del SYN-ACK | opzioni restituite |
|---|---|---|
| 24 byte NOP | 20 byte (IHL 5) | nessuna |
| Record Route, 12 byte | 32 byte (IHL 8) | 12 byte |
| nessuna | 20 byte (IHL 5) | nessuna |
Entrambi i fatti contano per il lavoro in laboratorio: la lunghezza dell'header della risposta indica se lo stack ha mantenuto il blocco di opzioni, e un parser corretto fa iniziare l'header TCP a IHL * 4, non all'offset 20.
Lo script in circolazione alla base di questo repo (cve_2026_98432_auto.zip) includeva il proprio log di esecuzione. Ogni SYN-ACK "riuscito" in quel log ha reply_sport=257 e tcp_seq=16843009. Quelli sono i byte di riempimento NOP: 0x0101 è 257 e 0x01010101 è 16843009. Il parser cercava i bit di flag ovunque nel pacchetto e finiva all'offset 55, all'interno del padding delle opzioni. Un'analisi corretta di quel pacchetto da 84 byte trova IHL 5, protocollo TCP e un segmento TCP il cui byte dei flag è 0x00, senza SYN e senza ACK.
Il payload è il SYN costruito dallo script stesso (0x4b all'offset 20). L'header esterno appartiene al kernel: lo script non impostava mai IP_HDRINCL, quindi Linux costruiva il proprio header IPv4 e prendeva il pacchetto costruito come payload. Nulla di ciò che costruiva veniva mai trasmesso come pacchetto. Due errori sovrapposti: il pacchetto non partiva mai come progettato, e il parser leggeva i byte di riempimento come risposta.
La fase di dork era inerte senza una chiave. Leggeva --shodan-key o SHODAN_KEY; senza nessuna delle due, scriveva 192.168.56.101 (un default host-only di VirtualBox) in targets.txt e proseguiva. Con una chiave avrebbe interrogato Shodan per host pubblici e avrebbe sparato contro di loro, cosa di cui nessuno strumento di laboratorio ha bisogno e che porta alla rimozione dei repo.
synipopt mantiene l'idea del singolo pacchetto sul filo, rimuove la fase di targeting, sostituisce il parser euristico con l'analisi basata sull'offset dell'header e trasforma i vecchi valori del log in un test di regressione in tests/test_packet.py.
CVE-2026-98432, networking IPv4 del kernel Linux, use-after-free, CVSS 3.1 9.8 secondo il CNA di Linux. Red Hat la classifica come Critical (RHSB-2026-016) ed elenca RHEL 8, 9, 10 e OpenShift tra i prodotti interessati.
La race, come descritta nel commit di correzione: tcp_v4_syn_recv_sock() trasferisce ireq->ireq_opt al socket figlio (newinet->inet_opt) senza copiarlo. Un'altra CPU può elaborare un SYN ritrasmesso per lo stesso request socket e inviare un SYN-ACK da tcp_check_req(). tcp_v4_send_synack() e inet_csk_route_req() leggono ireq->ireq_opt solo sotto rcu_read_lock(), e ip_build_and_send_pkt() e ip_options_build() leggono poi opt->optlen due volte. Poiché i request socket vengono elaborati senza il lock del listener (commit 079096f103fa), il figlio può essere liberato mentre il SYN-ACK è ancora in fase di costruzione. I socket figli non hanno SOCK_RCU_FREE, e inet_sock_destruct() liberava inet_opt con un semplice kfree(), quindi ip_options_build() poteva leggere memoria rilasciata.
La correzione mainline è il commit e3f33b0a1d89d738e2b913dab519226eaf76e15b, "ipv4: free inet_opt and ireq_opt after an RCU grace period" (Eric Dumazet, unito il 2026-10-05). Sostituisce kfree() con kfree_rcu() in inet_sock_destruct() e tcp_v4_reqsk_destructor(). Lo stesso commit segnala un secondo percorso tramite la migrazione dei request socket (net.ipv4.tcp_migrate_req=1 o un programma BPF reuseport), in cui inet_reqsk_clone() condivide ireq_opt tra il vecchio request socket e il suo clone.
La mitigazione temporanea di Red Hat scarta i pacchetti TCP IPv4 in ingresso che trasportano opzioni IP:
table inet cve_2026_98432 {
chain input {
type filter hook input priority -310; policy accept;
ip protocol tcp ip hdrlength > 5 counter drop
}
}
--compare verifica esattamente quel filtro: i SYN che trasportano opzioni restano senza risposta mentre i SYN semplici ricevono una risposta.
Fonti:
Le sonde a livello di rete non possono dimostrare una corruzione di memoria. Ciò che possono mostrare: se un host risponde ai SYN che trasportano opzioni IP, se il SYN-ACK restituisce quelle opzioni e se un filtro hdrlength si trova nel percorso.
uname -r rispetto all'advisory della propria distribuzione.tcpdump -ni eth0 'tcp and (ip[0] & 0x0f) > 5'sudo synipopt --target VM_IP --dport 22 --retries 5 --compare --log run.jsonlreply_kind classifica la risposta e il blocco tcp contiene l'header analizzato. ip_options_len mostra l'eco, che compare per blocchi di opzioni con payload (record route, timestamp); il puro riempimento NOP viene eliminato in fase di compilazione e ritorna come header di 20 byte.dmesg. A livello di rete si vede solo la gestione dei pacchetti.