Linux Kernel IPv4 TCP 释放后重用漏洞(CVE-2026-98432)
原创
Kratos
Kratos
Kratos Sec
2026年11月1日 10:42
北京
去阅读
内核栈的满分洞又来了。10 月 15 日 CVE-2026-98432 公开:IPv4 TCP 握手路径的竞态条件引发释放后使用(UAF),内核 CVE 团队给了 9.8。这个洞有三个不太寻常的地方。第一,它足够老——引入点是 2016 年 Linux 4.4 的一个提交,也就是说十年间的内核全部带着这个缺陷;第二,它的病理极其典型——读数据的人规规矩矩拿着 RCU 读锁,释放数据的人却用裸 kfree(),合同签了一半;第三,把它变成武器的过程创了个纪录——Anthropic 在 10 月 27 日披露,是 Claude 开发出的完整利用:对 MPTCP 监听服务实现未认证远程命令执行,直接 root。补丁侧还有个坏消息:上游 10 月 5 日就修完了,但到发稿,Ubuntu 的修复仍标注\"进行中\",RHEL 没有发包。这篇按代码层把整条链拆开。
0x0 概述
| 项目 | 内容 |
|---|---|
| CVE 编号 | CVE-2026-98432 |
| 漏洞类型 | 竞态条件 + 释放后使用(UAF,CWE-416) |
| 位置 | net/ipv4 TCP 握手路径:ip_options_build() / inet_sock_destruct() |
| CVSS | 内核团队 9.8;Red Hat 公开口径 5.9、机器可读口径 8.1(分歧原因见 0x7) |
| 引入版本 | Linux 4.4(commit 079096f103fa,约 2016 年) |
| 修复 | 上游 2026-10-05 合入;stable 线 5.10.272 / 5.15.223 / 6.6.159 及更新分支 |
| 报告者 | Xinyang Ge(Anthropic) |
| 在野利用 | 截稿未报告;EPSS 0.388% |
| IPv6 | 主问题不受影响(选项是复制不是移交);CALIPSO 迁移路径另有独立补丁 |
0x1 事件时间线
- 2026-10-05:修复 commit 合入上游,标题一句话说完整件事——\"ipv4: Free inet\_opt and ireq\_opt after an RCU grace period\";
- 10-15:CVE 公开,stable 各线修复版本发布;
- 10-24:Amazon Linux 2023(6.18 内核)完成修复;
- 10-27:Anthropic 披露利用验证:Claude 独立开发出针对 MPTCP 监听服务的未认证远程命令执行,root 权限。技术路径是地址泄露 + 回调指针替换。利用代码和被测内核配置未公开;
- 10-29:Go 安全团队发通告——Go 1.24+ 在 Linux 上默认对监听 socket 启用 MPTCP,等于大量 Go 服务在管理员不知情的情况下踩在已演示的利用路径上;
- 10-30:oss-security 上讨论还在继续;Ubuntu 修复\"进行中\",RHEL 截稿无包。
一个值得玩味的顺序:上游补丁比 CVE 公开早十天落地,说明这次披露走得相当克制——可惜\"补丁先落地\"只帮得到自己编内核的人,发行版用户的时钟是从 CVE 公开那天才开始走的。
0x2 根因:一次不拷贝的所有权移交
TCP 服务端处理三次握手时,SYN 到达先建一个请求套接字(request socket,SYN_RECV 状态),收到最终 ACK 才创建正式的子套接字。IP 选项(struct ip_options,来自 SYN 包头)挂在这个请求套接字的 ireq->ireq_opt 上。
问题出在\"转正\"这一步。tcp_v4_syn_recv_sock() 把请求套接字转成子套接字时,对 IP 选项做的是指针移交,不是复制:
// net/ipv4/tcp_ipv4.c tcp_v4_syn_recv_sock()
newinet->inet_opt = ireq->ireq_opt; // 所有权移交,无拷贝
ireq->ireq_opt = NULL;
移交本身没 bug——合同是\"从此刻起只有子套接字拥有它\"。但在移交前后的窗口里,请求套接字那一侧还可能有人在使用这个结构,而十年前那个 commit 让窗口开了起来。
0x3 竞态:读的人等得起,释放的人等不起
2016 年合入的 079096f103fa(\"tcp/dccp: install syn\_recv requests into ehash table\")把请求套接字装进 TCP ehash 表,处理时不再持有监听套接字的锁。性能优化,代价是并发面变宽。竞态全景:
- 攻击者对一个监听端口发 SYN(带 IP 选项),内核建请求套接字,回 SYNACK;
- CPU A:攻击者故意不完成握手,而是重传同一个 SYN。内核在
tcp_check_req()里决定再发一次 SYNACK,tcp_v4_send_synack()/inet_csk_route_req()读取ireq->ireq_opt——注意,读侧只在rcu_read_lock()保护下读; - CPU B:攻击者随后补上正确的 ACK,握手完成,请求套接字\"转正\",
ireq_opt移交给子套接字。子套接字随即进入 ehash,对其他 CPU 可见; - 攻击者立刻关闭连接(RST 或握完手即 close),子套接字销毁,
inet_sock_destruct()对inet_opt执行\\裸kfree()\\——不等任何 RCU 宽限期; - \\CPU A 这才走到
ip_options_build()\\,解引用那个已经释放的指针,读opt->optlen(还读了两次)——UAF 成立。
病理一句话:读侧遵守 RCU 合同(读锁下访问),释放侧不遵守(普通 kfree)。内核里同类结构的其他替换路径(do_ip_setsockopt()、cipso_v4_sock_setattr())早就用了 kfree_rcu(),唯独析构这条路漏了。顺带一提,SYNACK 定时器路径本身是安全的——它持有请求套接字引用,且 inet_csk_reqsk_queue_drop() 会先 timer_delete_sync()。
第二个变体:请求套接字迁移(net.ipv4.tcp_migrate_req=1 或 BPF SK_REUSEPORT 迁移)。reqsk_timer_handler() 里 inet_reqsk_clone() 克隆请求套接字时新旧两个共享同一个 ireq_opt,reqsk_migrate_reset() 清掉旧指针的瞬间,另一颗 CPU 可能还在用——tcp_v4_reqsk_destructor() 同样是裸 kfree()。
0x4 从 UAF 到远程 root:AI 交的作业
竞态 UAF 到远程 root 之间隔着堆布局、信息泄露和控制流劫持三座山,这正是 Anthropic 披露里最有分量的部分:完成这段登山的是 Claude。披露的技术路径两步——先地址泄露,再回调指针替换,对 MPTCP 监听服务以未认证方式执行命令,拿到 root。利用代码、被测内核版本与配置全部未公开,这是负责任的披露姿势,但也意味着\"别人复现不了\"的窗口不会太长——原理已经公开,工程化只是时间问题。
两个实操层面的要点:
- MPTCP 是演示路径的前提,不是漏洞的边界。 底层竞态在所有 IPv4 TCP 监听场景都存在,MPTCP 的多子流机制给了攻击者更可控的握手编排。真正的坏消息是 Go:1.24+ 对 Linux 监听 socket 默认启用 MPTCP,大量 Go 服务从写第一行代码起就站在演示路径上,写代码的人未必知道。Go 团队的原话很不客气:按程序逐个关闭 MPTCP 的缓解方式是脆弱的,正确答案是打内核补丁;
- \\利用面是\"所有暴露 TCP 的 Linux\"\\——这几乎是全部 Linux 服务器。Web、数据库、K8s 节点、负载均衡,任何监听端口都在范围内,容器逃逸不了这个(内核是共享的)。
0x5 修复:把合同补完整
修复 commit 的思路直白:既然读侧在 RCU 下访问,释放侧就改成 kfree_rcu()——延迟到宽限期后、所有在途读者完成后再回收,两处:inet_sock_destruct()(子套接字路径)和 tcp_v4_reqsk_destructor()(迁移路径)。
IPv6 不在主问题射程内,原因很朴素:tcp_v6_syn_recv_sock() 对选项做的是复制,不是移交——同一份代码库里两种写法,一种活成了补丁的参照物。但 tcp_v6_reqsk_destructor() 在 CALIPSO(IPv6 的标签框架)迁移路径上有同款问题,官方拆成了独立补丁处理。
发行版修复状态(截稿 10-30,以各厂商 advisory 为准):Debian bookworm/trixie、Oracle UEK 7-10、SUSE、Amazon Linux 2023 已发;Ubuntu 标注\"进行中\",RHEL 尚未发包;Proxmox VE 在 7.0.14-25-pve 修复。用 Ubuntu/RHEL 的读者,现在能做的是把内核源锁定到厂商源并盯 advisory,不要自己动手编内核凑数。
0x6 自查与检测
受影响面判断三条(比想象中难):
- 别只看内核版本号。发行版 backport 不改大版本号,
uname -r一个数字回答不了\"修没修\";正确姿势是对照本发行版 advisory 里的包版本(如 Debian 的6.12.113-1),不是上游内核号; - 别把 MPTCP 当暴露面判断。
ss -M查到没有 MPTCP 监听不等于安全——那只是已演示路径的前提,底层竞态覆盖所有 IPv4 监听。MPTCP 检查(含 Go 1.24+ 服务)的意义是给风险排序,不是划边界; tcp_migrate_req=1或 BPF reuseport 迁移在用的(sysctl net.ipv4.tcp_migrate_req、负载均衡器配置),额外吃第二个变体,优先级再抬一档。
检测 exploiting 特征: 这个洞没有可靠的成品 IOC,能靠的是侧面证据——内核日志里 ip_options_build 相关的 BUG/KASAN/oops 记录、无业务解释的监听端口崩溃重启、握手异常流量模式(大量未完成三次握手的 SYN 重传组合快速 RST)。真在被打的场景里这些都不会干净,依赖主机上 EDR/内核审计的存在感;结论通常要靠内存取证兜底。
缓解的诚实说法:没有。 这是协议栈内核路径的洞,netfilter 挡不住完成握手的正常流量;Go 服务关 MPTCP 只是收窄已演示路径。唯一动作是换内核。
0x7 冷静一下:9.8 和 5.9 打起来了
同一洞,内核团队 9.8,Red Hat 公开口径 5.9(机器可读 8.1)。分歧在攻击复杂度(AC):内核团队的逻辑是竞态的两侧都能由攻击者诱导——SYN 重传的时机、ACK 的时机、关连接的时机全在攻击者手里,竞态窗口不是运气是编排,所以 AC:L;Red Hat 按传统竞态评估给了更保守的分。
我站内核团队这边的判断,理由是 Anthropic 的演示已经证明了\"AC 高\"的假设站不住——能稳定打穿的竞态就不是低成功率的竞态。但也要把另一半说全:\\截稿无在野利用报告,EPSS 0.388%\\,且利用对堆布局和内核配置有工程依赖,不是 nmap 一扫就通的场面。合理的姿态介于两派评分之间:按\"迟早武器化的未认证远程内核 RCE\"做升级排期,不必按\"正在爆发的蠕虫\"做应急。
0x8 复盘
三条值得留下:
- 生命周期合同要对账。 这个洞的教训不是\"又一个忘了加锁\",而是读侧和释放侧遵守着不同的内存生命周期合同——一边 RCU 一边裸 free。内核和任何长期演进的 C 代码库都该做这种\"同一结构、多条释放路径、各自何时等宽限期\"的审计,而不是等下一个析构函数被点名;
- 十年窗口是结构性的。 2016 年的性能优化埋雷,2026 年才响——中间每个\"没出事\"的年份都在给\"这个路径安全\"的错觉充值。内核栈漏洞的排查预算应该按代码年龄排,不是按 CVE 新鲜度排;
- AI 写利用这个里程碑比这个 CVE 本身重要。 Anthropic 的披露意味着\"从公开补丁 diff 到可用内核远程利用\"之间的工程鸿沟,已经窄到 AI 可以独自跨完。防守方过去依赖的那点缓冲——\"漏洞公开到武器化之间有时间\"——正在被压缩。以后每个 9.8 的 diff 公开之日,就该当作武器已存在的日期来排期。
参考
- https://www.cve.org/CVERecord?id=CVE-2026-98432
- https://github.com/advisories/ghsa-qp6h-4hvh-qqqc(镜像内核 commit 全文)
- https://nvd.nist.gov/vuln/detail/CVE-2026-98432
免责声明:本文仅用于安全研究与学习,未经授权不得用于任何非法渗透测试活动。
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。









评论