声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由用户承担全部法律及连带责任,文章作者不承担任何法律及连带责任。


摘要: 四大全新 Linux 本地提权漏洞;其中两个内存破坏漏洞在非常特定条件下可远程触发,还有一个理论上可通过纯远程内存布局实现远程提权。
研究背景
本文将比以往简短,原因有二:a) 时间紧迫;b) 我们一次性要介绍四个漏洞。
这些漏洞是通过结合 CIFSwitch 中使用的基于图的安全相关对象/属性追踪技术,以及 OVSwrap 中展示的让智能代理能够“几何化”思考内存状态的工具链发现的。更多细节请参考上述文章的背景章节。
漏洞研究框架的设计思路大致记载于 《让大语言模型畅饮以发现远程 Linux 内核越界写入(及其他)漏洞》,尽管之后已有了长足演变。
漏洞概览
这批漏洞分别是 DirtyAH6 (CVE-2026-80844)、TUNderflow (CVE-2026-81000)、PPPoEject (CVE-2026-68121) 和 DiagSpill (CVE-2026-74469)。
其底层漏洞已经存在了 10 至 21 年。前三个本地提权 (Local Privilege Escalation, LPE) 漏洞需要非特权用户命名空间*的权限;DiagSpill 则不需要。在极其特定的场景下,DirtyAH6 和 DiagSpill 的内存破坏漏洞可以被远程利用,详情见下文。
我于七月中旬向 security@kernel.org 报告了这些漏洞,修复补丁在过去的几周内陆续进入主线。根据与 linux-distros@ 邮件列表协调的禁运协议,我们同意在协调世界时 9 月 18 日早上 6 点发布技术分析和概念验证 (Proof of Concept, PoC),以便受影响的系统所有者可以优先打补丁。衷心感谢参与补丁修复/协调过程的众多维护者:Stefan Klassert、Xin Long、Paolo Abeni、Willem de Bruijn、Greg KH 以及许多其他人。
基础知识
简要介绍一下各个漏洞所涉及的内核子系统背景:
DirtyAH6:IPsec 协议族的认证头 (Authentication Header, AH) 用于检查数据包数据是否被篡改。Linux 在 AH6 模块中实现了其 IPv6 侧的功能,使用内核的 XFRM 代码;在计算或校验认证数据之前,AH6 会将一些 IPv6 字段(包括路由头中的地址)转换为预期形式。
TUNderflow:TUN 和 TAP 是虚拟网络设备,通过 /dev/net/tun 在内核和用户空间之间传递数据包。构建在其他设备之上的网络设备可以通过 ndo_set_rx_headroom() 将自身需要的接收头部空间向下传递,而 Open vSwitch 可以将该值从另一个端口传递到 TUN 或 TAP 端口。
PPPoEject:PPPoE 用以太网帧承载 PPP 会话。发送时,pppoe_sendmsg() 函数构建一个 skb,复制载荷数据,在填充 PPPoE 头部之前,要求下层网络设备创建其硬件头部。
DiagSpill:一个 SCTP 关联可以有多个对端传输器,每个对端地址对应一个。sctp_diag 通过 sock_diag 报告 SCTP 套接字和对端信息,在内核的网络链路回复中为每个传输器构建一个 sockaddr_storage 结构。
漏洞详情
DirtyAH6:ipv6_rearrange_rthdr() 函数从路由头长度 (hdrlen) 获取地址数量,然后使用公式 segments - segments_left 来移动地址指针,但没有先验证 segments_left <= segments。因此,一个 hdrlen=2 且 segments_left=255 的原始 IPv6 HDRINCL 数据包,会将指针向后移动 4064 字节,并向 memmove() 传递一个 4064 字节的长度,导致一次越界访问。
如果一个目标系统充当 IPv6 路由器/网关,并在传输模式下添加 AH 封装,同样的漏洞可以转化为远程崩溃/拒绝服务攻击;在目标系统上进行内存布局调整后,我在实验室中也能将其转化为远程提权。仅通过纯远程的内存布局调整实现提权看起来极其困难,但并非全无可能。
TUNderflow:tun_set_headroom() 函数直接将接收头部空间存储在 tun->align 中,而 tun_get_user() 函数也用它来决定在 skb 头中保留多少数据包数据。一个配置了 4096 字节头部空间的 netkit 设备,位于 VXLAN 设备和 Open vSwitch 数据路径之下时,可以向一个原始 TUN 端口传递 4160 字节的数据。随后,SKB_MAX_HEAD(4160) 计算会发生下溢。负的 good_linear 值变成了巨大的正 size_t 类型值;prepad + linear 和 len - linear 随之发生环绕,tun_alloc_skb() 函数最终使 skb->data 指针超出了其 4096 字节的分配范围 64 字节。之后的数据包处理过程便会读写 skb 头之外的内存。
PPPoEject:pppoe_sendmsg() 函数在调用 dev_hard_header() 期间,一直持有一个指向 skb 头部的指针。然而,设备回调函数可能调用 pskb_expand_head() 并释放那个头部。在向空的 team 或绑定设备添加第一个 GRE/IP6GRE 端口时,利用 FUSE 阻塞载荷复制操作,触发了重新分配,实质上弹出了旧的 skb 头,而 PPPoE 仍持有一个指向它的指针。后续的头部和长度写入操作就会使用这个悬垂指针。
DiagSpill:一个 SCTP 关联最多可以有 65536 个对端传输器,但 transport_count 是一个16 位整数——因此第 65536 个传输器会使其值回绕为 0。sctp_diag 随后不会为对端负载预留空间,却复制了整个列表,导致大约 8 MiB 的数据溢出到 Netlink 响应的末尾之外。
如果启用了 ASCONF/ADD-IP 并结合 SCTP-AUTH 或设置了 net.sctp.addip_noauth_enable=1(所有这些默认都是禁用的),这种内存破坏就可能转化为远程崩溃/DoS 攻击——恶意对端添加足够多的传输器,而目标系统上的某个程序(例如 ss 命令)发出了触发覆写操作的 sock_diag 请求。即使假设远程内存布局调整完全无懈可击,我也找不到通向彻底远程提权的路径。
补丁详情
DirtyAH6:在修改头部或地址指针之前检查 segments_left,并通过现有的 AH6 错误路径返回 -EINVAL:
segments = rthdr->hdrlen >> 1;
if (segments_left > segments)
return -EINVAL;
rthdr->segments_left = 0;
TUNderflow:限制 TUN 存储的头部空间不超过单页 skb 头的预算以及最大允许的 16 位头部偏移量,同时为原始 TUN 协议字节或完整的 TAP 以太网头部保留足够的空间。然后确保在使用这些字节前它们确实存在:
max_headroom = min_t(size_t, SKB_MAX_HEAD(0), U16_MAX - 1);
if ((tun->flags & TUN_TYPE_MASK) == IFF_TAP)
max_headroom -= ETH_HLEN + NET_IP_ALIGN;
else
max_headroom -= 1;
tun->align = clamp_t(int, new_hr, NET_SKB_PAD, max_headroom);
...
case IFF_TUN:
if (tun->flags & IFF_NO_PI) {
u8 ip_version;
if (!pskb_may_pull(skb, 1)) {
err = -EINVAL;
goto drop;
}
ip_version = skb->data[0] >> 4;
...
}
...
break;
case IFF_TAP:
if (!pskb_may_pull(skb, ETH_HLEN)) {
err = -ENOMEM;
drop_reason = SKB_DROP_REASON_HDR_TRUNC;
goto drop;
}
PPPoEject:在设备头部创建后,通过 skb 的网络头部偏移量重新加载 PPPoE 头部;让 pskb_expand_head() 在移动头部时更新该偏移量:
dev_hard_header(skb, dev, ETH_P_PPP_SES,
po->pppoe_pa.remote, NULL, total_len);
ph = pppoe_hdr(skb);
memcpy(ph, &hdr, sizeof(struct pppoe_hdr));
DiagSpill:一旦 transport_count == U16_MAX 就拒绝新的唯一对端。该检查发生在查找现有对端之后,因此在数量达到上限时,列表中已存在的地址仍能返回其现有的传输器:
if (asoc->peer.transport_count == U16_MAX)
return NULL;
peer = sctp_transport_new(asoc->base.net, addr, gfp);
漏洞利用剖析
这些全是需要针对每个目标进行内存布局调整才能提权的内存错误,因此链接的概念验证代码都指定了其调优针对的发行版/内核/CPU/内存配置。我已设法通过一些调优在多个发行版上重现了这些漏洞,但出于简化,我只发布了针对少数特定目标的最小化 PoC。
虽然这些利用在目标系统上通常可靠,但它们仍可能破坏错误的内存区域,因此请务必仅在专用虚拟机或一次性主机上运行。关于每个漏洞利用的具体机制、受影响版本完整表格、缓解措施等,建议进一步参考原文与 Linux 内核安全与漏洞分析相关资源,其中也涵盖 TCP/IP、内核与网络协议栈等背景知识。
原文:https://heyitsas.im/posts/lpe-quartet/
PoC:https://github.com/manizada/DirtyAH6
PoC:https://github.com/manizada/TUNderflow
PoC:https://github.com/manizada/PPPoEject
PoC:https://github.com/manizada/DiagSpill