Dirty Frag:Linux 内核又一页缓存篡改漏洞链,无需特权即可提权 root
又一个 Dirty Pipe 级别的内核漏洞来了,而且这次连特权都不需要。
0. 这事儿有多严重?
2026 年 5 月 7 日,韩国安全研究员 Hyunwoo Kim(@v4bel)公开披露了 Linux 内核本地权限提升漏洞链 "Dirty Frag"。
如果你对 Dirty Pipe(CVE-2022-0847)和 Copy Fail(CVE-2026-31431)还有印象,Dirty Frag 跟它们是同一个漏洞类——页缓存篡改。但这次命中的是完全不同的内核子系统,Copy Fail 的缓解措施对它毫无作用。
更狠的是,Dirty Frag 由两个独立漏洞串联而成:
| | |
|---|
| | |
| RxRPC 页缓存写入 | 8 字节 STORE | 不需要任何特权 |
没错,RxRPC 子漏洞连特权都不需要。普通用户一条命令就能触发。
截至本文写作时(2026 年 5 月 8 日):
- ✅ PoC 及完整 Exploit 已公开(https://github.com/V4bel/dirtyfrag)
1. 先说结论:该慌吗?
先别慌,但要重视。
这是本地权限提升(LPE),不是远程代码执行(RCE)。攻击者必须已经拿到目标系统上的普通用户权限才能利用。在管理规范的生产环境中,普通用户拿到服务器登录权限本身就不容易。
但以下场景需要重点关注:
- 🔴 允许 SSH 登录的服务器(用户账号被攻破的链路)
- 🔴 Web 应用服务器(RCE 漏洞 → Webshell → 提权)
2. 与 Copy Fail 的对比:缓解措施无效
Copy Fail(CVE-2026-31431)命中的是 algif_aead(crypto 子系统),很多同学已经通过黑名单 algif_aead 模块做了缓解。
但 Dirty Frag 不依赖 algif_aead,它命中的是 esp4/esp6(xfrm 子系统)和 rxrpc。
关键结论:即使已经应用了 Copy Fail 的 algif_aead 黑名单,Dirty Frag 仍然可以打。
3. 漏洞原理:两条路径互补覆盖
3.1 子漏洞一:xfrm-ESP 页缓存写入
- 模块:
esp4 / esp6(内核 IPsec ESP 实现) - 引入时间:2017 年 1 月 17 日(commit
cac2661c53f3),已存在约 9 年 - 写入原语
- 利用前提:需要
unshare(CLONE_NEWUSER|CLONE_NEWNET) 获取 CAP_NET_ADMIN - 攻击目标:
/usr/bin/su 的页缓存,替换前 192 字节为 root-shell ELF
3.2 子漏洞二:RxRPC 页缓存写入
- 模块
- 引入时间:2023 年 6 月(commit
2dc334f1a63a),约 3 年 - 写入原语:8 字节 STORE(通过
fcrypt_decrypt(C, K) 产生,需暴力搜索密钥 K) - 利用前提:无需任何特权!
add_key()、socket(AF_RXRPC)、splice()、recvmsg() 均为无特权 API - 攻击目标:
/etc/passwd 第 1 行 root 条目,将 passwd 字段置空,利用 PAM nullok 免密登录
3.3 两路径互补覆盖
这是 Dirty Frag 最精妙的地方——两个子漏洞互补覆盖,不管你的系统是什么发行版,至少有一条路径可走:
| | | |
|---|
| | | |
| 限制用户命名空间(Ubuntu + AppArmor) | | | |
| | | |
| | | 至少一条路径可用 |
4. 影响范围:几乎所有 2017 年后的 Linux 内核
4.1 不是所有内核都受影响
| | |
|---|
| | 内核 < 4.10;未编译 CONFIG_INET_ESP |
| | 内核 < 6.4;未编译 CONFIG_RXRPC |
关键点:ESP 子漏洞影响极广(约 9 年),覆盖几乎所有 2017 年后发布的内核。RxRPC 子漏洞范围较窄但无需特权,Ubuntu 默认就加载了 rxrpc.ko。
4.2 已验证可提权的发行版
| | |
|---|
| | |
| 6.12.0-124.49.1.el10_1.x86_64 | |
| | |
| | |
| 6.12.0-124.52.3.el10_1.x86_64 | |
| | |
4.3 各发行版综合风险评估
| | | | |
|---|
| Ubuntu | | | | |
| Ubuntu | | | | |
| Ubuntu | | | | |
| Ubuntu | | | | |
| RHEL | | | | |
| CentOS | | | | |
| Debian | | | | |
| 国产 OS | | | | |
| RHEL/CentOS 7 | | | | |
快速确认 rxrpc 模块:modinfo rxrpc && lsmod | grep rxrpc
4.4 修复状态(截至 2026-05-08)
ESP 子漏洞补丁已合并至 netdev tree,RxRPC 补丁已提交但未合并。CVE 编号暂未分配。
5. 临时缓解:现在就能做的
官方补丁还没出来,但你可以立即做两件事。
方案一:禁用 esp4/esp6/rxrpc 模块(覆盖两条路径)
# 步骤1:创建 modprobe 阻止规则(必须先写规则,再卸载模块!)
sudo sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf"
# 步骤2:卸载已加载的模块
sudo rmmod esp4 esp6 rxrpc 2>/dev/null || true
# 步骤3:验证
lsmod | grep -E 'esp4|esp6|rxrpc'# 应无输出
modprobe install 规则的作用机制:
内核通过 request_module() 自动加载模块时,最终会调用 /sbin/modprobe,而 modprobe 会读取 /etc/modprobe.d/ 下的配置。所以 install esp4 /bin/false能拦截 Dirty Frag PoC 触发的自动加载:
| |
|---|
socket(AF_RXRPC) → request_module("rxrpc") | |
| NET)+ 注册 XFRM SA →request_module("esp4")` |
⚠️ 不适用的场景:
- VPN 网关、IPsec 隧道端点(
esp4/esp6 是核心模块) - 模块编译为内建(
=y)的系统——modprobe 规则无法拦截内建模块
检查模块是否内建:
grep -E 'CONFIG_INET_ESP=|CONFIG_INET6_ESP=|CONFIG_RXRPC=' /boot/config-$(uname -r)
# =m 表示可卸载模块(modprobe 规则可生效)
# =y 表示内建模块(modprobe 规则无法生效,需升级内核)
方案二:禁用非特权用户命名空间(仅缓解 ESP 路径)
sudo sysctl -w kernel.unprivileged_userns_clone=0
⚠️ 此措施无法缓解 RxRPC 子漏洞! RxRPC 不需要用户命名空间权限,所有 API 都是无特权调用。
最佳组合方案:方案一 + 方案二同时使用。方案一阻断 RxRPC 路径,方案二阻断 ESP 路径的用户命名空间前提。
常见缓解失效原因
| | |
|---|
| 模块在措施前已加载,rmmod 未成功(模块被占用) | |
rmmod | | ip xfrm state flush |
| | 确认 /etc/modprobe.d/dirtyfrag.conf 存在且正确 |
6. 两个自查脚本:快速判断你的系统是否受影响
我基于 PoC 的实际利用前提,写了两个互补的检测工具:一个纯只读的 Shell 脚本做静态分析,一个 C 程序做动态 PoC 触发验证。
🛠 开源地址:https://github.com/tangjie1/dirtyfrag-check
6.1 Shell 脚本:dirtyfrag-check.sh(静态分析,纯只读)
设计思路:基于 PoC (V4bel/dirtyfrag) 的实际利用前提逐条验证,纯只读检测,不改写任何系统状态。
检测逻辑:
Dirty Frag 有两条独立提权路径:
- 路径 A — xfrm-ESP:
unshare(NEWUSER|NEWNET) + NETLINK_XFRM SA + splice → 页缓存覆写 - 路径 B — RxRPC:
AF_RXRPC + rxkad 密钥 + pcbc(fcrypt) + splice → 页缓存覆写
只要任意一条路径的所有前提均满足,系统就存在漏洞。
脚本功能清单:
- 内核版本快速排除:与漏洞引入版本比对(ESP: ≥ 4.10, RxRPC: ≥ 6.4),版本不够的直接跳过
- 路径 A 前提逐条验证
- A1:
unshare(NEWUSER|NEWNET) 是否可创建 - A2:
esp4 模块是否可用(区分内建/可加载/已加载) - A4: XFRM 子系统(NETLINK_XFRM)是否可用
- A6: 加密算法
authencesn(hmac(sha256), cbc(aes)) 是否可用 - A7: 缓解措施检查(modprobe 黑名单、initcall_blacklist)
- 路径 B 前提逐条验证
- B5: 密钥子系统(
add_key syscall)是否可用
- 页缓存篡改痕迹检查
/etc/passwd root 行密码字段是否为空(RxRPC 利用典型特征)/usr/bin/su
- 综合风险判定
使用方式:
sudo bash dirtyfrag-check.sh
输出示例:
=====================================================
DirtyFrag 利用条件精确检测 v2.1
基于 PoC (github.com/V4bel/dirtyfrag) 逐项验证
=====================================================
内核: 6.8.0-45-generic
发行版: Ubuntu 24.04 LTS
╔═══════════════════════════════════════════════════╗
║ 内核版本快速排除 ║
╚═══════════════════════════════════════════════════╝
内核 6.8.0-45-generic vs ESP (≥4.10) .... [🔴] ≥4.10(在 ESP 子漏洞引入范围内)
内核 6.8.0-45-generic vs RxRPC (≥6.4) ... [🔴] ≥6.4(在 RxRPC 子漏洞引入范围内)
╔═══════════════════════════════════════════════════╗
║ 路径 A: xfrm-ESP (su_lpe_main) ║
╚═══════════════════════════════════════════════════╝
A0 内核版本 ≥ 4.10 ................. [🔴] ≥4.10
A1 unshare(NEWUSER|NEWNET) ......... [✅] 可创建
A2 esp4 模块可用 .................. [✅] 可用(已加载)
A3 esp6 模块可用 .................. [✅] 可用
A4 XFRM (NETLINK_XFRM) ............. [✅] 可用
A5 XFRM ESN ....................... [✅] 已启用
A6 authencesn(hmac(sha256),cbc(aes)) [✅] 可用
A7 缓解措施 ....................... [ℹ️] 无缓解
== 路径 A 结论: [🔴] 所有前提满足,可被 xfrm-ESP 路径提权 ==
╔═══════════════════════════════════════════════════╗
║ 路径 B: RxRPC (rxrpc_lpe_main) ║
║ 注意: 无需任何特权!不依赖用户命名空间! ║
╚═══════════════════════════════════════════════════╝
B0 内核版本 ≥ 6.4 ................. [🔴] ≥6.4
B1 rxrpc 模块可用 .................. [✅] 可用(已加载)
B2 AF_RXRPC socket ................ [✅] 协议已注册
B3 pcbc(fcrypt) 算法 .............. [✅] 已注册
B4 AF_ALG 接口 .................... [✅] 已启用
B5 密钥子系统 (add_key) ........... [✅] 可用
B6 缓解措施 ....................... [ℹ️] 无缓解
== 路径 B 结论: [🔴] 所有前提满足,可被 RxRPC 路径提权 ==
╔═══════════════════════════════════════════════════╗
║ 最终判定 ║
╚═══════════════════════════════════════════════════╝
[🔴] 存在 DirtyFrag 可利用条件!
- 路径 A (xfrm-ESP): 所有前提满足,可被用于本地提权至 root
- 路径 B (RxRPC): 所有前提满足,可被用于本地提权至 root(无需特权!)
6.2 C 程序:dirtyfrag-poc-check.c(动态验证,基于 PoC 触发原语)
设计思路:Shell 脚本是静态分析——检查前提条件是否存在。但"前提存在"不等于"一定能利用"。C 程序更进一步,实际触发 PoC 的写入原语,但将攻击目标从系统文件替换为自创建的临时文件,验证页缓存是否真的可以被篡改。
安全保障(这是关键——安全自查工具自身必须安全):
- 所有页缓存写入操作的目标均为自创建的临时文件(
/tmp/.dirtyfrag-check-esp 和 /tmp/.dirtyfrag-check-rxrpc),不触碰任何系统文件 - ESP 路径写入已知标记
0xDEADBEEF(4 字节),不植入任何可执行代码 - RxRPC 路径仅验证页缓存可被修改,不构造特定密码字段
- 检测完成后自动清理:删除临时文件、刷新页缓存、销毁密钥
- 即使程序异常退出,页缓存篡改也仅存在于临时文件中,
drop_caches 或重启后即可恢复
检测流程:
ESP 路径检测
- 创建临时文件
/tmp/.dirtyfrag-check-esp(4096 字节,填充 0x41) mmapunshare(CLONE_NEWUSER|CLONE_NEWNET)- 通过
NETLINK_XFRM 注册 SA(seq_hi = 0xDEADBEEF) vmsplice
- 父进程验证:
mmap 读取 + pread 读取,对比原始值
RxRPC 路径检测
- 前置检查:
socket(AF_RXRPC) → add_key("rxrpc") → AF_ALG pcbc(fcrypt) - 创建临时文件
/tmp/.dirtyfrag-check-rxrpc(4096 字节) - 用户空间计算
fcrypt_decrypt(C, K) 预期结果 - 发送 CHALLENGE → 排空 RESPONSE → 计算 cksum
- 构造恶意 DATA 头 +
vmsplice + splice 触发写入 recvmsg 触发内核 rxkad_verify_packet_1 解密路径
- 验证页缓存:对比
mmap 读取值与 fcrypt_decrypt 预期结果
编译和使用:
# 编译
gcc -o dirtyfrag-poc-check dirtyfrag-poc-check.c
# 检测两条路径
./dirtyfrag-poc-check
# 仅检测 ESP 路径
./dirtyfrag-poc-check --esp-only
# 仅检测 RxRPC 路径
./dirtyfrag-poc-check --rxrpc-only
# 详细输出
./dirtyfrag-poc-check --verbose
# 仅清理残留临时文件
./dirtyfrag-poc-check --cleanup-only
退出码:
输出示例:
╔══════════════════════════════════════════════════════════╗
║ Dirty Frag 安全自查工具 v0.1 ║
║ 基于漏洞触发原语,验证页缓存是否可被篡改 ║
╚══════════════════════════════════════════════════════════╝
安全说明:本工具仅操作自创建的临时文件,不修改任何系统文件
[+] === 前置条件检查 ===
[+] 内核版本: 6.8.0-45-generic x86_64
[+] 当前用户: uid=1000 euid=1000 gid=1000
[+] 非特权用户命名空间: 允许
[+] esp4 模块: 已加载
[+] rxrpc 模块: 已加载
[+] === 检测 ESP (xfrm-ESP Page-Cache Write) 子漏洞 ===
[+] 目标文件: /tmp/.dirtyfrag-check-esp (非系统文件)
[+] ESP 检测:页缓存已被篡改! (mmap) 原始=0x41414141 当前=0xDEADBEEF
[+] ESP 检测:页缓存已被篡改! (pread) 原始=0x41414141 当前=0xDEADBEEF
[+] === 检测 RxRPC (RxRPC Page-Cache Write) 子漏洞 ===
[+] 目标文件: /tmp/.dirtyfrag-check-rxrpc (非系统文件)
[+] RxRPC 检测:页缓存已被篡改! (mmap)
[+] 确认:篡改值与 fcrypt_decrypt(C, K) 结果完全匹配,漏洞100%可利用
╔══════════════════════════════════════════════════════════╗
║ 检测结果报告 ║
╚══════════════════════════════════════════════════════════╝
ESP (xfrm-ESP) 子漏洞: ⚠ 受影响
RxRPC 子漏洞: ⚠ 受影响
╔═══════════════════════════════════════════════════╗
║ ⚠ 警告:系统受 Dirty Frag 漏洞影响! ║
║ 请立即参考安全通告执行修复操作 ║
╚═══════════════════════════════════════════════════╝
6.3 两个脚本怎么配合用?
| |
|---|
| Shell 脚本(dirtyfrag-check.sh),无需编译,纯只读 |
| C 程序(dirtyfrag-poc-check),实际触发 PoC 写入原语 |
| 先跑 Shell 脚本看前提是否阻断,再跑 C 程序确认页缓存不可篡改 |
| Shell 脚本优先(零风险),C 程序需确认安全策略允许 |
7. 官方修复方案与回退
目前上游补丁尚未完全合并,各发行版均未发布官方修复包。待修复包发布后:
回退操作(修复后):
# 删除模块禁用配置
sudo rm /etc/modprobe.d/dirtyfrag.conf
# 恢复用户命名空间设置
sudo sysctl -w kernel.unprivileged_userns_clone=1
# 重启
sudo reboot
各发行版查询方式:
| |
|---|
| apt update && apt list --upgradable |
| dnf check-update |
| apt update && apt list --upgradable |
| zypper list-patches |
8. Dirty Frag 的技术深潜
如果你只关心操作,上面的内容够了。如果你想理解漏洞原理,继续往下看。
8.1 为什么叫"页缓存篡改"?
Linux 内核通过页缓存(Page Cache) 机制缓存磁盘文件内容。当进程读取文件时,内核优先从页缓存返回数据,避免磁盘 I/O。当进程写入文件时,内核先修改页缓存中的数据,再异步刷回磁盘。
Dirty Frag 的核心在于:通过内核子系统的漏洞,将任意数据写入页缓存中属于其他文件的页面。这绕过了文件系统的权限检查——你以为你在读 /usr/bin/su,实际上这个文件在页缓存中的内容已经被替换成了攻击者的 root-shell ELF。
8.2 ESP 路径的利用链
- 建立命名空间:
unshare(CLONE_NEWUSER|CLONE_NEWNET) 获取 CAP_NET_ADMIN - 注册 XFRM SA:通过
NETLINK_XFRM 注册 IPsec SA,设置 XFRM_STATE_ESN flag 和自定义 seq_hi 值 - 触发 ESP 处理:发送 UDP 包到 ESP 端口,内核
esp4_recv() 解密后处理 ESN replay window - splice 注入:
vmsplice ESP 头部到 pipe → splice 目标文件页缓存页到 pipe → splice pipe 到 socket - 页缓存覆写:内核在
esp_output_head() 路径中,将 seq_hi 的 4 字节 STORE 到了 splice 注入的页缓存页面
关键点:seq_hi 是攻击者通过 XFRM SA 注册时设定的值,4 字节任意可控。
8.3 RxRPC 路径的利用链
- 创建 AF_RXRPC socket + rxkad 密钥:
add_key("rxrpc", ...) 注册 rxkad 类型密钥 - 建立 RxRPC 连接:客户端发起 call,伪造 UDP 服务器完成 CHALLENGE-RESPONSE 握手
- 构造恶意 DATA 包:伪造 RxRPC wire header + splice 目标文件页缓存页
- 触发解密:
recvmsg() 触发内核 rxkad_verify_packet_1(),对页缓存数据执行 fcrypt_decrypt(C, K) - 页缓存覆写
关键点:RxRPC 的 8 字节写入不是直接可控的——它是 fcrypt_decrypt(C, K) 的结果,其中 C 是页缓存中的原始内容,K 是攻击者注册的 rxkad 密钥。攻击者需要暴力搜索 K 使得解密结果恰好是自己想要的值。
对于 /etc/passwd 提权场景,攻击者只需将 root 行的 passwd 字段(x)替换为空——这不需要精确控制全部 8 字节,只需要第 2 个字节(x)被替换为 : 即可。
8.4 为什么 RxRPC 路径不需要任何特权?
这是一个精妙的利用链设计:
| |
|---|
add_key("rxrpc", ...) | |
socket(AF_RXRPC, ...) | |
socket(AF_ALG, ...) | |
splice() | |
recvmsg() | |
所有 API 都是普通用户可以直接调用的。不需要 CAP_NET_ADMIN,不需要用户命名空间,甚至不需要任何 capability。这就是为什么 RxRPC 子漏洞的 CVSS 基础得分(8.8)比 ESP 子漏洞(7.8)高了整整 1 分。
9. CVSS 评分
因无 CVE 编号,暂无官方 CVSS 评分。以下为参考评分:
10. 参考资料
- Dirty Frag 漏洞官方仓库:https://github.com/V4bel/dirtyfrag
- Dirty Frag 技术白皮书:https://github.com/V4bel/dirtyfrag/blob/master/assets/write-up.md
- Dirty Frag 检测工具集(本文配套):https://github.com/tangjie1/dirtyfrag-check
- ESP 补丁 commit:https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net.git/commit/?id=f4c50a4034e62ab75f1d5cdd191dd5f9c77fdff4
- RxRPC 补丁提交:https://lore.kernel.org/all/afKV2zGR6rrelPC7@v4bel/
- Openwall OSS-Security 披露:https://www.openwall.com/lists/oss-security/2026/05/07/8
- LWN.net Dirty Frag 专文:https://lwn.net/Articles/1071719/
- CVE-2026-31431 (Copy Fail) NVD:https://nvd.nist.gov/vuln/detail/CVE-2026-31431
本文首发于微信公众号,转载请注明出处。