一、漏洞概述
2026 年 5 月 7 日,安全研究员 Hyunwoo Kim(@v4bel)公开披露了 Dirty Frag——一个影响几乎所有主流 Linux 发行版的本地提权(LPE)漏洞链。Dirty Frag 通过链式组合两个独立的内核页缓存写入漏洞,实现了确定性的 root 权限获取。该漏洞无需竞态条件、不会触发内核 panic、成功率极高。
该漏洞链由两个 CVE 组成:
| CVE | 影响子系统 | 类型 | 写入原语 |
|---|---|---|---|
| CVE-2026-43284 | xfrm/ESP(IPsec) | 页缓存任意 4 字节写入 | seq_hi 直接控制写入值 |
| CVE-2026-43500 | RxRPC | 页缓存任意 8 字节写入 | fcrypt_decrypt(C, K) 暴力搜索 |
关键数据:
-
CVSS 评分: 7.8(高危)
-
影响范围: ESP 变体 Linux 4.10 ~ 7.0(引入 commit
cac2661c53f3,2017-01);RxRPC 变体 Linux 6.4 ~ 7.0(引入 commit2dc334f1a63a,2023-06) -
有效生命周期: ESP 变体约 9 年
-
在野利用: 微软确认已观测到主动攻击,用于后渗透阶段的权限提升
-
公开 PoC:
已确认受影响的发行版:
-
Ubuntu 24.04.4(6.17.0-23-generic)
-
RHEL 10.1(6.12.0-124.49.1.el10_1.x86_64)
-
openSUSE Tumbleweed(7.0.2-1-default)
-
CentOS Stream 10(6.12.0-224.el10.x86_64)
-
AlmaLinux 10(6.12.0-124.52.3.el10_1.x86_64)
-
Fedora 44(6.19.14-300.fc44.x86_64)
二、前置知识
2.1 Linux 页缓存(Page Cache)
Linux 内核通过页缓存机制缓存磁盘文件内容。当进程读取文件时,内核优先从页缓存中获取数据,避免磁盘 I/O。页缓存以页(4KB)为单位、以 inode 为键进行管理。
关键特性:
-
跨进程共享: 同一 inode 的页缓存被所有进程共享。进程 A 对页缓存的修改,进程 B 可立即可见
-
延迟回写: 修改不会立即写回磁盘,停留在内存中
-
Dirty Frag 的攻击面: 如果攻击者能在内存中篡改页缓存,所有后续读取该文件的进程都会看到被篡改的版本,但磁盘上的原始文件不变
2.2 struct sk_buff 与 frag 机制
sk_buff(socket buffer)是 Linux 网络子系统的核心数据结构,用于在各协议层之间传递网络数据包。
当数据包较大时,sk_buff 使用非线性(nonlinear)模式存储数据——数据存放在 frags 数组中,每个 skb_frag 描述一个内存页片段:
1 | struct sk_buff { |
关键点: 当通过 splice() 发送数据时,MSG_SPLICE_PAGES 标志使得内核将页缓存页原样引用(零拷贝)放入 frags 槽位,而不是复制数据。这意味着 frags 中的 page 指针直接指向页缓存中的物理页。
2.3 XFRM/ESP 子系统
XFRM(Transform)是 Linux 内核的 IPsec 框架,ESP(Encapsulating Security Payload)是其加密协议。内核在处理 ESP 数据包时,需要对载荷进行 AEAD(Authenticated Encryption with Associated Data)解密。
关键路径:udp_rcv → xfrm4_udp_encap_rcv → xfrm_input → esp_input
2.4 RxRPC 子系统
RxRPC 是内核实现的远程过程调用协议,用于 AFS(Andrew File System)等场景。在 RXRPC_SECURITY_AUTH 安全级别下,RxRPC 使用 rxkad 安全类对数据包进行校验和解密。
关键路径:recvmsg → rxrpc_recvmsg → rxrpc_verify_data → rxkad_verify_packet → rxkad_verify_packet_1
2.5 漏洞家族谱系
Dirty Frag 属于与 Dirty Pipe(CVE-2022-0847)和 Copy Fail(CVE-2026-31431)同源的漏洞类。它们的共同特征是:通过 splice 零拷贝机制将页缓存页引入内核数据处理路径,然后在该路径上对共享页进行原地(in-place)写操作,从而篡改页缓存内容。
| 漏洞 | 污染目标 | 写入原语 | 写入位置 |
|---|---|---|---|
| Dirty Pipe | struct pipe_buffer |
任意字节 | pipe 页 |
| Copy Fail | AF_ALG TX/RX SGL | 4 字节 scratch write | AEAD 目标页 |
| Dirty Frag (ESP) | sk_buff frags |
4 字节 seq_hi STORE | ESP 解密目标页 |
| Dirty Frag (RxRPC) | sk_buff frags |
8 字节 fcrypt_decrypt | RxRPC 解密目标页 |
三、漏洞根因深度分析
Dirty Frag 的核心问题在于:当 sk_buff 的 frags 中包含通过 splice 引入的共享页缓存页时,内核代码路径上的原地解密操作会直接覆写这些共享页——即使认证失败,写入已经发生且不可撤销。
3.1 CVE-2026-43284:xfrm-ESP 页缓存写入
3.1.1 根因:跳过 COW 的快速路径
esp_input() 在执行 AEAD 解密前,应通过 skb_cow_data() 分配私有缓冲区并复制数据。但以下分支创建了绕过 COW 的路径:
1 | // net/ipv4/esp4.c - esp_input() |
在 [2] 处,即使 skb 有 frag(包含页缓存页),只要没有 frag_list,代码就直接跳过 COW,在共享页上执行原地解密。
3.1.2 4 字节 STORE 原语
当使用 ESP + ESN + authencesn(hmac(sha256), cbc(aes)) 组合时,crypto_authenc_esn_decrypt() 在预处理阶段将序列号的高 4 字节移动到 src SGL 末尾:
1 | // crypto/authencesn.c |
[3] 处的 4 字节 STORE 发生在 dst SGL 的 assoclen + cryptlen 位置。如果攻击者通过 splice 将页缓存页 P 放置在该位置,这 4 字节就被写入页 P 的精确偏移处。
3.1.3 攻击者控制写入值
这 4 字节的值来自 ESP 头中的序列号高 32 位(seq_hi),而 seq_hi 由 SA 的 XFRMA_REPLAY_ESN_VAL netlink 属性指定——攻击者在注册 SA 时自由设定:
1 | // SA 注册时设置 seq_hi |
因此攻击者完全控制:
-
写入位置: 通过 splice 偏移控制文件内偏移
-
写入值: 通过 SA 的
seq_hi控制写入的 4 字节内容
关键: AEAD 认证验证在 STORE 之后运行。即使认证失败(返回 -EBADMSG),STORE 已经完成且不可撤销。
3.1.4 触发条件
注册 XFRM SA 需要 CAP_NET_ADMIN,因此攻击者需要通过 unshare(CLONE_NEWUSER | CLONE_NEWNET) 创建用户/网络命名空间来获取该权限。在某些发行版(如 Ubuntu 的 AppArmor 策略)可能阻止非特权用户命名空间创建。
3.2 CVE-2026-43500:RxRPC 页缓存写入
3.2.1 根因:原地解密共享 frag
rxkad_verify_packet_1() 对 RxRPC 数据包的前 8 字节执行 pcbc(fcrypt) 原地解密:
1 | // net/rxrpc/rxkad.c |
在 [4] 处 src == dst(原地操作),skb_to_sgvec() 将 skb 的 frag 直接转换为 SGL——攻击者通过 splice 植入的页缓存页 P 直接成为解密的目标。[5] 处的 8 字节 STORE 直接写入页 P。
3.2.2 间接控制写入值
不同于 ESP 变体直接控制写入值,RxRPC 变体的写入值是 fcrypt_decrypt(C, K) 的结果——使用攻击者的密钥 K 对密文 C 做一次 fcrypt 解密。
fcrypt 是 Andrew File System 专用密码,56 位密钥、8 字节分组。攻击者通过 add_key("rxrpc", ...) 注册 RxRPC v1 令牌时自由设置 K,无需任何特权。
攻击者在用户态暴力搜索 K,使得 fcrypt_decrypt(C, K) 产生期望的明文。由于 IV=0 且单分组,pcbc_decrypt 退化为单次 fcrypt_decrypt,可以在用户态完整模拟。
3.2.3 无需用户命名空间权限
与 ESP 变体不同,RxRPC 变体不需要创建用户命名空间的权限。add_key()、socket(AF_RXRPC)、socket(AF_ALG)、splice()、recvmsg() 都是普通用户可用的 API。
但 RxRPC 变体的限制是:rxrpc.ko 模块在大多数发行版中默认不加载,而 Ubuntu 是一个例外——它默认加载了 rxrpc.ko。
四、漏洞复现
以下复现基于 Kali Linux 6.19.11-amd64,用户需自行在受影响版本的测试环境中操作。
4.1 环境检查
1 | uname -r |
4.2 创建测试用户
1 | useradd -m -s /bin/bash testuser |
4.3 编译并运行 PoC
1 | git clone https://github.com/V4bel/dirtyfrag.git |
PoC 内部执行流程:
-
ESP 变体(子进程):
unshare(CLONE_NEWUSER | CLONE_NEWNET)创建用户/网络命名空间 → 注册 48 个 XFRM SA(每个 SA 的seq_hi携带一段 4 字节 shellcode)→ 循环 splice 触发 48 次 4 字节页缓存写入 → 覆写/usr/bin/su页缓存的前 192 字节为静态 root shell ELF -
检查 ESP 是否成功: 读取
/usr/bin/su入口偏移处是否已被植入 shellcode 首字节 -
ESP 成功路径: 父进程
forkpty + execve("/usr/bin/su")→ shellcode 执行setuid(0); setgid(0); setgroups(0, NULL); execve("/bin/sh")→ root shell -
ESP 失败 → 回退 RxRPC 变体: 在用户态暴力搜索密钥 K_A/K_B/K_C → 三次 splice 触发 RxRPC 原地解密 → 覆写
/etc/passwd第 1 行root:x:为root::(清空密码字段)→su通过 PAMpam_unix.so nullok无密码认证 → root shell
4.4 验证提权
五、内核源码级分析
5.1 ESP 变体攻击数据流
攻击者构造的 skb 通过环回发送后的结构:
1 | skb { |
内核接收端调用链:
1 | udp_rcv(skb) |
5.2 RxRPC 变体攻击数据流
伪造的 DATA 包结构:
1 | skb { |
内核接收端调用链:
1 | recvmsg(rxsk_cli) |
5.3 ESP 变体 exploit 细节
目标文件: /usr/bin/su
前 192 字节被替换为一个静态 root shell ELF,该 ELF:
-
通过 PT_LOAD 在
0x400000处映射0xb8字节(R+X) -
入口点
0x400078(文件偏移0x78)执行:setgid(0) → setuid(0) → setgroups(0, NULL) → execve("/bin/sh", NULL, envp) -
完全绕过 PAM 认证流程
-
192 字节 = 48 个 4 字节块,通过 48 次 4 字节 STORE 组装
每次触发:
1 | uint8_t hdr[24]; |
splice_to_socket() 自动设置 MSG_SPLICE_PAGES,使得 /usr/bin/su 的页缓存页原样进入 frag[0]。
5.4 RxRPC 变体 exploit 细节
目标文件: /etc/passwd 第 1 行
原始内容:root:x:0:0:root:/root:/bin/bash
利用三次 8 字节写入(last-write-wins)将第 4~15 字符改为 ::0:0:GGGGGG::
1 | 偏移: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ... |
passwd 字段变为空字符串,PAM 的 pam_unix.so nullok 配置接受空密码,无需提示即返回 PAM_SUCCESS。
暴力搜索密钥 K 的过程:
1 | find_K(Ca, check_pa, &Ka, &Pa); // 搜索 K_A,~5ms |
关键是:每次 splice 写入会改变后续 splice 看到的密文,因此密文链必须级联修正。
5.5 链式组合策略
两个变体互补对方的盲区:
| 条件 | ESP 变体 | RxRPC 变体 |
|---|---|---|
| 需要用户命名空间权限 | 是 | 否 |
esp4/esp6 模块普遍存在 |
是 | - |
rxrpc.ko 模块普遍存在 |
- | 否(仅 Ubuntu 默认加载) |
| 写入值直接可控 | 是 | 否(需暴力搜索) |
| AppArmor 阻止 unshare 时可用 | 否 | 是 |
链式 exploit 流程:
1 | 1. 尝试 ESP 变体(子进程): |
5.6 与 Copy Fail 的关系
Copy Fail(CVE-2026-31431)通过 splice(file → pipe → AF_ALG_fd) 路径利用页缓存。Dirty Frag 的 ESP 变体与 Copy Fail 共享同一个 sink(crypto_authenc_esn_decrypt 中的 4 字节 scratch write),但触发路径完全不同:
-
Copy Fail 依赖
algif_aead模块 → 可以通过黑名单algif_aead缓解 -
Dirty Frag 通过
esp4/esp6和rxrpc模块触发 → 即使已应用 Copy Fail 的algif_aead黑名单缓解,系统仍然受 Dirty Frag 影响
六、补丁分析
6.1 ESP 变体补丁(commit f4c50a403)
补丁采用 shared-frag 标记方案:
1) 在 IPv4/IPv6 数据报发送路径中标记共享 frag:
1 | // net/ipv4/ip_output.c - __ip_append_data() |
当通过 splice 引入的页缓存页进入 skb frags 时,设置 SKBFL_SHARED_FRAG 标志。
2) 在 ESP 输入快速路径中检查该标志:
1 | // net/ipv4/esp4.c - esp_input() |
当检测到 skb 包含共享 frag 时,不再跳过 COW,强制走 skb_cow_data() 路径分配私有副本。
esp6.c 做了相同修改。
6.2 RxRPC 变体补丁
RxRPC 变体补丁已由上游合入,包含在内核 6.6.138、6.12.87、6.18.28、7.0.5 及之后版本中。研究员提交的补丁方案是在原地解密前增加非线性 skb 检查:
1 | // net/rxrpc/call_event.c |
1 | // net/rxrpc/conn_event.c |
通过增加 skb->data_len 检查(非零表示有非线性数据),确保非线性 skb 也被 skb_copy() 隔离,不直接在共享 frag 上执行原地解密。
七、检测与防御
7.1 临时缓解(补丁不可用时的应急措施)
黑名单受影响的内核模块并清理页缓存:
1 | sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf; rmmod esp4 esp6 rxrpc 2>/dev/null; echo 3 > /proc/sys/vm/drop_caches; true" |
注意: 卸载
esp4/esp6会中断 IPsec VPN 功能,卸载rxrpc会中断 AFS 文件系统功能。
7.2 限制用户命名空间
对于 ESP 变体,可以通过限制非特权用户创建用户命名空间来增加攻击门槛:
1 | sysctl -w kernel.unprivileged_userns_clone=0 |
注意: 这只影响 ESP 变体,RxRPC 变体不需要用户命名空间权限。且 Ubuntu 默认已通过 AppArmor 部分限制了此能力。
7.3 内核升级
各发行版已发布补丁内核,尽快升级:
已修复的上游稳定版:
| 版本线 | 首个修复版本 |
|---|---|
| 6.6.x | 6.6.138 |
| 6.12.x | 6.12.87 |
| 6.18.x | 6.18.28 |
| 7.0.x | 7.0.5 |
各发行版:
-
Ubuntu:
apt update && apt upgrade获取更新内核 -
Debian:已发布 6.12.86-1 修复版本
-
RHEL/CentOS/AlmaLinux:已发布各版本线修复
-
源码编译:应用 commit
f4c50a4034e62ab75f1d5cdd191dd5f9c77fdff4(ESP)及 RxRPC 相关补丁
7.4 检测规则
可以通过以下方式检测利用行为:
auditd 规则: 监控 unshare 系统调用和 add_key("rxrpc") 的异常使用
内核模块加载监控: 检测 rxrpc.ko 被非预期加载(通过 socket(AF_RXRPC) 会触发自动加载)
页缓存完整性: 对关键文件(/usr/bin/su、/etc/passwd)做内存哈希校验,检测页缓存与磁盘内容不一致
