你好。CVE-2026-98432 是 Linux 内核中一个可远程利用的释放后使用(Use-After-Free)漏洞。部分报道称,AI 实验室通过多路径 TCP(MPTCP)在大约 45 分钟内利用了该漏洞。该漏洞实际上存在于 IPv4 TCP 协议栈本身,只是 MPTCP 是唯一勉强可行的、能够到达易受攻击代码路径的方式。即便如此,对于约 45 分钟这一说法,也应当以极其审慎的眼光加以审视。
ireq_opt 加载到第一次解引用之间的距离。当然,确实存在一些自定义 Linux 内核,它们使用 MPTCP 并配合逐包引导选项,这很可能使该漏洞在“最纯粹的意义上”变得“可达”。但实际而言,你几乎可以肯定没有运行这样的内核配置,而且你也不太可能倒霉到有人延迟足够接近、又足够执着地恰好击中那个间隙。
使利用勉强具备实际可行性的主要因素是虚拟化/超售的虚拟机,配合 PREEMPT_RT、threadirqs 等……而这并非攻击者所能控制或影响,除非他们拥有某种额外的特权/已认证访问权限。我在业界有些朋友,他们大概知道一两个能改变我想法的技巧,但我并不知道任何此类技巧,AI 也一无所获,而且至少那个“技巧”——如果存在的话——并不广为人知。
你应该为服务器打补丁,但大概不必为此恐慌。本仓库中的其他所有内容,包括下面这些文字,都是 AI 生成的,但我引导了会话,并已审查了产物的准确性。
如下所述:
一个远程黑盒探测工具,通过数据包级证据证明 CVE-2026-98432(Linux IPv4 TCP ireq_opt / inet_opt 释放后使用)的存在——而非通过匹配内核版本。
它止步于释放后使用:它证明内核读取了已释放的内存,并在网络上表明了这一点。它不绕过 KASLR、不劫持控制流、也不执行代码。
仅限授权测试。竞争探测可能使目标不稳定;内核崩溃是可能的结果。仅在你拥有或已获得书面许可测试的主机上使用。
修复提交是 e3f33b0a1d89d738e2b913dab519226eaf76e15b(ipv4: free inet_opt and ireq_opt after an RCU grace period),这是一个两行改动,将 inet_sock_destruct() 和 tcp_v4_reqsk_destructor() 中的 kfree() 改为 kfree_rcu()。
tcp_v4_init_req() 将 SYN 回显的 IP 选项(Record Route / Timestamp)存储在 ireq->ireq_opt 中。tcp_v4_syn_recv_sock() 转移该指针到子套接字,而不是复制它。rcu_read_lock() 下读取 ireq->ireq_opt,并在 ip_options_build() 中使用它。最后一点就是检测器:内核将过期的选项字节回显给发送方。
范围更正:CVE 记录是通用 IPv4 TCP。只有 Anthropic 的博客将其与“多路径 TCP 监听器”联系起来;没有任何 CVE/NVD/Red Hat/Debian/Ubuntu 文本提及 MPTCP。该探测首先测试普通 TCP。
三条独立证据链,从最弱到最强:
| 环节 | 展示内容 | 产物 |
|---|---|---|
| 静态(build-id 固定) | 运行中内核的代码形态:两个析构函数处均为普通 kfree = 存在漏洞;kvfree_call_rcu = 已修补 | verify-local,见下表 |
| 合成预言机 | 当响应方回显被复用的 slab 内存时,线上检测器触发 | scripts/validate_synthetic.sh → CONFIRMED VULNERABLE |
| 动态、内核级(KVM 实验环境) | KASAN 报告 ip_options_build 中实际的 slab-use-after-free,并附带该提交的调用栈;在相同流量下,kfree_rcu 内核保持干净 | evidence/guest-inst-vuln6-KASAN.log、evidence/guest-inst-patched2-CONTROL.log |
| 线上可见、真实内核(非 KASAN 实验环境) | 线上的 SYN-ACK 携带了喷射的选项块(42 04 be ef…),而非回显的时间戳;kfree_rcu 对照组保持干净 | evidence/race-stage1-wire-uaf.json、evidence/race-control-kfree_rcu.json |
动态环节是一个专门构建的 7.0.14 客户机(KASAN + 启动参数窗口拉伸),并配有逐包 CPU 引导:重传的 SYN 在一个 vCPU 上处理,ACK/传输在第二个 vCPU 上处理,受害者的 accept()+close() 在第三个 vCPU 上处理,因此该覆盖是常规发生的,而非偶然。内核本身报告了:
BUG: KASAN: slab-use-after-free in ip_options_build
Read of size 16 ... by task swapper/0 (cpu0, softirq)
__asan_memcpy <- ip_options_build <- ip_build_and_send_pkt
<- tcp_v4_send_synack <- tcp_rtx_synack <- tcp_check_req <- tcp_v4_rcv
Freed by task 1 (victim, cpu3):
kfree <- inet_sock_destruct <- __sk_destruct <- inet_release <- __x64_sys_close
object: kmalloc-96, 16 bytes in (struct ip_options at the rcu_head offset)
加上 30 条读端 READ-AFTER-FREE 记录(在 free 之后 71–322 µs 读取,optlen=40,KASAN shadow 0xfb)。打过补丁的对照组(kfree_rcu,已在目标文件中验证)运行了相同的竞态与保持:0 KASAN,0 read-after-free。这就是漏洞本身,而不是版本猜测。
读端并不是一个长循环。tcp_v4_send_synack() 加载 ireq->ireq_opt 并将其传给 ip_build_and_send_pkt()/ip_options_build(),后者的 memcpy 就是 use-after-free 读取;加载与读取之间只相隔几条指令(几十到几百纳秒)。而 free 一侧——受害者的 accept()+close() 之后的子对象销毁——要晚得多:仅实测的 close 周期就有 61–92 µs,而 ACK→销毁的延迟还要加上跨 CPU 唤醒。
因此这两个事件并非只是很少同时发生;它们在构造上就相隔约 90 µs,而读取早在 free 之前很久就结束了。自然时序实验对此进行了量化:在同一个插桩内核上,不做窗口拉伸,约 14 万次竞态尝试以及并发的半开连接搅动,
stats: frees=180223 overlaps=0 raf=0 KASAN: 0
从未有一次落在读取方的窗口内。第二次更强的尝试使用了实验环境的 slab 喷射:8,000 次尝试,每次携带 200 个重传 SYN(160 万个独立读取方窗口),针对一个干净的内核,其读取方 vCPU 保持可运行并被宿主 CFS 抢占任务抢占——仍然没有任何携带喷射的 SYN-ACK。读取方 vCPU 大约每 5 ms 运行时间被抢占一次;在 20-40 秒运行时间内,约 10 ns 的 200 万个窗口即使在完全均匀的模型下也应有若干次命中,而观察到的零说明抢占点在客户机指令流中并不均匀。
因此,线路级证明需要读取方被确定性地延迟,实验环境通过启动参数窗口拉伸(reader_hold_us)实现这一点;在真实目标上,只有在这约 10 ns 内发生 hypervisor 或调度器停顿才能做到。这就是为什么远程探测将阳性视为证明,将阴性视为一个界限,而绝不视为“已修补”。
还有一个后果:在 KASAN 内核上,隔离区会保持已释放字节的完整性,因此即使 UAF 发生,线路也无法显示它。在生产内核上,线路预言机仅在已释放的 kmalloc-96 槽位在读取方的 memcpy 之前被复用时才会触发。这条路径不再只是合成的:在非 KASAN 实验内核(无隔离区)中,受害者关闭子连接并立即用 40 字节的选项对象重新分配该槽位,内核将它们复制进一个 SYN-ACK(42 04 be ef…,evidence/race-stage1-wire-uaf.json)。同一内核使用 kfree_rcu 时没有产生任何结果(evidence/race-control-kfree_rcu.json)。
uv run netprobe preconditions -t <target> -p <port>
NOT_REACHABLE。IPPROTO_MPTCP 连接并检查 MPTCP_INFO,因此可以直接测试“MPTCP 监听器”这一说法。对于每个元组,探测会记录基线 SYN-ACK 选项结构
S0 = (total option length, ordered (kind, length, pointer))。
随后它以微秒级偏移竞速发送一个重复 SYN、一个 ACK 和一个 RST。重复 SYN 使
tcp_check_req() 读取 ireq_opt;ACK 将指针交给子套接字;RST 将其释放。
任何后续 SYN-ACK 若其结构与 S0 不同,在正确的内核上都不可能发生。这是一个单侧证明,不依赖于 KASLR 泄漏或崩溃。
时间戳值和记录路由地址被排除在比较之外:
Linux 在回显中会重写这两者(已验证:指针被强制为 len+1,两个时间戳槽位都被替换为内核时钟)。
| 判定 | 含义 |
|---|---|
CONFIRMED_VULNERABLE | 捕获到结构性回显异常,并附有数据包 |
INCONCLUSIVE | N 次尝试,无异常;明确为弱否定 |
NOT_REACHABLE | 无 IP 选项回显,或无 ireq_opt 分配路径 |
该工具从不输出“已修补”。远程否定不能作为证明:在已修补的内核上,每次尝试的概率为零,但在存在漏洞的内核上,该概率很小且未知,因此一次干净运行只能对其给出上界(1-(1-p)^N)。
spoof(已在此沙箱中验证)从可路由但非本地的源地址(例如 203.0.113.7)发送 SYN。目标的 SYN-ACK 从接口发出;本地内核从不处理它,因此无法发出自己的 RST。探测使用 AF_PACKET 捕获 SYN-ACK,并完全掌控 ACK / 重复 SYN / RST 的时序。
适用于可从扫描主机访问的目标(实验室、链路上,或你可对其源地址欺骗的目标)。仅需 CAP_NET_RAW。
sudo uv run netprobe race -t 172.18.0.3 -p 9101 \
--iface eth0 --spoof 203.0.113.7 --variant child -n 100000
firewall(远程目标,本沙箱中未实际执行)针对真实的远程目标,SYN-ACK 会返回给扫描器自身的地址,内核会对其发送 RST。要抑制这些 RST 需要 CAP_NET_ADMIN:
sudo uv run netprobe race -t 198.51.100.10 -p 80 --mode firewall -n 100000
这会安装一条作用域受限的 nftables 规则,用于丢弃探测端口范围内本地生成的 RST,除非这些 RST 携带探测的 SO_MARK(netprobe/netfilter.py)。用于开发的沙箱没有 CAP_NET_ADMIN(CapEff 0xa80425fb),因此该路径虽已生成并通过单元测试,但在此处并未进行端到端验证。
对于你可以检查的主机,自由调用的形态是确定性的:
uv run netprobe verify-local \
--vmlinux /usr/lib/debug/boot/vmlinux-$(uname -r) \
--expected-build-id "$(readelf -n /sys/kernel/notes | awk '/Build ID/{print $3}')"
kfree → 存在漏洞;kvfree_call_rcu → 已修补。已在两类镜像上验证:
| 镜像 | inet_sock_destruct | tcp_v4_reqsk_destructor | 判定 |
|---|---|---|---|
Ubuntu 7.0.0-42.42 (82d07c0b…) | kfree | kfree | 存在漏洞 |
Ubuntu 7.3.0-10.10 (a24c081e…) | kvfree_call_rcu | kvfree_call_rcu | 已修补 |
scripts/fetch_vmlinux.sh 可从 Ubuntu ddebs 归档中复现下载/提取步骤。
src/netprobe/
l3/ipopts.py option build/parse, structural fingerprint
l4/tcp.py raw IPv4/TCP build/parse
l4/link.py spoof (AF_PACKET) and connected links
l4/fastsend.py per-CPU forked senders (no GIL)
netfilter.py scoped RST suppression for remote mode
probe/precondition.py
probe/race.py
probe/churn.py
local/verify.py static build-id + disassembly classifier
steer.py eBPF per-packet queue steering (two-queue and four-queue)
cli.py
lab/victim.py lab listener (MPTCP optional, accept/close/hold)
lab/fake_server.py synthetic reuse responder (detector validation)
lab/guest/ KVM lab: kernels, initramfs, per-CPU steering, victims
evidence/ kernel logs of the dynamic proof and the patched control
docs/REMOTE-DEPLOYMENT.md remote/firewall mode, calibration, safety
tests/ unit tests
NOTES.md running engineering log and measurements
uv sync
sudo .venv/bin/python lab/victim.py --port 9101 --host 0.0.0.0 &
uv run netprobe preconditions -t 172.18.0.3 -p 9101
sudo .venv/bin/python -m netprobe.cli race -t 172.18.0.3 -p 9101 \
--iface eth0 --spoof 203.0.113.7 --variant child -n 2000
对检测器本身的合成检查(无需存在漏洞的内核):