TL;DR :这是 2026 年 5 月 7 日公开披露的 Dirty Frag 漏洞链的主角之一(另一条是 CVE-2026-43500)。一句话定性------**内核网络栈的"高效零拷贝"把自己绊倒了:一个只读的页缓存页,被 splice 偷偷塞进网络包的碎片里,然后 ESP 接收路径以为那是它私有的缓冲区,直接在上面做原地解密写入。结果是:非特权用户可确定性地污染任意文件的页缓存,稳稳拿到 root。** 无竞态、无 crash、不碰磁盘。
0 · 先定位:Dirty Frag 到底是什么
Dirty Frag 不是单一 CVE,而是一个漏洞家族名称,覆盖两个独立但同构的 bug:
| CVE | 子系统 | 做什么 | CVSS |
|---|---|---|---|
| CVE-2026-43284 | xfrm/ESP(esp4 + esp6 模块) |
ESP-in-UDP 的原地解密路径把外部共享页当私有页写入 | 8.8 High(kernel.org CNA) |
| CVE-2026-43500 | rxrpc(AFS 的 RxRPC 协议) |
RxRPC 接收路径的 fcrypt原地解密对共享页做 8 字节 STORE |
7.8 High(NVD / CISA) |
两者的共同祖先缺陷一句话就能说透:
splice()/MSG_SPLICE_PAGES可以把一个你只有读权限的文件的 page cache page,以"引用"的方式挂到内核网络包(sk_buff.frags[])里;下游的消费代码(ESP 解密 / RxRPC 解密)没有检查"这个页是不是别人也在用",直接原地写入 → 你改了只读文件的 RAM 副本。
这个 bug class 和 Dirty Pipe(CVE-2022-0847) 、Copy Fail(CVE-2026-31431) 是一脉相承的------都是**"零拷贝的引用语义 + 下游的 in-place 写假设"**撞在一起的结果。
1 · 漏洞核心:SKBFL_SHARED_FRAG 的缺席
1.1 正常情况下的保护机制:SKBFL_SHARED_FRAG + skb_cow_data()
Linux 网络栈里,sk_buff(简称 skb)的非线性数据可以存在 skb_shinfo(skb)->frags[]数组里,每一项是一个 skb_frag_t,指向一个 struct page *(就是页缓存页或别的什么页)。
当一个 skb 的 frag 引用的 page 不是内核独家拥有的(比如来自 pipe 的页、来自文件页缓存的页),正确做法是:
-
标记
SKBFL_SHARED_FRAG标志位 → 告诉后续路径"这个 skb 的数据不是私有的" -
任何需要修改数据的路径看到这个 flag → 先
skb_cow_data()做一个 COW(Copy-On-Write)私有拷贝 → 再写私有副本
TCP 路径(skb_splice_from_iter()之后)正确地做了第一步 :设置了 SKBFL_SHARED_FRAG。
1.2 问题出在哪:IPv4/IPv6 的 UDP 拼接路径漏设了 flag
关键句子就在官方修复描述里,也是最精辟的一句:
TCP marks such skbs with
SKBFL_SHARED_FRAGafterskb_splice_from_iter(), so later paths that may modify packet data can first make a private copy. The IPv4/IPv6 datagram append paths did not set this flag when splicing pages into UDP skbs.
翻译成人话:
| 步骤 | 正常期望 | 实际情况 |
|---|---|---|
你用 splice()把一个文件(比如 /usr/bin/su的某个页)喂到 socket |
内核把该页的引用挂进 frags[],**同时标 SKBFL_SHARED_FRAG** |
IPv4/IPv6 datagram 路径忘了标 |
| 后续 ESP 接收处理看到这个 skb | 看到 shared flag → skb_cow_data()→ 私有拷贝 → 安全 |
没看到 flag → 以为 skb 是 uncloned 私有非线性 skb → 走 no-COW fast path |
| ESP 原地解密 | 写在私有拷贝上,不影响原始页 | 直接写在原始 page cache page 上 💀 |
结果:/usr/bin/su的页缓存在内存里被覆写,但磁盘上的 /usr/bin/su完好无损------传统文件完整性监控完全看不见。
2 · 攻击链全景:从零到 root 的逻辑链条
2.1 攻击者需要什么 precondition?
| 条件 | 说明 |
|---|---|
| 本地低权限用户 shell | 不是远程直接打,但 foothold 易得(Web RCE、SSH、CI runner、容器内执行等) |
| 内核版本 ≥ 4.11(~2017)且未打补丁 | 几乎所有 2017--2026.5 之间的内核 |
| esp4/esp6 模块可用(CVE-2026-43284 路径) | 多数发行版默认编译进内核或可作模块加载;利用时需要 CAP_NET_ADMIN→ 通常通过 `unshare(CLONE_NEWUSER |
| 或者 rxrpc 模块可用(CVE-2026-43500 路径) | RxRPC 路径不需要用户命名空间创建,纯普通用户权限即可 → 这是它在 Ubuntu 等加固系统上也通的key |
2.2 两条利用路线(你通常选更容易的那条)
路线 A:CVE-2026-43284(ESP 路径)
1. 普通用户 → unshare(CLONE_NEWUSER|CLONE_NEWNET) 拿到伪 CAP_NET_ADMIN
2. 创建 XFRM SA(IPsec 转换状态),注册 ESP 处理
3. 用 splice()/vmsplice() 把 /usr/bin/su 的页缓存页引用喂入 UDP socket
4. 触发 ESP-in-UDP 接收 → 原地解密写入 su 的页缓存
5. 覆写 su 的前 N 字节为 shellcode stub(setuid(0)+execve)
6. 执行 /usr/bin/su → 以 root 身份跑你的代码 → 🎉
路线 B:CVE-2026-43500(RxRPC 路径,更优雅)
1. 不需要用户命名空间
2. 用 RxRPC socket + splice 把 /etc/passwd 的页缓存页挂进去
3. 原地解密路径对其做 8-byte STORE × 3
4. 把 root 行的 password 字段清空
5. su - → 空密码直接拿 root[2,12](@ref)
PoC 已在 GitHub 公开(
V4bel/dirtyfrag),且有多个安全厂商复现确认稳定性。
3 · 为什么这个漏洞格外危险------跟 Dirty Pipe / Copy Fail 放一起看
| Bug | 年份 | 污染载体 | 写入粒度 | 竞态? | 可见性(磁盘) |
|---|---|---|---|---|---|
| Dirty COW (CVE-2016-5195) | 2016 | mmap + write fault | 页级 | ✅ 竞态 | 需要 sync 才落盘 |
| Dirty Pipe (CVE-2022-0847) | 2022 | pipe+ splice()+ 页标志 |
任意偏移 4KB 内 | ❌ 确定 | 仅 RAM |
| Copy Fail (CVE-2026-31431) | 2026.04 | AF_ALG + splice()→ crypto SGL |
4 字节 / 轮 | ❌ 确定 | 仅 RAM |
| Dirty Frag (CVE-2026-43284/500) | 2026.05 | sk_buff.frags[]+ ESP/RxRPC 原地解密 |
4B(ESP) / 8B(RxRPC) | ❌ 确定 | 仅 RAM,不改变磁盘哈希 |
核心规律很清楚:Linux 的页缓存共享语义 + 零拷贝引用传递 + "我拥有这个 buffer"的假设这三者的交集,是过去 10 年里最高产的 LPE 温床之一。
Dirty Frag 的特别之处:
-
✅ 确定性:不靠 timing,不靠 CPU pinning,不靠 brute force
-
✅ 安静 :不改磁盘 →
sha256sum /usr/bin/su看起来完全正常 -
✅ 双入口:ESP 路径 + RxRPC 路径互为备份------一个被堵还有另一个
-
✅ 跨容器 :页缓存是宿主全局共享的,容器内一样能污染宿主机
su的缓存
4 · 受影响范围速判
内核版本维度
| 范围 | 状态 |
|---|---|
| < 4.11 | 不受影响(esp4/esp6 的 splice 路径那时还不存在) |
| 4.11 ≤ kernel < 修复版本 | ⚠️ 受影响 |
| 修复阈值举例 | 5.10.206+、5.15.206+、6.1.172+、6.6.138+、6.12.87+、6.18.28+、≥ 7.0.5、≥ 7.1-rc3 |
一键快查
bash
# 1. 内核版本
uname -r
uname -a
# 2. esp4/esp6 是否编译进来了
grep -E "^CONFIG_(INET_ESP|INET6_ESP)=" /boot/config-$(uname -r) 2>/dev/null
# =y 或 =m → 受影响的编译路径存在
# 3. 模块是否已加载
lsmod | grep -E "esp4|esp6|rxrpc"
# 4. 你能否创建用户命名空间(ESP 路径的常用前置)
unshare -Ur -- cat /proc/self/status 2>&1 | head -5
# 能看到 Effective: 0 0 ... 就说明 unprivileged userns 开着
5 · 修复 & 缓解(按优先级)
✅ 第一选择:升级内核(必须重启)
bash
# Ubuntu / Debian
sudo apt update && sudo apt full-upgrade
sudo reboot
# RHEL / Alma / Rocky
sudo dnf update kernel
sudo reboot
# 验证新内核起来后
uname -r
Canonical 的公告给出了各版本的 fixed kernel 版本号(18.04/20.04/22.04/24.04 各有对应包版本),阿里云 Linux 也有对应的 ALINUXx-SA 条目。
🛡️ 临时缓解(不能立即重启时用)
核心思路:让 esp4/esp6/rxrpc 不可加载 / 不活动,斩断攻击面:
bash
# 方案 A:modprobe 黑名单 + 尝试卸载
sudo sh -c 'printf "install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n" \
> /etc/modprobe.d/dirtyfrag.conf'
sudo rmmod esp4 esp6 rxrpc 2>/dev/null || true
# 顺手清一下可能被污染的页缓存(不能"修复"已污染但能降风险)
sync && echo 3 | sudo tee /proc/sys/vm/drop_caches > /dev/null
⚠️ 业务代价 :如果你这台机器真的跑 IPsec VPN(strongSwan / libreswan 的 kernel-mode ESP),禁用 esp4/esp6 会断 VPN------需要按业务情况评估。
🐳 容器场景额外防线
默认 Docker seccomp 不拦 AF_KEY / XFRM netlink,但部分配置会拦 AF_RXRPC。最稳的做法是在容器 runtime 层加约束,并尽快把宿主机内核升级------因为页缓存是宿主机全局的,容器内的缓解只是缩面,不是根治。
6 · 蓝队检测线索
因为 Dirty Frag 不改磁盘,检测必须靠行为:
| 信号 | 采集方式 |
|---|---|
普通用户频繁调用 socket(AF_NETLINK, ...)+ XFRM msg(XFRM_MSG_NEWSA等) |
AuditD / eBPF LSM |
splice()/ vmsplice()紧跟 sendmsg()/ recvmsg()到奇怪 socket 类型 |
syscall tracing |
非特权用户成功 unshare(CLONE_NEWUSER)+ 随后加载 netdev 相关模块 |
audit + kaudit |
/usr/bin/su执行后产生异常子进程(shell 从 su 出来但 parent 不是正常 login 链) |
进程血缘监控 |
页缓存异常:文件 inode 的 RAM 内容与磁盘不一致(高级检测:用 vmtouch/ eBPF 做抽查) |
内存取证 |
Microsoft Defender 这边已经发了签名覆盖(Exploit:Linux/DirtyFrag.A/B、Trojan:Linux/DirtyFrag.*),Defender for Cloud 也有 posture 检测。
7 · 官方修复到底改了什么(代码层面一句话版)
修复做了两件互补的事:
-
IPv4/IPv6 datagram 的 splice-to-UDP 路径 :补上缺失的
SKBFL_SHARED_FRAG标记(对齐 TCP 的正确行为) -
ESP input 路径 :即使 flag 出现,也 fallback 到
skb_cow_data()做 COW 私有拷贝,绝不对外引用的页原地解密
本质上就是把"这不是我的页"这个事实,从 flag 的缺席态 → 强制显式态,让后续所有写路径正确走 COW。
⚠️ 结语
CVE-2026-43284 是那种教科书级别的"效率换安全"陷阱------splice 零拷贝很快,但不该快的代价是让页缓存的 ownership 变得模糊。从 Dirty Pipe 到 Copy Fail 到 Dirty Frag,同一个主题反复出现:
只要内核把一个"仅可读页缓存页的引用"交给了某个可以做 in-place write 的下游路径,且下游没有 ownership 检查,攻击者就拥有一个 page-cache 级的 write-what-where。
这不是某个开发者马虎,而是整个"零拷贝引用传递"范式的系统性张力------快,就要共享引用;共享引用,就必须付出精确的追踪成本。
仅供合法授权环境研究/防御加固使用。 在自己没授权的机器上验证这个 exploit = 违法入侵。如果这是你接到的安全工单,最合理的下一步是:拉内核升级窗口 + 先上 modprobe 黑名单兜底。