一个自包含、非特权权限的 CVE-2026-74586 PoC,该漏洞是 Linux 内核 SCTP ASCONF(动态地址重配置)处理中的释放后使用(use-after-free)问题。漏洞本身的发现归功于 Qing Ming,由其上报至上游;修复提交为 beb33f8ee1ca(“sctp: clear new_transport when removing a peer”),于 2026-08-12 合并,并带有 Cc: stable —— Red Hat 将其评为重要(important),第三方追踪机构给出的 CVSS 评分为 9.8。
本仓库在公告之外额外提供:
sctp_process_asconf_param() 将 ADD-IP 参数所创建的 transport 存入 asoc->new_transport。同一 ASCONF 块中针对同一地址的 DEL-IP 会通过 sctp_assoc_rm_peer() 释放该 transport,而该函数(在修复前)不会清除 new_transport。当 sctp_process_asconf() 返回后,sctp_sf_do_asconf() 会操作这个悬空指针 —— 在原版内核上,可见效果是向刚被释放的 transport 的地址发出一个 HEARTBEAT;在 KASAN 下则是一份 slab-use-after-free 报告。
一个 ASCONF 块就足够了:
[Address Parameter L] [ADD-IP G] [DEL-IP G]
gcc -O2 -Wall -o poc poc.c
./poc
没有 root 权限。该 PoC 会 unshare 一个用户 + 网络命名空间,在其中设置 SCTP sysctl,通过 loopback 建立一个多宿主关联,然后 使用原始套接字注入构造好的 ASCONF。
在存在漏洞的内核上的预期输出:
[+] Association established (no AUTH)
[+] server_vtag=0x... captured_tsn=0x...
[*] serial=0x... (= initial_tsn)
[+] Injected 60 bytes
[9222->9111 vt=...] ASCONF-ACK(0x80) len=8
[+] ASCONF-ACK -> server processed ADD-IP + DEL-IP
[9222->9111 vt=...] HB(0x04) len=60 <- kernel using freed transport
[9111->9222 vt=...] ABORT(0x06) len=8
[+] GhostTransport UAF TRIGGERED (ASCONF-ACK received)
[+] HEARTBEAT to ghost address observed (dangling transport USED)
ACK 之后的 HEARTBEAT 才是有意思的部分:这个数据包之所以存在,仅仅是因为内核遍历了悬空的 new_transport。客户端紧接着就发送 ABORT,因为心跳是凭空冒出来的。
这些都不是什么新颖的研究,只是文档记录得不够,足以让人花掉一个下午,所以写在这里留给下一个人:
sctp_rcv() 会校验校验和,不匹配就丢弃。需要在整个 SCTP 数据包上计算 CRC32C(多项式 0x82F63B78,反射),并将校验和字段置零。sctp_auth_recv_cid() 在输入路径中、状态机之前运行,即使 addip_noauth_enable=1 也会丢弃未认证的 ASCONF——该 sysctl 只放宽了 sctp_sf_do_asconf() 内部的检查。出路是一开始就不在套接字层面协商 AUTH(不设置 SCTP_AUTH_SUPPORTED setsockopt),这样 peer.auth_capable 保持为 false,两道检查都能通过。上游提交上的 Fixes: 标签是 6af29ccc223b(“sctp: Bundle HEARTBEAT into ASCONF_ACK”),所以这段代码很老。已在主线修复并回移到稳定系列(Debian tracker:sid 中从 7.1.9-1 起修复,trixie-security 中为 6.12.107)。截至 2026-09-09,Kali rolling 在所有套件中都提供 7.1.5-1kali1,早于所有这些修复——这就是这个 PoC 的主要实际意义。
给想进一步推进的人——以 QEMU 中的 7.1.5 为基准,偏移量来自随附 sctp.ko 的 objdump -d:
| 字段 | 偏移 |
|---|---|
| flowi(88 字节) | 0x30 |
| ipaddr | 0x88 |
| af_specific | 0xa8 |
| asoc | 0xb0 |
| dst | 0xe0 |
| state | 0x15c |
| rcu | 0x2b0 |
sctp_association->new_transport 位于 0x6b0。该表来自运行内核上的 pahole -C sctp_transport /sys/kernel/btf/vmlinux——此表的早期版本有一个错误的 ipaddr 偏移(0x68,源于 flowi 计算错误);BTF 才是基准真相。
sctp_transport_destroy_rcu),这条路径上不存在双重释放。如果你原本指望关闭一个套接字就能得到两次释放——sctp_association_free() 只会遍历 transport 链表,而 rm_peer() 早已把那个幽灵对象从链表中摘除。别在这上面耗一晚上。sctp_outq_select_transport() 在 chunk 的 +0xe8 处读取 chunk->transport,再从该 transport 的 +0x15c 处读取 state。一个被回收后喷洒成 state=1(ACTIVE)的对象会在释放之后改变内核行为——说明该对象仍被访问。sctp_outq_select_transport() 在注入时刻通过陈旧的 transport 指针读到 state=0xFFFF(slab 垃圾数据)。结论:喷洒需要在触发之前预先 groom 缓存,而不是之后——在这条路径上,宽限期尚未到期时该对象就已经被读取了。*(p+0x288) 与 p+0x288 比较。这是一个自指针。不知道 slab 地址就无法靠喷洒绕过它,也就是说,这个 bug 需要先有一个信息泄露,才能把 af_specific 控制转化为指令指针控制。af_specific 本身就是一个间接调用点(*(af_specific+0x18),rdi = transport),所以一旦有了泄露,那就是战利品。setsockopt(SCTP_AUTH_KEY),会落入同一个 kmalloc-1k 缓存。记住头部是 struct sctp_authkey { assoc_id; keynumber; keylength; key[] },并且 AUTH 需要先通过 SCTP_AUTH_SUPPORTED 配合 sctp_assoc_value 按套接字启用——传一个普通 int 会得到 EINVAL。这是一个触发/崩溃 PoC。它不是提权,本仓库也不声称它是。请在虚拟机中针对你自己的内核运行它。
测试于:7.1.5+kali-amd64(Kali rolling)和 7.0.12+kali-amd64。
逻辑中源自内核的部分采用 GPL-2.0;其余部分随你处置。仅限授权的安全测试与研究使用。
acklock.c —— ASCONF-ACK 软锁死(在所有 7.1.x 中均未修复)同一代码路径中的第二个未修补内核 bug,在 7.1.5 上演示:sctp_verify_asconf() 接受一个没有最小长度限制的 ERR_CAUSE TLV,并按 PAD4 对齐遍历参数,而 sctp_get_asconf_response()(ASCONF-ACK 的消费方)按原始长度前进。一个精心构造的 ASCONF-ACK,其 ERR_CAUSE TLV 长度为 5,会使消费方错位 3 字节;接下来的 4 个字节随后被读作一个 length = 0 的参数头,而 asconf_ack_len -= 0 会永远自旋——在软中断中形成无限循环(ksoftirqd 占用 100%,CPU 被饿死,在 7.1.5 上的 3/3 次实验室运行中均由 clocksource-watchdog 超时确认)。
主线已由 2cb0b0b1ed69 修复(“sctp: fix soft lockup from unpadded ASCONF-ACK parameter iteration”,2026-08-30)——在 HEAD(7.1.13)的 linux-7.1.y 中缺失,因此每一个已发布的 7.1.x 内核,包括 Kali 最新的内核,都受影响。远程可利用,需要已建立的关联以及 net.sctp.addip_noauth_enable=1(或同一窗口内的 AUTH 绕过)。构建和运行方式同 poc.c:
gcc -O2 -Wall -o acklock acklock.c && ./acklock
观察虚拟机:在 poison ACK 后几秒内 CPU 就会卡死
(clocksource watchdog 超时;在使用启用了 perf-NMI-watchdog 的内核时,
你会在 sctp_get_asconf_response 中看到经典的 BUG: soft lockup 报错)。