
Votre hôte Linux est-il exposé à CVE-2026-98432 ?
Vérifie si un hôte Linux peut être exposé à CVE-2026-98432, la use-after-free dans la poignée de main TCP IPv4 du noyau. Fait partie de shouldipanic.ch.
Un SYN portant des options IPv4 (Record Route, Timestamp) atteint du code qui lit les options après qu'elles ont pu déjà être libérées. N'importe quel port TCP IPv4 ouvert suffit, aucune connexion requise. C'est présent dans tous les noyaux depuis 4.4 (2016) jusqu'à la version corrigée de chaque branche : 5.10.272, 5.15.223, 6.1.190, 6.6.159, 6.12.113, 6.18.56, 7.2.10, 7.3. Le CNA du noyau lui attribue un score de 9.8 ; un exploit root distant fonctionnel contre des serveurs avec des listeners MPTCP a été signalé (non publié). Le correctif est invisible sur le réseau : un noyau corrigé répond exactement comme un noyau non corrigé.
Cela détermine ce que chaque mode peut affirmer :
--on-host lit le noyau en cours d'exécution sur la machine elle-même : sa version,
le changelog du paquet et si un noyau plus récent attend un redémarrage.Server et indique si cette distribution
a déjà publié un noyau corrigé. C'est un indice fort, pas un verdict : derrière un
reverse proxy ou dans un conteneur, le noyau appartient à une autre machine.kernel98scan se présente sous deux formes :
kernel98scan.py : un fichier Python unique sans dépendances. Sur un hôte,
--on-host donne le verdict réel ; pointé vers des adresses, il effectue une
empreinte de distribution sur des plages IP, avec un scan de ports et une découverte de vhosts.
curl -LO https://github.com/akaihigurashi/kernel98scan/releases/latest/download/kernel98scan.py
python3 kernel98scan.py --on-host # real verdict for THIS machine
Python 3.8 ou plus récent, rien à installer. Exemples :
python3 kernel98scan.py --on-host # read the running kernel
python3 kernel98scan.py 203.0.113.10 # outside: distribution + fix status
python3 kernel98scan.py 203.0.113.0/24 # a whole range from outside
python3 kernel98scan.py 203.0.113.10 --ip-options # + do IP-options packets get through?
python3 kernel98scan.py 203.0.113.10 -o out.csv # CSV output
python3 kernel98scan.py -h # all options
Sur l'hôte (--on-host, code de sortie 2 / 1 / 0 / 3) :
| Statut | Signification |
|---|---|
VULNERABLE | Rien n'indique le correctif dans le noyau en cours d'exécution |
CHECK | Un noyau corrigé est installé, mais l'ancien est toujours en cours d'exécution : redémarrer |
FIXED | Le changelog du noyau mentionne la CVE, ou un noyau upstream standard est à la version corrigée |
Les noyaux des fournisseurs (Ubuntu, RHEL, Proxmox) rétroportent les correctifs sur d'anciens numéros de version, donc
pour eux seul le changelog compte. Il indique aussi si MPTCP est activé (l'exploit signalé passe par les
listeners MPTCP) et tcp_migrate_req.
Depuis l'extérieur :
| Statut | Signification |
|---|---|
POSSIBLY VULNERABLE | La bannière indique une distribution Linux : soit elle n'a pas encore de noyau corrigé (Ubuntu, RHEL et leurs rebuilds aujourd'hui), soit elle en a un, qui doit être installé et démarré |
UNKNOWN | La distribution n'est pas visible, ou un proxy/CDN répond |
NOT LINUX | L'en-tête Server pointe vers un hôte non-Linux |
VULNERABLE et PATCHED ne sont jamais rapportés depuis l'extérieur. Le statut du correctif par
distribution est une table dans check.py (FIX_STATUS, FIXED_TABLE),
mise à jour à chaque version du fournisseur.
| Option | Ce qu'elle fait |
|---|---|
--on-host | Lit le noyau en cours d'exécution sur cette machine et donne un verdict réel |
--ip-options | Une connexion dont le SYN porte une option IP Record-Route (voir ci-dessous) |
-o FILE | Écrit aussi tous les résultats dans un CSV |
-a / -v | Affiche les résultats masqués / affiche les preuves |
-d | Débogage : chaque requête, en-tête et correspondance |
-q | Pas de bannière (pour les scripts) |
-V | Affiche la version |
-h | Toutes les options |
--ip-options (opt-in, ligne de commande uniquement)Ouvre une connexion normale et une dont le SYN porte une option IP Record-Route vide,
et indique si de tels paquets atteignent l'hôte (reach), sont abandonnés
en chemin (filtered) ou si le port est fermé (noport). C'est la forme de paquet
par laquelle le bug est atteint, donc elle est désactivée par défaut et ne s'exécute jamais sur la
page web : utilisez-la sur vos propres hôtes, par exemple pour confirmer que votre pare-feu
abandonne les options IP. Une seule connexion ne peut pas gagner la course nécessaire au bug, et un
résultat filtered peut provenir de n'importe quel réseau intermédiaire.
nft add table ip cve98432
nft add chain ip cve98432 pre '{ type filter hook prerouting priority -300; }'
nft add rule ip cve98432 pre meta l4proto tcp ip hdrlength != 5 drop
sysctl -w net.mptcp.enabled=0 # if you don't need MPTCP
Supprime les paquets TCP IPv4 qui transportent des options IP. Non persistant après les redémarrages, et ne remplace pas le noyau corrigé.
docker run -d --name kernel98scan --restart unless-stopped \
-p 127.0.0.1:8092:8092 \
-e PANIC_ORIGIN=https://kernel98scan.example.ch \
-v kernel98scan-data:/data \
ghcr.io/akaihigurashi/kernel98scan:latest
Ou avec Compose : téléchargez docker-compose.yml, définissez
PANIC_ORIGIN, puis docker compose up -d. Le conteneur n'écoute que sur localhost ;
placez un reverse proxy avec TLS devant celui-ci
(exemple nginx).
| Variable | Valeur par défaut | Rôle |
|---|---|---|
PANIC_ORIGIN | – | Adresse publique. Les requêtes provenant d'autres origines sont refusées. Définissez-la. |
PANIC_OPERATOR / PANIC_CONTACT | – | Section de contact facultative en bas de la page |
PANIC_IMPRINT_URL | – | Lien facultatif vers une mention légale existante |
PANIC_TRUSTED_PROXY | 127.0.0.1,::1 | Proxies dont l'en-tête X-Real-IP est approuvé |
PANIC_DENY_HOSTS / PANIC_DENY_IPS | – | Ne jamais vérifier ces domaines / adresses |
PANIC_AUDIT_DIR | /data/audit | Fichiers de journal d'audit quotidiens |
PANIC_RETENTION_DAYS | 30 | Les fichiers d'audit plus anciens que cette durée sont supprimés |