
CHARON — pre-built PoC for CVE-2026-46333 (Linux ptrace mm==NULL fd theft)
____ _ _ _ ____ ___ _ _
/ ___|| | | | / \ | _ \ / _ \| \ | |
| | | |_| | / _ \ | |_) | | | | \| |
| |___ | _ |/ ___ \| _ <| |_| | |\ |
\____||_| |_/_/ \_\_| \_\\___/|_| \_|
transporta descritores de arquivo através do Styx exit-mm()
CVE-2026-46333 / Linux <= 6.12.89
"É coisa terrível cair nas mãos do Deus vivo." — Hebreus 10:31
Um PoC compacto, sem dependências, para CVE-2026-46333: a
burlagem de __ptrace_may_access mm==NULL divulgada pela Qualys em
15 de maio de 2026. Charon faz uma corrida entre pidfd_getfd(2) e um processo
SUID-root moribundo para capturar seu descritor de arquivo aberto /etc/shadow
através da breve janela mm-NULL em do_exit(). Execute-o como um usuário
não privilegiado em uma máquina afetada; ele exibe /etc/shadow na saída padrão.
$ ./charon
[banner no stderr]
[*] isca /usr/bin/chage alvo /etc/shadow
root:$y$j9T$ztS5H...$hz9W87TlqxEW...:...
daemon:*:20582:0:99999:7:::
bin:*:20582:0:99999:7:::
...
Taxa típica de acerto: menos de um segundo em uma VM de 4 núcleos, ~137 tentativas no teste de fumaça.
__ptrace_may_access() faz um curto-circuito em sua verificação de dumpability quando
task->mm == NULL. O caminho rápido foi escrito para threads do kernel
(swapper etc.), que legitimamente não têm mm e nunca devem ser
traceados. Mas do_exit() executa exit_mm() antes de exit_files(),
o que significa que um processo SUID do espaço do usuário brevemente possui:
task->mm == NULL (mm coletado) → verificação de dumpability ignoradasetreuid() → verificação de acesso passapidfd_getfd(2) confia nessa verificação de acesso e entrega ao atacante os
descritores de arquivo abertos do processo SUID.
do_exit()
├── exit_mm() ← task->mm = NULL
├── ... ← __ptrace_may_access() agora mente
└── exit_files() ← tabela fd coletada
Jann Horn sinalizou a forma de roubo de FD no lore.kernel.org em outubro de 2020. A correção ficou na revisão do mantenedor por ~6 anos antes de a Qualys trazê-la de volta ao topo da fila.
Correção upstream: 31e62c2ebbfd
(Linus 14/05/2026). Em 15/05/2026, o backport ainda não chegou ao
linux-6.12.y ou linux-6.6.y estável.
| Árvore estável | Status |
|---|---|
| linux-6.12.y (≤ 6.12.89) | ❌ vulnerável |
| linux-6.6.y (backport pré-correção) | ❌ vulnerável |
| mainline ≥ 6.15-rc1 | ✅ corrigido (31e62c2ebbfd) |
A tabela de PR / status contínuo será atualizada conforme os backports chegarem.
# Binário estático pequeno de 38 KB (recomendado)
sudo apt-get install musl-tools
make static
# Ou apenas a compilação padrão com glibc
make
Saída: um único ELF ./charon.
./charon # exibe /etc/shadow (padrão)
./charon -q # sem banner/progresso, apenas shadow no stdout
./charon -v # mostra estatísticas por tentativa + finais
./charon -r 5000 # mais paciência para sistemas lentos
./charon -t /etc/ssh/ssh_host_ecdsa_key # alvo diferente (usa isca ssh-keysign)
./charon -a # descobre automaticamente iscas SUID/SGID se as embutidas falharem
./charon -L # lista iscas candidatas sem tentar nenhuma
./charon --help
--auto percorre /usr/bin, /usr/sbin, /usr/local/{bin,sbin},
/usr/lib/openssh, /usr/libexec, /bin, /sbin, encontra todos
os arquivos regulares SUID/SGID (excluindo iscas interativas como su,
sudo, newgrp, pkexec) e tenta cada um como isca contra o alvo
solicitado. O orçamento por isca é apertado (5 rodadas × 2000 internas),
portanto uma varredura completa termina em ~10 segundos mesmo quando nada
corresponde.
--list-baits é a versão somente leitura — enumera os mesmos
candidatos sem disparar o exploit. Útil para verificar quais distribuições
trazem quais iscas.
| Código | Significado |
|---|
Charon vem com quatro iscas SUID conhecidas:
Adicionar uma isca é uma edição de 3 linhas no array lures[] em charon.c.
31e62c2ebbfd diretamente.pidfd_getfd(2) via seccomp em servidores de produção.chage e passwd se você não precisar que
usuários não privilegiados consultem o envelhecimento de senhas.no_new_privs no host
bloqueia completamente a primitiva — cada "SUID" dentro do contêiner
torna-se inerte, deixando Charon sem presa.A VM de desafio VRP do Google kernelctf executa o bash do jogador dentro
de uma sandbox nsjail com clone_newuser:true (uid 0 não mapeado),
chroot:/chroot e no_new_privs:1. Sob no_new_privs o bit setuid
é inerte, portanto não há presa SUID real dentro da sandbox, e /flag
vive no host fora do chroot. Portanto, Charon não pode ganhar kCTF VRP.
Continua sendo um LPE legítimo do Linux em instalações bare-metal das
famílias Debian / Ubuntu / RHEL.
Uso educacional e defensivo autorizado apenas.
⛵ STYX ⛵
╔══════════════════════════════╗
║ do_exit(): ║
║ ├── exit_mm() ← task->mm ║
║ │ = NULL ║
║ ├── ... ← ferry ║
║ └── exit_files() ║
╚══════════════════════════════╝
| Distribuição | Kernel | Status (15/05/2026) |
|---|
| Debian trixie | 6.12.86+deb13 | ❌ |
| AlmaLinux 10.1 | 6.12.0-124.55.3 | ❌ |
| Ubuntu 26.04 | 7.0.0-15 | ⚠️ verificar |
| Fedora 44 | 7.0.4-200 | ⚠️ verificar |
| 0 | Sucesso — conteúdo do arquivo no stdout |
| 1 | Nenhuma isca SUID neste sistema abre o arquivo solicitado |
| 2 | Kernel parece corrigido (CVE-2026-46333 fechada) |
| 3 | Esgotou as rodadas sem acerto (raro; tente -r 5000) |
| 4 | Erro de CLI / E/S |
| Binário | Arquivo que abre | Cobertura de distribuições |
|---|
/usr/bin/chage (chage -l <usuário>) | /etc/shadow | Maioria Debian, Ubuntu, Fedora |
/usr/sbin/chage | /etc/shadow | Família RHEL / Rocky / Alma |
/usr/bin/passwd (passwd -S <usuário>) | /etc/shadow | Maioria das distribuições |
/usr/lib/openssh/ssh-keysign | /etc/ssh/ssh_host_*_key | Distribuições com HostbasedAuthentication ativado |