
IPv4 SYN avec options IP, une cible de laboratoire par exécution, pour la recherche sur CVE-2026-98432. Analyseur d'offset d'en-tête, tests de régression et postmortem d'un auto-exploiteur défaillant.
Une sonde à cible unique pour CVE-2026-98432, la faille use-after-free dans la pile IPv4 TCP du noyau Linux corrigée en octobre 2026.
Elle construit un SYN IPv4 transportant des options IP (24 octets NOP par défaut, ce qui porte l'IHL à 11), l'envoie à un seul hôte et lit la réponse par décalage d'en-tête plutôt que par correspondance de motifs. La logique des paquets fonctionne aussi hors ligne : une exécution à blanc affiche et réanalyse le paquet forgé, l'analyseur hexadécimal analyse le trafic capturé, et la suite de tests ne nécessite ni root ni réseau.
Il n'y a pas de gestion de liste de cibles, pas d'intégration Shodan et pas de mode par lots. Une adresse par exécution, et la CLI refuse tout ce qui sort des plages de laboratoire sauf si vous passez --allow-public. L'outil suppose que vous possédez la cible ou que vous disposez d'une autorisation écrite pour la tester.
python3 -m pip install -e ".[test]"
Python 3.9 ou plus récent. Bibliothèque standard uniquement.
Exécution à blanc, sans privilèges, sans paquet envoyé :
synipopt --dry-run --target 192.0.2.10
Analyser le paquet issu du journal du script d'origine, celui qu'il a signalé comme SYN-ACK :
synipopt --analyze-hex "$(cat docs/fixtures/original-log-packet.hex)"
Sonder une VM de laboratoire et comparer les SYN porteurs d'options aux SYN simples :
sudo synipopt --target 192.168.56.101 --dport 443 --retries 3 --compare --log probe.jsonl
Construction par défaut, 64 octets sur le réseau :
0x01C'est un SYN transportant des options qui place ireq->ireq_opt sur le socket de requête. Le noyau compile le bloc d'options, et un remplissage uniquement NOP se compile en un bloc vide, donc un SYN-ACK répondant à la construction par défaut revient sans options IP. Les options porteuses de données, comme record route, sont renvoyées en écho. Mesuré pendant le développement face à un socket en écoute :
| Options SYN | En-tête IP du SYN-ACK | Options renvoyées en écho |
|---|---|---|
| 24 octets NOP | 20 octets (IHL 5) | aucune |
| Record Route, 12 octets | 32 octets (IHL 8) | 12 octets |
| aucune | 20 octets (IHL 5) | aucune |
Ces deux faits comptent pour le travail en laboratoire : la longueur d'en-tête de la réponse vous indique si la pile a conservé le bloc d'options, et un analyseur correct commence l'en-tête TCP à IHL * 4, et non au décalage 20.
Le script qui circule derrière ce dépôt (cve_2026_98432_auto.zip) livrait son propre journal d'exécution. Chaque SYN-ACK « réussi » de ce journal présente reply_sport=257 et tcp_seq=16843009. Ce sont les octets de remplissage NOP : 0x0101 vaut 257 et 0x01010101 vaut 16843009. L'analyseur cherchait les bits de drapeaux n'importe où dans le paquet et tombait sur le décalage 55, à l'intérieur du remplissage d'options. Une analyse correcte de ce paquet de 84 octets trouve IHL 5, protocole TCP, et un segment TCP dont l'octet de drapeaux vaut 0x00, sans SYN ni ACK.
La charge utile est le SYN forgé par le script lui-même (0x4b au décalage 20). L'en-tête externe appartient au noyau : le script n'a jamais défini IP_HDRINCL, donc Linux a construit son propre en-tête IPv4 et a pris le paquet forgé comme charge utile. Rien de ce qu'il construisait n'a jamais été transmis en tant que paquet. Deux défaillances se cumulaient : le paquet n'est jamais parti comme prévu, et l'analyseur a lu les octets de remplissage comme la réponse.
L'étape de dork était inerte sans clé. Elle lisait --shodan-key ou SHODAN_KEY ; sans l'un ni l'autre, elle écrivait 192.168.56.101 (une valeur par défaut host-only de VirtualBox) dans targets.txt et poursuivait. Avec une clé, elle interrogeait Shodan pour des hôtes publics et tirait dessus, ce dont aucun outil de laboratoire n'a besoin et qui fait supprimer les dépôts.
synipopt conserve l'idée du paquet unique sur le réseau, supprime l'étape de ciblage, remplace l'analyseur heuristique par une analyse par décalage d'en-tête, et transforme les anciennes valeurs du journal en test de régression dans tests/test_packet.py.
CVE-2026-98432, pile réseau IPv4 du noyau Linux, use-after-free, CVSS 3.1 9.8 selon le CNA Linux. Red Hat la classe Critique (RHSB-2026-016) et liste RHEL 8, 9, 10 et OpenShift parmi les produits affectés.
La course, telle que décrite dans le commit de correction : tcp_v4_syn_recv_sock() transfère ireq->ireq_opt vers le socket enfant (newinet->inet_opt) sans copie. Un autre CPU peut traiter un SYN retransmis pour le même request socket et envoyer un SYN-ACK depuis tcp_check_req(). tcp_v4_send_synack() et inet_csk_route_req() lisent ireq->ireq_opt uniquement sous rcu_read_lock(), et ip_build_and_send_pkt() et ip_options_build() lisent ensuite opt->optlen deux fois. Comme les request sockets sont traités sans le verrou du listener (commit 079096f103fa), l'enfant peut être libéré pendant que le SYN-ACK est encore en cours de construction. Les sockets enfants n'ont pas SOCK_RCU_FREE, et inet_sock_destruct() libérait inet_opt avec un simple kfree(), si bien que ip_options_build() pouvait lire de la mémoire libérée.
La correction mainline est le commit e3f33b0a1d89d738e2b913dab519226eaf76e15b, « ipv4: free inet_opt and ireq_opt after an RCU grace period » (Eric Dumazet, fusionné le 2026-10-05). Il remplace kfree() par kfree_rcu() dans inet_sock_destruct() et tcp_v4_reqsk_destructor(). Le même commit signale un second chemin via la migration de request socket (net.ipv4.tcp_migrate_req=1 ou un programme BPF reuseport), où inet_reqsk_clone() partage ireq_opt entre l'ancien request socket et son clone.
L'atténuation temporaire de Red Hat supprime les paquets TCP IPv4 entrants qui transportent des options 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 teste exactement ce filtre : les SYN portant des options restent sans réponse tandis que les SYN simples reçoivent une réponse.
Sources :
Les sondes réseau ne peuvent pas prouver une corruption mémoire. Ce qu'elles peuvent montrer : si un hôte répond aux SYN portant des options IP, si le SYN-ACK renvoie ces options, et si un filtre hdrlength se trouve sur le chemin.
uname -r par rapport à l'avis de votre distribution.tcpdump -ni eth0 'tcp and (ip[0] & 0x0f) > 5'sudo synipopt --target VM_IP --dport 22 --retries 5 --compare --log run.jsonlreply_kind classe la réponse et le bloc tcp contient l'en-tête analysé. ip_options_len montre l'écho, qui apparaît pour les blocs d'options avec charge utile (record route, timestamp) ; le remplissage NOP pur est éliminé à la compilation et revient sous forme d'en-tête de 20 octets.dmesg. Le réseau ne montre que le traitement des paquets.