我们的 AI 引擎 Neutron 在加州大学伯克利分校的 CyberGym 基准测试中取得了 96.75% 的成绩。 了解更多

安全

安全

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 需要:

  1. 计算链式密文(将先前已施加于该页面的 STORE 考虑在内)。
  2. 暴力破解 K,使得 fcrypt_decrypt(C_actual, K) 产生所需的明文。
  3. 用 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

系统信息 —— Ubuntu 24.04.1,内核 6.17.0-23-generic
图 1:目标系统 —— Ubuntu 24.04.1,内核 6.17.0-23-generic(未打补丁)

漏洞利用

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

DirtyFrag PoC —— 已获得 root shell
图 2:无特权用户通过 DirtyFrag 获得 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