SCTPhantom: 潜伏18年的Linux内核提权与容器逃逸漏洞
TL;DR: SCTPhantom 是 Linux 内核 SCTP 动态地址重配置功能中的一个释放后使用漏洞。一组按特定顺序排列的 ASCONF 操作可以移除某个 transport,随后继续使用它的陈旧指针,使 association 中留下悬空引用。
Corvus AI 将最初的发现推进为可稳定复现的漏洞,并在测试机上完成了本地提权与容器到宿主机逃逸。该漏洞编号为 CVE-2026-64564,上游修复提交为
9b2854f86f0b。
1. 背景
在 SCTP 这类协议代码中寻找漏洞,往往需要追踪复杂而密集的运行时状态,单轮分析很容易迷失其中。编码 Agent 可以承担大量机械工作,包括浏览庞大的内核源码树、反复修改概念验证程序、编译并启动内核、收集 sanitizer 报告或 panic 日志,再根据结果设计下一轮实验。
Coding Agent 已经能够覆盖大部分执行循环,但完整的内核漏洞研究还需要高于代码生成层面的研究决策,并且这些决策和证据需要跨任务、跨模型持续保存。
Corvus AI 由 TencentOS 安全团队(腾讯朱雀实验室)联合开发,将上述安全研究员都经历的过程,组织成一条持久运行的多 Agent 漏洞研究流水线。

图 1 — Corvus AI:操作系统漏洞研究流水线。
SCTPhantom 是这套工作流的一个具体案例。漏洞位于 Linux SCTP 动态地址重配置功能中,其根源是 DEL-IP 操作所校验的地址,与后续 ASCONF 处理所保留的 transport 并不一致。
文章的剩余部分将包括该漏洞的发现与验证过程、完整提权利用链的构造,以及最终的上游修复。
2. 漏洞
2.1 发现
从漏洞研究流程开始,Corvus AI 首先通过一份结构化的 SCTP 研究计划划定搜索范围,其中包括对端可控字段、ASCONF 参数顺序、多宿主状态和 transport 所有权。Corvus AI 随后将这些方向转化为边界明确的源码分析、数据包生成、崩溃归因和虚拟机复现任务。
将 IPv4 数据包的源地址与 ASCONF Address Parameter 指向的 transport 分开分析后,漏洞路径就变得清晰了。通过不断调整对端 transport 的状态,Corvus AI 在测试中最终稳定复现了后续 socket 操作访问已释放对象的情况,并在全新启动的虚拟机中得到确认。
2.2 SCTP 与 ASCONF
RFC 4960 定义的流控制传输协议(Stream Control Transmission Protocol,SCTP)是一种支持多宿主的面向消息传输协议。一个 association 可以包含多条对端路径。Linux 使用 struct sctp_association 表示 association,使用 struct sctp_transport 表示每条路径,并通过 asoc->peer.transport_addr_list 将这些 transport 串联起来。
与该漏洞相关的两个缓存指针如下:
struct sctp_association
peer.transport_addr_list -> [ transport A ] -> [ transport L ] -> [ transport C ]
peer.primary_path -------------------------------^
peer.active_path -------------------------------^按照设计,primary_path 和 active_path 应当指向归 association 所有且仍然存活的 transport。
RFC 5061 为 SCTP 增加了动态地址重配置功能。一个 ASCONF chunk 由 Address Parameter 以及随后的 ADD-IP、DEL-IP、SET-PRIMARY 等操作组成,Linux 按照消息中的先后顺序处理这些操作参数。
2.3 根因
触发漏洞的 ASCONF chunk 使用了两种不同的身份:IPv4 数据包源地址 S,以及用于选择 transport 的 Address Parameter L。DEL-IP 检查根据 S 校验待删除地址,后续处理依赖的却是通过 L 选中的 transport。
这使下面这组有序操作成为可能:
[ Address Parameter L ] [ DEL-IP L ] [ DEL-IP 0.0.0.0 ]由于 S 与 L 不同,DEL-IP L 可以通过源地址检查并移除 transport(L)。随后,通配符 DEL-IP 又把该 transport 的缓存指针作为需要保留的路径继续使用。结果是 association 可能把已经移除的 transport 留在 primary_path 和 active_path 中,后续 socket 操作因此会解引用陈旧指针。
上游修复消除了这一身份错配:当选中的 peer 是 ASCONF chunk 所保留的 transport 时,内核会拒绝删除操作。
3. 利用链
完整利用链如下:
surviving SCTP transport UAF
→ UAF #1 reclaimed by pg_vec
→ direct-map page disclosure
→ repeatable 4-byte kernel read
→ IDT-based KASLR recovery
→ UAF #2 reclaimed by controlled SCTP authentication-key data
→ controlled kernel object graph
→ data-oriented commit_creds()
→ global root
→ usermode-helper variant
→ container-to-host escapeCorvus AI 保留了每个阶段的证据和约束,使利用链能够逐步开发、逐步验证。
3.1 存活的 UAF
利用首先需要同时满足三个条件:
the transport has completed RCU release
AND
the association remains alive
AND
userspace can still reach the stale path删除 ACTIVE primary 可以保住 association,但定时器和引用会阻止 transport 被回收。删除 UNCONFIRMED secondary 可以让 transport 完成释放,但后续协议处理会销毁 association。最终,已经确认的 ACTIVE secondary 在两者之间提供了所需的平衡。
稳定的设置过程如下:
create a multihomed association
→ demand heartbeats until the target secondary becomes ACTIVE
→ disable heartbeats on the target and remaining paths
→ inject [Address target][DEL target][wildcard DEL]
→ allow ASCONF processing to return
→ wait for the RCU release
→ access the stale path through the surviving socket具体实现会黑洞处理 ASCONF 数据包使用的伪造源地址,避免 ASCONF-ACK 在已释放的槽位中分配新的 transport。在对可靠性敏感的阶段,CPU 亲和性还会让接收处理、RCU 回调和回收分配保持在同一条 per-CPU slab 路径上。
之后调用 getsockopt(SCTP_STATUS),即可通过仍然存活的 socket 访问已释放的 primary path。KASAN 确认这是一次 slab UAF,其分配栈和释放栈分别位于 sctp_transport_new() 与 RCU 回调中。
3.2 pg_vec 泄漏
在具有代表性的 6.6 目标内核上,struct sctp_transport 占用一个 kmalloc-1024 对象。包含 128 个 block 的 TPACKET V1 发送环会分配一个 1024 字节的 pg_vec 数组:
pg_vec allocation
+0x000 page pointer 0
+0x008 page pointer 1
+0x010 page pointer 2
+... ...
+0x3f8 page pointer 127当 pg_vec 回收到 transport 的槽位后,SCTP_STATUS 会把选定的页指针解释为 transport 字段。在这个内核构建中,返回的 srtt 和 cwnd 可以还原出一个 64 位内核页地址。
每个 pg_vec 表项还指向一个映射到攻击进程的页面,因此这次泄漏会给出同一物理页对应的两个虚拟地址:
userspace mapping <---- same page ----> kernel direct-map address利用程序可以通过用户空间映射写入伪造对象,再通过泄漏出的 direct-map 地址引用这些对象。之后使用的所有伪造对象都放在这个页面中,从而避开早期一个错误假设:独立分配的 TPACKET block 在物理内存中连续排列。
只有当返回值是规范地址、按页对齐,并且通过用户空间映射写入的数据能经由预期的内核指针被读取时,这次泄漏才会被接受。若回收未命中,利用程序会重新开始本轮 victim 尝试。
3.3 任意读
SCTP_STATUS 会报告选定 transport 所属 association 的标识符,相关操作等价于:
spinfo_assoc_id = sctp_assoc2id(transport->asoc);控制 transport->asoc 后,可以通过下列设置读取地址 T 处的目标 word:
transport->asoc = T - offsetof(struct sctp_association, assoc_id)返回值将变为:
SCTP_STATUS.assoc_id = *(u32 *)T由此可以获得稳定的 4 字节内核读。对周边解释字段写入受控值,可以确保状态查询路径继续有效。利用程序使用彼此独立的 victim association 执行读取,并拒绝回收标记、规范地址检查或已知值校验失败的尝试。
3.4 IDT KASLR
读取原语从 CPU entry area 中固定的只读 IDT 映射的 0 号 gate 恢复内核代码的 KASLR slide。UMIP 使测试内核上的用户空间 sidt 无法返回可用结果,因此利用程序改用内核读取原语访问该映射。
只需读取包含 gate 中间 offset 位的 32 位数据,再结合已知低位、规范高位和目标 slide 对齐,即可重建 asm_exc_divide_error 的运行时地址:
runtime_handler = reconstruct(IDT gate 0)
KASLR_slide = runtime_handler - link_time_asm_exc_divide_error在把 slide 应用于 commit_creds 等链接时符号之前,利用程序会先对其进行验证。
3.5 commit_creds
第二个存在漏洞的 association 用于放置一个内容完全受控、大小与 transport 相同的对象。在所分析的 6.6 内核构建中,msg_msg 分配使用 GFP_KERNEL_ACCOUNT,落入 kmalloc-cg-1024;SCTP transport 则位于普通的 kmalloc-1024。持久化 SCTP authentication-key 分配能够在匹配的 cache 中提供受控字节。
利用程序首先记录 association 和目标 transport 的地址。LIST_HARDENED 要求回收后 packet 的空 chunk_list 精确回指其 list head 地址,因此回收对象必须针对 transport 槽位的具体地址构造,而不能使用与位置无关的数据。
支撑利用的对象图可以完整放入 UAF #1 泄漏出的 direct-map 页面:
stale association active_path
|
v
[ reclaimed transport / embedded packet state ]
|
+---- packet->transport ------> [ fake transport ]
|
+---- asoc ------> [ fake association ]
| |
| +-- base.sk --> [ fake socket / cred ]
|
+---- af_specific -> [ fake SCTP address-family ops ]
|
+---- dst --------> NULL伪造的 address-family operations 为 get_dst 使用一个正常返回的函数,为 get_saddr 使用 commit_creds 的运行时地址。伪造 association 的 base.sk 指向受控页面中的一块区域,其中包含 SCTP 所需的 socket 字段和高权限 credential 字段。NULL destination 会在 credential 操作完成后停止 packet 构造。
执行最终触发前,SCTP_STATUS 必须从伪造 association 中返回 0x1337beef 之类的标记。这个结果可以确认预期的 authentication-key 分配已经回收到悬空 transport,并且受控对象图已经连通。
最终触发方式是 abortive close:
setsockopt(SO_LINGER, { on = 1, linger = 0 })
→ close(victim_socket)
→ SCTP ABORT generation
→ output-queue flush
→ sctp_transport_route()
→ controlled af_specific->get_saddr(fake_sk, ...)
→ commit_creds(controlled_cred)整条利用链复用现有内核代码,不需要用户空间 shellcode 或传统 ROP chain。对 global root 的验证并非只观察 UID 变化,而是比较触发前后对 /etc/shadow 的访问结果,并在 /root 下创建 root 所有的文件。

图 2. 在四个不同 Linux 发行版上成功完成本地提权
3.6 容器逃逸
随后,我们使用 Corvus AI 评估该 SCTP 漏洞在容器环境中的影响。完整的容器利用链从容器内执行 exploit 命令开始,在宿主机初始 namespace 中执行操作结束。
早期 PoC 会启用 net.sctp.addip_enable 和 net.sctp.addip_noauth_enable,因此一度让人以为必须具备 CAP_NET_ADMIN。Corvus AI 随后找到了一条无需修改这两个 sysctl 的路径:通过 SCTP_ASCONF_SUPPORTED 和 SCTP_AUTH_SUPPORTED 在单个 socket 上启用相关功能,并让触发数据包携带有效的 AUTH chunk。
前几个阶段与本地提权链相同:
container SCTP trigger
→ UAF in the shared host kernel
→ pg_vec direct-map disclosure
→ 4-byte kernel read
→ IDT-based KASLR recovery
→ controlled kernel object graph最后一个受控回调会调用 call_usermodehelper_exec(),其 subprocess_info 结构存放在泄漏出的内核映射页面中。内核 helper 在初始 namespace 中运行,并执行宿主机操作:
controlled callback
→ call_usermodehelper_exec()
→ process in the initial namespaces
→ host-root filesystem operation
图 3. 容器逃逸。
保留的测试继续使用默认 seccomp profile,也没有为容器提供 CAP_NET_ADMIN 或 CAP_SYS_ADMIN。八次尝试中有六次获得宿主机 root;另外两次以干净的 pointer-walk miss 结束,没有引发内核 panic。其他环境中的暴露情况还会受到 SCTP 可用性、raw socket 与 packet socket 访问权限、user namespace 策略、seccomp、capability 和 LSM 策略影响。
3.7 验证
我们在多个内核和发行版目标上以不同深度验证了该利用程序。
目标 | 内核 | 结果 |
本地编译内核 | Linux 7.2-rc2 | Root |
OpenCloudOS 系目标 | 6.6.119 | Root |
Debian 13 | 6.12.95+deb13-amd64 | Root |
Rocky Linux 9 / RHEL 9 | 5.14 厂商内核 | Root(SCTP 模块已加载) |
Ubuntu 24.04 | 6.8.0-134-generic | Root |
这些验证结果也为影响评估提供了依据。按照 CVSS v4.0 基础指标计算,该漏洞评分如下:
CVSS v4.0 Base Score (CVSS-B): 8.5 (High)
Vector: CVSS:4.0/AV:L/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
4. 上游修复与时间线
4.1 上游修复

上游补丁会拒绝删除 ASCONF chunk 所保留的 transport:
peer = sctp_assoc_lookup_paddr(asoc, &addr);
if (!peer)
return SCTP_ERROR_DNS_FAILED;
+ if (peer == asconf->transport)
+ return SCTP_ERROR_REQ_REFUSED;
+
sctp_assoc_rm_peer(asoc, peer);这项比较直接保护后续通配符分支还要使用的对象,而不是只保护数据包源地址对应的身份。修复修改了 net/sctp/sm_make_chunk.c。主线提交为 9b2854f86f0b;Linux 2.6.25 的提交 42e30bf3463c 补全了最终构成漏洞的代码序列。
4.2 修复版本
Linux kernel CVE 公告列出的首个修复版本如下:
内核分支 | 首个修复版本 | 稳定版修复提交 |
6.6.y | 6.6.148 | |
6.12.y | 6.12.101 | |
6.18.y | 6.18.42 | |
7.1.y | 7.1.6 | |
主线 | 7.2-rc5 |
厂商内核可能在保留较旧基础版本号的同时回移该补丁。因此,单凭版本字符串无法判断是否受影响,还应检查厂商公告或源码包中的修复状态。
4.3 时间线
下列研究日期均为中国标准时间(UTC+8)。
日期 | 里程碑 |
2007-12 / Linux 2.6.25 | 提交 42e30bf3463c 引入了通配符处理逻辑,补全了最终构成漏洞的代码序列 |
2026-07-12 | 发现 ASCONF transport 生命周期违规,并将 soft-lockup 推进为全新启动环境中的 post-RCU crash |
2026-07-12 | 携带可读 PoC、控制台证据和已测试补丁开始私下披露 |
2026-07-15 | 完成 association 保持存活的 transport UAF,以及首条稳定的 global-root 利用链 |
2026-07-15–23 | 在多个基于 5.14、6.6、6.8 和 6.12 的目标上评估 exploit 与 trigger |
2026-07-24 | 修复进入 Linux networking tree |
2026-07-27 | 完成容器到宿主机逃逸验证 |
2026-08-04 | Linux kernel CVE team 公布 CVE-2026-64564 |
5. 结论
这一案例体现了持续、以证据为驱动的研究流程所具有的价值。Corvus AI 将一项协议研究计划转化为边界清晰的实验,保留了未能成功的尝试及其结果,并以同一条证据链贯穿源码分析、全新启动环境下的复现、漏洞利用以及上游修复等阶段。
这套工作流程可以为研究类似的状态机与对象生命周期漏洞提供一套可复用的基础方法:明确记录各项假设,保留约束条件和负面结果,并将可复现的验证过程贯穿漏洞披露与修复始终。我们期待这些实践能够帮助研究人员更加一致、严谨地评估复杂的内核漏洞,并将早期发现的线索转化为可供独立复现、进一步拓展和实际处置的可靠证据。
参考资料
- CVE-2026-64564 记录
- Linux kernel CVE 公告
- 主线 networking-tree 修复:
9b2854f86f0b - 引入漏洞的提交:
42e30bf3463c - RFC 4960:流控制传输协议
- RFC 5061:SCTP 动态地址重配置
腾讯朱雀实验室
作者
