← all projects
// PoC C · static CVE-2026-46333 v1.1.0
  ____  _   _    _    ____   ___  _   _
 / ___|| | | |  / \  |  _ \ / _ \| \ | |
| |    | |_| | / _ \ | |_) | | | |  \| |
| |___ |  _  |/ ___ \|  _ <| |_| | |\  |
 \____||_| |_/_/   \_\_| \_\\___/|_| \_|

  ferries fds across the exit-mm() Styx
  CVE-2026-46333  /  Linux <= 6.12.89

A tight, dependency-free PoC for CVE-2026-46333: the __ptrace_may_access mm==NULL bypass disclosed by Qualys on 2026-05-15. Races pidfd_getfd(2) against a dying SUID-root process to lift its open /etc/shadow file descriptor through the brief mm-NULL window in do_exit().

"It is a fearful thing to fall into the hands of the living God."
— Hebrews 10:31
git.thecoven github upstream fix
CVE2026-46333
targetLinux ≤ 6.12.89
binary38 KB · static
hit rate~137 tries · <1 s

Demo

Run as an unprivileged user on an affected box; it dumps /etc/shadow to 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:::
...

Typical hit rate: under one second on a 4-core VM (~137 tries in the smoke test).

The bug, in 30 seconds

__ptrace_may_access() short-circuits its dumpability check when task->mm == NULL. The fast-path was written for kernel threads (swapper et al.), which legitimately have no mm and should never be ptraced. But do_exit() runs exit_mm() before exit_files(), which means a userspace SUID process briefly has:

pidfd_getfd(2) trusts that access check and hands the attacker the SUID process's open file descriptors.

do_exit()
  ├── exit_mm()      ← task->mm = NULL
  ├── ...            ← __ptrace_may_access() now lies
  └── exit_files()   ← fd table reaped

Jann Horn flagged the FD-theft shape on lore.kernel.org in October 2020. The fix sat in maintainer review for ~6 years before Qualys brought it back to the front of the queue. Upstream commit 31e62c2ebbfd (Linus 2026-05-14).

Affected kernels

Stable treeStatus
linux-6.12.y (≤ 6.12.89)vulnerable
linux-6.6.y (pre-fix backport)vulnerable
mainline ≥ 6.15-rc1patched
DistroKernelStatus (2026-05-15)
Debian trixie6.12.86+deb13vulnerable
AlmaLinux 10.16.12.0-124.55.3vulnerable
Ubuntu 26.047.0.0-15check
Fedora 447.0.4-200check

Status table will be updated as stable-tree backports land.

Build

# 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.

Run

$ ./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-discovery

--auto walks /usr/bin, /usr/sbin, /usr/local/{bin,sbin}, /usr/lib/openssh, /usr/libexec, /bin, /sbin, finds every SUID/SGID regular file (excluding interactive baits like su, sudo, newgrp, pkexec), and tries each as a bait against the requested target. Per-bait budget is tight (5 rounds × 2000 inner) so a full scan finishes in ~10 s even when nothing matches. --list-baits is the read-only version.

Exit codes

CodeMeaning
0Success — file contents on stdout
1No SUID lure on this system opens the requested file
2Kernel appears patched (CVE-2026-46333 closed)
3Ran out of rounds without a hit (rare; try -r 5000)
4CLI / IO error

Lures

CHARON ships with four known SUID lures. Adding one is a 3-line edit to the lures[] array in charon.c.

BinaryFile it opensDistro coverage
/usr/bin/chage (chage -l <user>)/etc/shadowMost Debian, Ubuntu, Fedora
/usr/sbin/chage/etc/shadowRHEL / Rocky / Alma family
/usr/bin/passwd (passwd -S <user>)/etc/shadowMost distros
/usr/lib/openssh/ssh-keysign/etc/ssh/ssh_host_*_keyDistros with HostbasedAuthentication

Mitigations until your distro ships the backport

Not a kernelctf VRP candidate

The Google kernelctf VRP challenge VM runs the player's bash inside an nsjail sandbox with clone_newuser:true (uid 0 unmapped), chroot:/chroot, and no_new_privs:1. Under no_new_privs the setuid bit is inert, so there are no real SUID prey inside the sandbox, and /flag lives on the host outside the chroot. CHARON therefore cannot win kCTF VRP. It remains a legitimate Linux LPE on bare-metal Debian / Ubuntu / RHEL family installations.

Provenance

                        ⛵ STYX ⛵
              ╔══════════════════════════════╗
              ║  do_exit():                  ║
              ║   ├── exit_mm()   ← task->mm ║
              ║   │                = NULL    ║
              ║   ├── ...      ←  ferry      ║
              ║   └── exit_files()           ║
              ╚══════════════════════════════╝

Educational and authorized-defensive use only.