En bref
- Composant : pile TCP IPv4 du noyau Linux, fonction
tcp_v4_syn_recv_sock()(net/ipv4/tcp_ipv4.c,net/ipv4/af_inet.c).- Classe : condition de course qui débouche sur un use-after-free (CWE-416), atteignable à distance, sans authentification, pendant le handshake.
- Ancienneté : le bug existe depuis Linux 4.4 (2016), soit près de dix ans de noyaux.
- Correctif : commit
e3f33b0a1d89d'Eric Dumazet, intégré dans les stables 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56, 7.2.10 et 7.3.- Au 29 octobre 2026 : pas d'exploitation connue dans la nature, pas dans le catalogue KEV de la CISA, mais Ubuntu et RHEL n'ont toujours pas publié de noyau corrigé.
Cet article explique le mécanisme, la détection et la correction. Il ne contient volontairement aucun scénario d'exploitation.
Contexte
La faille a été découverte par Xinyang Ge (Anthropic), dont l'équipe a démontré en interne qu'elle permettait une prise de contrôle root à distance. L'exploit n'a pas été publié (The Hacker News).
Les scores divergent fortement selon les éditeurs, ce qui est en soi instructif :
| Source | Score CVSS 3.1 |
|---|---|
| CNA noyau Linux | 9.8 (AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H) |
| SUSE, Oracle | 8.1 |
| Amazon | 7.0 |
| Red Hat | 5.9, puis 8.1 le 29/10 |
Red Hat jugeait au départ la course trop difficile à gagner et n'y voyait qu'un déni de service. La démonstration d'Anthropic a contredit cette lecture, d'où la réévaluation (Cyber Kendra). Leçon générale : une race condition « difficile » n'est pas une race condition sûre, surtout quand perdre la course ne coûte rien à celui qui essaie.
IPv6 n'est pas concerné par le bug principal : son chemin de code copie les options au lieu de partager un pointeur (voir plus bas).
Rappel : qu'est-ce qu'un use-after-free ?
Un use-after-free (UAF) survient quand un programme continue d'utiliser une zone mémoire après l'avoir rendue à l'allocateur. Le pointeur, lui, existe toujours : il désigne maintenant une zone qui peut être réattribuée à n'importe quel autre objet.
allocation usage normal libération usage fautif
┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐
│ kmalloc() │ ───▶ │ lecture / │ ───▶ │ kfree() │ ───▶ │ lecture │ ✗
│ objet A │ │ écriture │ │zone rendue│ │ via ancien│
└───────────┘ └───────────┘ └─────┬─────┘ │ pointeur │
│ └───────────┘
▼
┌───────────────────┐
│ zone réattribuée │
│ à un objet B │
└───────────────────┘
Dans le noyau, c'est particulièrement grave : l'allocateur (SLUB) recycle très vite les zones libérées pour des objets de même taille. Le code qui lit « l'objet A » lit en réalité le contenu de « l'objet B ». Selon ce que contient B et ce que le code fait de A (longueur, pointeur de fonction, adresse), cela va de la simple corruption à la fuite d'information ou à la prise de contrôle du flot d'exécution.
Le mécanisme de CVE-2026-98432
1. Les options IP d'un SYN
Un paquet IPv4 peut porter des options dans son en-tête, au-delà des 20 octets standards : Record Route, Timestamp, etc. Elles sont rares dans le trafic légitime, mais le noyau doit les gérer.
Quand un serveur reçoit un SYN qui en contient, il les conserve dans la request socket (struct inet_request_sock), la petite structure qui représente une connexion à moitié ouverte, dans le champ ireq_opt. Il en a besoin pour les refléter dans le SYN-ACK, y compris en cas de retransmission.
2. Un pointeur partagé au lieu d'une copie
Quand le ACK final arrive, tcp_v4_syn_recv_sock() crée la socket « enfant », la vraie connexion établie. Les options passent dans son champ inet_opt… mais le noyau transmet le pointeur au lieu de copier les données.
Deux structures désignent alors la même zone mémoire, et chacune croit pouvoir en disposer :
Source du diagramme
flowchart LR
SYN["SYN avec options IP"] --> RSK["request socket<br/>ireq_opt"]
RSK -- "pointeur transmis<br/>(pas de copie)" --> CHILD["socket enfant<br/>inet_opt"]
RSK -.-> OPT[("zone mémoire<br/>options IP")]
CHILD -.-> OPT
style OPT fill:#fde2e4,stroke:#c0392b3. Pas de verrou sur ce chemin
Pour tenir la charge (un serveur peut recevoir des milliers de SYN par seconde), les request sockets sont traitées sans prendre le verrou de la socket d'écoute. Deux CPU peuvent donc manipuler la même request socket au même moment. C'est un choix de performance assumé, sûr tant que chaque objet a un seul propriétaire clair. Le partage du pointeur casse cette hypothèse.
4. La fenêtre de course
Deux traitements légitimes peuvent se croiser :
- sur un CPU, la socket enfant est créée puis détruite (par exemple sur réception d'un RST), ce qui libère
inet_optaveckfree(); - sur un autre CPU, le noyau prépare une retransmission du SYN-ACK pour la même request socket et lit encore
ireq_opt.
Comme les deux champs pointent vers la même zone, le second CPU lit de la mémoire déjà rendue à l'allocateur.
Source du diagramme
sequenceDiagram
participant A as CPU A (socket enfant)
participant M as Zone options IP
participant B as CPU B (request socket)
A->>M: inet_opt = ireq_opt (même pointeur)
B->>M: début de construction d'un SYN-ACK, lit ireq_opt
A->>M: socket enfant détruite → kfree(inet_opt)
Note over M: zone rendue à l'allocateur,<br/>réattribuable à tout moment
B->>M: continue de lire ireq_opt ✗ use-after-freeVue chronologique, en ASCII pour les lecteurs sans rendu Mermaid :
temps ─────────────────────────────────────────────────────────────▶
CPU A │ crée l'enfant détruit l'enfant
│ inet_opt=ireq_opt kfree(inet_opt)
│ │ │
│ ▼ ▼
zone │ [ allouée ][ partagée ][ LIBÉRÉE ][ réattribuée ? ]
│ ▲ ▲
│ │ │
CPU B │ lit ireq_opt ............ lit encore ireq_opt ✗
│ (SYN-ACK) (même pointeur)
│
│ └──────── fenêtre de course ────────┘
5. Pourquoi c'est dangereux
Trois propriétés rendent ce bug plus sérieux qu'une race condition ordinaire :
- Il est atteignable avant toute authentification. Tout se joue pendant le handshake TCP, avant que l'application (SSH, nginx, base de données…) ne voie la moindre donnée. N'importe quel port TCP IPv4 en écoute expose le chemin de code.
- Les données lues servent à construire un paquet. Le noyau ne se contente pas de lire la zone libérée : il s'en sert pour fabriquer un SYN-ACK. Si la zone a été réattribuée, des informations internes au noyau peuvent se retrouver dans la réponse envoyée sur le réseau, et des valeurs incohérentes peuvent corrompre d'autres structures.
- Perdre la course ne fait pas planter la machine. Dans la grande majorité des cas, seule la tentative de connexion échoue. Il n'y a ni crash ni redémarrage pour alerter l'administrateur, ce qui permet des essais répétés sans bruit visible côté service.
C'est cette combinaison, et non la difficulté théorique de la course, qui justifie le 9.8 du CNA noyau.
6. Pourquoi IPv6 s'en sort
Le chemin IPv6 équivalent copie les options dans la socket enfant au lieu de partager le pointeur. Chaque structure libère sa propre copie, la durée de vie des deux objets est indépendante, et la course n'a plus d'effet. Un cas IPv6 voisin, distinct, doit être traité dans un patch séparé (Atlas).
7. Le correctif : attendre une période de grâce RCU
Le commit e3f33b0a1d89 (« ipv4: free inet_opt and ireq_opt after an RCU grace period ») remplace les deux kfree() par des kfree_rcu().
RCU (Read-Copy-Update) est le mécanisme de synchronisation du noyau pensé pour les lecteurs sans verrou. Au lieu de libérer la mémoire immédiatement, kfree_rcu() attend la fin d'une période de grâce, c'est-à-dire le moment où tous les CPU sont passés par un état où ils ne peuvent plus tenir de référence lue avant la libération.
Source du diagramme
stateDiagram-v2
[*] --> Allouee: SYN avec options IP
Allouee --> Partagee: socket enfant créée (pointeur transmis)
Partagee --> Liberee: kfree() (avant le correctif)
Liberee --> UAF: lecteur concurrent encore actif
Partagee --> EnAttente: kfree_rcu() (après le correctif)
EnAttente --> LibereeSure: fin de période de grâce RCU<br/>(plus aucun lecteur possible)
LibereeSure --> [*]
UAF --> [*]Le lecteur sur l'autre CPU termine toujours sa lecture sur une mémoire encore valide. Le correctif ferme aussi un second chemin vers le même problème : la migration de request sockets entre listeners (net.ipv4.tcp_migrate_req=1, ou un programme BPF de type reuseport).
Une zone encore floue : MPTCP
La démonstration d'Anthropic visait des serveurs exposant un listener MPTCP (Multipath TCP). Ni la description du CVE ni le patch ne mentionnent MPTCP, et il n'a pas été précisé si un serveur sans MPTCP est exploitable dans les mêmes conditions (Atlas). Prudence donc : ne pas considérer un serveur sans MPTCP comme hors de danger, mais traiter en priorité ceux qui l'exposent.
Détection
Soyons clairs : aucun IOC (adresse, hash, signature) n'a été publié pour cette CVE au 29 octobre, faute d'exploitation connue. La détection repose donc sur des signaux comportementaux et sur l'inventaire. Aucun de ces signaux n'est une preuve à lui seul.
Signaux réseau
-
SYN entrants porteurs d'options IP. C'est la condition d'entrée du bug, et le trafic légitime en contient très peu. Un champ IHL supérieur à 5 (en-tête de plus de 20 octets) sur un SYN TCP mérite attention :
# Observation passive des SYN IPv4 avec options IP tcpdump -ni any 'ip[0] & 0x0f > 5 and tcp[tcpflags] & tcp-syn != 0'Côté IDS, une règle Suricata ou Zeek qui compte ces paquets par source donne une base de référence ; un pic soudain est un signal.
-
Rafales de SYN dupliqués et de handshakes avortés depuis une même source : SYN répétés pour le même tuple, suivis de RST, sans jamais aboutir à une session applicative. Le bug ne fait pas planter la machine quand la course échoue, donc ce motif répété est l'un des rares effets visibles.
-
SYN-ACK sortants anormaux : taille inhabituelle, options IP incohérentes avec le SYN reçu. Une capture en sortie (egress) sur les serveurs exposés peut révéler une réponse qui contient plus de données que prévu.
Signaux système
- Journaux noyau (
dmesg,journalctl -k) : oops,general protection fault,BUG: unable to handle page faultou avertissements SLUB (Poison overwritten,Object already free) dont la pile d'appels mentionnetcp_v4_syn_recv_sock,tcp_v4_send_synack,ip_optionsouinet_csk_*. Un crash isolé peut être un essai raté. - Compteurs TCP :
nstat -az | grep -Ei 'ListenDrops|ListenOverflows|TCPReqQFullDrop|SynRetrans'à suivre dans le temps. Une hausse anormale ne prouve rien mais justifie un regard. - Après coup : processus root inattendus sans parent applicatif cohérent, nouveaux modules chargés, modifications de
/etc/passwdou des clés SSH. Les règles auditd ou EDR habituelles de post-compromission s'appliquent.
En laboratoire
Sur un noyau de test compilé avec KASAN (Kernel Address Sanitizer), toute lecture de la zone libérée produit un rapport BUG: KASAN: slab-use-after-free qui nomme la fonction fautive. C'est la façon la plus fiable de valider qu'un noyau est corrigé, à faire uniquement dans une VM isolée. Innora a publié une analyse de cause et un environnement QEMU de reproduction, sans exploit (The Hacker News).
Attention aux faux PoC
Des dépôts GitHub sur cette CVE sont apparus dès le 29/10. Les « PoC » publiés dans la foulée d'une CVE médiatisée contiennent régulièrement des malwares visant… les chercheurs qui les lancent. Ne rien exécuter hors d'une VM jetable, sans réseau vers la production.
Remédiation
1. Savoir où on en est
uname -r # noyau en cours d'exécution
needs-restarting -r # RHEL / Fedora : redémarrage requis ?
ls /var/run/reboot-required 2>/dev/null # Debian / Ubuntu
Un paquet noyau installé ne protège qu'après redémarrage sur ce noyau.
2. Appliquer les versions corrigées
Noyaux amont (stables) : 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56, 7.2.10, 7.3 et suivants. Pour un noyau compilé maison, passer sur une stable corrigée plutôt que de reporter le patch à la main.
Distributions, au 29 octobre 2026 (Cyber Kendra, Atlas) :
| Distribution | Statut |
|---|---|
| Debian 13 | Corrigé le 16/10 (DSA-6568-1, linux 6.12.113-1) |
| Debian 12 | Corrigé (DLA-4841-1, DLA-4850-1) |
| Oracle Linux 7 à 10 (UEK) | Corrigé le 16/10 |
| openSUSE Leap 16.0, SUSE Linux Micro 6.2 | Corrigé |
| SLES 15 SP4 à SP7, SLES 16.0 | En cours ou en test |
| Amazon Linux 2023 (kernel6.18, kernel6.12) | Corrigé les 24 et 29/10 |
| Amazon Linux 2, noyau AL2023 par défaut | En attente |
| Ubuntu 16.04 à 26.10 | Vulnérable, pas de correctif |
| RHEL 8, 9, 10, OpenShift 4 | Correctif reporté |
| RHEL 7 | Non affecté |
Ce tableau bouge vite : vérifier les trackers officiels (Ubuntu CVE tracker, Red Hat Customer Portal) avant de conclure.
3. En attendant le correctif : contournements
Filtrer les paquets TCP IPv4 porteurs d'options IP. C'est la mesure temporaire proposée par Atlas : jeter le TCP IPv4 dont l'en-tête dépasse 20 octets, ce qui supprime la condition d'entrée du bug (Atlas).
nft add table ip cve_2026_98432
nft add chain ip cve_2026_98432 pre '{ type filter hook prerouting priority -300; }'
nft add rule ip cve_2026_98432 pre meta l4proto tcp ip hdrlength gt 5 counter drop
Limites à connaître :
- mesure non validée par un éditeur, testée seulement contre l'option Record Route ;
- elle ne survit pas au redémarrage : l'ajouter à
/etc/nftables.confsi elle doit durer ; - les options IP sont rares mais pas inexistantes : tester sur un serveur avant de généraliser, et surveiller le compteur (
nft list table ip cve_2026_98432) qui sert au passage de signal de détection ; - la placer au plus près de la bordure (pare-feu, load balancer) protège aussi les machines qu'on ne peut pas toucher.
Réduire la surface MPTCP. Lister les listeners avec ss -Mlnp et vérifier sysctl net.mptcp.enabled. Les serveurs exposant MPTCP sur Internet passent en premier dans la file de patch. Personne n'a confirmé que désactiver MPTCP suffit à se protéger.
Laisser net.ipv4.tcp_migrate_req à 0 (valeur par défaut). Cela ferme le second chemin corrigé par le patch, sans être une protection complète.
Livepatch. Si vous utilisez Canonical Livepatch, kpatch ou Ksplice, vérifier si un correctif à chaud est publié pour cette CVE : c'est le moyen de corriger sans fenêtre de redémarrage.
4. Bonnes pratiques à retenir
- Exposer le moins de ports TCP possible sur Internet. Un service non exposé n'atteint pas ce chemin de code depuis l'extérieur.
- Mettre l'inventaire noyau au même niveau que l'inventaire applicatif : version en cours, date du dernier redémarrage, présence de MPTCP.
- Ne pas se fier au seul score d'un éditeur. L'écart 5.9 / 9.8 sur cette CVE montre qu'un score peut sous-estimer un bug tant que personne n'a démontré son impact.
- Prévoir des fenêtres de redémarrage régulières. Une grande partie des « serveurs patchés » tournent encore sur l'ancien noyau.
Ce qu'il faut retenir
CVE-2026-98432 n'est pas un bug exotique : c'est un pointeur partagé entre deux objets dont les durées de vie divergent, sur un chemin sans verrou optimisé pour la performance. La correction tient en deux appels remplacés, mais le bug a survécu près de dix ans. La priorité est simple : patcher et redémarrer là où c'est possible, filtrer les options IP là où ça ne l'est pas encore, et surveiller les SYN porteurs d'options en attendant les correctifs Ubuntu et Red Hat.
Sources
- The Hacker News — Linux Kernel TCP Flaw With 9.8 CVSS Score (29 oct. 2026) : https://thehackernews.com/2026/10/linux-kernel-tcp-flaw-with-98-cvss.html
- Atlas — Linux's October 2026 TCP Use-After-Free : https://atlasum.net/blog/cve-2026-98432-linux-ipv4-tcp-uaf
- Cyber Kendra — CVE-2026-98432 Still Unpatched on Ubuntu and RHEL : https://www.cyberkendra.com/2026/10/cve-2026-98432-linux-kernel-tcp-flaw-ubuntu-rhel.html
- Commit du correctif :
e3f33b0a1d89, « ipv4: free inet_opt and ireq_opt after an RCU grace period » (Eric Dumazet)