言零的博客

pedit COW:一场在页面缓存里悄无声息的 root 政变

2026 年 06 月 29 日 18:00

真正的剑客不在杯中寻毒,而在茶里看见破绽。内核亦然。

零、一句话概括

CVE-2026-46331(绰号 “pedit COW”)是 Linux 内核流量控制子系统中的一个越界写入漏洞。攻击者利用 act_pedit 模块在 Copy-on-Write 阶段的校验缺陷,对共享页面缓存进行越界写入,在不触碰磁盘文件的情况下毒化 /bin/su 的内存映射,最终以非特权用户身份获取 root 权限。

CVSS 评分:7.8(HIGH)|攻击复杂度:低|利用条件:本地、非特权用户在启用了非特权用户命名空间的系统上

漏洞曝出不到 24 小时,公开 PoC 即现身 GitHub。这是一场从 mailing list 上被低估的「例行数据损坏补丁」到 weaponized exploit 的经典演变。


一、先复盘:CoW 这一类漏洞的血脉传承

如果你对 Linux 内核的 page-cache 漏洞稍有涉猎,应该已经对这个家族不陌生了:

漏洞名称 CVE 年份 核心手法
Dirty Pipe CVE-2022-0847 2022 splice() 未重置 pipe buffer flags,向已释放的 page cache 写入
Copy Fail CVE-2024-XXXX 2024 copy_file_range() 的 CoW 断裂
DirtyClone CVE-2026-XXXX 2026 io_uring 零拷贝路径越过 page 所有权检查
Dirty Frag CVE-2026-XXXX 2026 碎片整理过程中 page 引用计数竞争
pedit COW CVE-2026-46331 2026 act_pedit 运行时偏移解析绕过越界检查

它们的本质是同一个「脉门」:

内核在某条快速路径上对页面做了写操作,但这个页面并不归它独占。

本质上这是一个逻辑漏洞,而非内存破坏(memory corruption)。不涉及 ROP、不涉及 KASLR 绕过、不涉及堆喷。只要知道哪一页被共享,把 payload 写进去就行。文件完整性校验会告诉你一切正常,而你早已是 root。


二、漏洞机制:一封被投毒的信,信封完好无损

2.1 act_pedit 的正常工作流

tc(traffic control)是 Linux 的流量整形瑞士军刀。其中 pedit action 可以在数据包经过时,实时修改包头字段——比如改 TTL、改 DSCP、改 IP 地址。

当你配置一条 pedit 规则并触发它时,内核调用链大致是:

tcf_pedit_act()
 → pedit_skb_hdr_offset() // 计算偏移
 → tcf_pedit_act() 内部 // 校验写入范围
 → 如果页面是共享的(refcount > 1)
 → 创建私有副本 (CoW)
 → 写入数据

理想情况下,CoW 保护在写入前完成,shared page 永远不被污染。

2.2 断裂点在哪里?

问题出在两次偏移解析之间。

第一次校验发生在 tcf_pedit_act() 计算 nkeys 时,它检查所有 key 的 offsetvalmask 组合后的总写入范围是否合法。此时对于某些特殊的 key 类型(偏移在运行时依赖实际数据包头内容才能确定),内核使用的是一种「预留槽」机制——先分配了一个最大范围,但没有精确锁定每个 key 的最终落点。

第二次,也就是实际写入时,这些 key 的偏移被运行时解析(例如基于 VLAN tag 层数动态计算)。如果运行时解析出的偏移超出了第一次校验时预留的私有副本范围——

写入操作就越界落回了共享的 page cache 页。

用一句话讲:

它量好尺寸才去借西装,结果扣子缝歪了,扎到了别人的皮肤上。

2.3 投毒链条

攻击者的完整攻击链:

1. 创建用户命名空间 → 获得 namespace-local CAP_NET_ADMIN
2. 加载 act_pedit 模块(如果未加载)
3. 打开 /bin/su(setuid root)→ 页面进入 page cache(共享状态)
4. 构造 tc pedit 规则,精确计算偏移使写入落在 /bin/su 的内存页中
5. 触发规则,向 cached page 注入 shellcode / 修改逻辑
6. 执行被投毒的 /bin/su → 内核看到 setuid bit + 文件未修改(磁盘是干净的)
 → 实际执行的是被污染的内存页中的代码
 → 获得 root shell

这套攻击链不需要写 /etc/passwd、不需要改 sudoers、不触发任何文件系统级别的审计日志。整个过程在内存中完成,文件哈希不变,AIDE/Tripwire 静默。


三、为什么这次特别危险?

3.1 入口门槛低

过去的 page-cache 漏洞往往需要特定条件:

而 pedit COW 的入口是 非特权用户命名空间 + 可加载的内核模块。在主流发行版的默认配置下,这两项都是开启的:

发行版 unprivileged userns act_pedit 可用 默认可被利用?
RHEL 8/9/10 默认开启 内核内置或可加载
Debian 11/12/13 (trixie) 默认开启 可加载
Ubuntu 18.04-24.04 默认开启 可加载 是(24.04 需绕 AppArmor)
Ubuntu 26.04 内核支持 可加载 受限,AppArmor 默认阻断 userns

3.2 检测盲区

传统检测手段几乎全部失效:

唯一有效的检测是行为审计:一个非特权用户突然执行了 tc 命令并带着 pedit action,这本身就是异常信号。但攻击者可以在投毒后清理 tc 规则,痕迹只留在一瞬间。

3.3 时间线令人不安

2026-05-23 补丁出现在 netdev mailing list(标注为"数据损坏修复")
2026-06-16 CVE 编号分配(补丁合入主线)
2026-06-17 公开 PoC 发布
2026-06-26 The Hacker News 报道
2026-06-29 本文发布时,大量发行版仍未推送修复

从补丁公开到武器化利用,只用了 3 周。一个漏洞的补丁在 mailing list 上躺了三周无人重视,本身就是安全响应流程的失败。


四、攻防实操

4.1 复现环境(PoC 已验证)

根据公开 PoC 的测试结果:

PoC 地址:github.com/sgkdev/packet_edit_meme

4.2 快速自查

# 1. 检查内核版本
uname -r

# 2. 检查 act_pedit 模块是否可加载(存在 .ko 文件即危险)
find /lib/modules/$(uname -r) -name '*act_pedit*'

# 3. 检查 unprivileged user namespaces 状态
# Debian/Ubuntu:
cat /proc/sys/kernel/unprivileged_userns_clone
# RHEL/CentOS:
cat /proc/sys/user/max_user_namespaces

# 4. 综合判定:
# - 内核版本 < 修复版本(各发行版自行查询)
# - act_pedit 模块可用(.ko 存在或已加载)
# - unprivileged userns = 1(或 max_user_namespaces > 0)
# → 系统存在被利用条件

4.3 立即处置:两条防线,双管齐下

防线一:阻断 act_pedit 模块加载

# 检查模块是否已加载
lsmod | grep act_pedit

# 永 久阻止加载
echo 'install act_pedit /bin/true' | sudo tee /etc/modprobe.d/disable-act_pedit.conf

# 如果已加载,立即卸载
sudo rmmod act_pedit 2>/dev/null

此项操作不影响正常网络功能——除非你主动使用 tc pedit 修改数据包头。绝大多数服务器不需要此功能。

防线二:禁用非特权用户命名空间

# Debian/Ubuntu(立即生效 + 持久化)
sudo sysctl -w kernel.unprivileged_userns_clone=0
echo "kernel.unprivileged_userns_clone = 0" | sudo tee /etc/sysctl.d/99-disable-userns.conf

# RHEL/CentOS
sudo sysctl -w user.max_user_namespaces=0
echo "user.max_user_namespaces = 0" | sudo tee /etc/sysctl.d/99-disable-userns.conf

注意:禁用 unprivileged userns 会破坏 rootless 容器(Podman rootless、Docker rootless)、部分 CI 沙箱、以及基于命名空间隔离的浏览器沙箱(Chromium/Firefox)。在生产环境操作前务必评估影响。

防线三:升级内核并重启

这是唯一彻底的修复方案。对于无法立即重启的系统,前两条防线是救命稻草。

# Ubuntu/Debian
sudo apt update && sudo apt install linux-image-generic -y
sudo reboot

# RHEL/CentOS
sudo yum update kernel -y
sudo reboot

4.4 事故后的取证思路

如果怀疑已被入侵:

# 1. 检查 tc 规则中是否有异常的 pedit action
tc -s filter show dev lo 2>/dev/null
tc -s filter show dev eth0 2>/dev/null

# 2. 清除页面缓存(会丢掉攻击痕迹,但至少确保当前运行的二进制是干净的)
echo 3 | sudo tee /proc/sys/vm/drop_caches

# 3. 检查是否有异常 root shell 进程(如 /bin/su 的不正常子进程)
ps auxf | grep -v grep | grep -E 'su|bash.*root'

# 4. 全面取证:dump 内存、检查 auditd 日志时间线上的 tc 命令
# 但因攻击在内存中完成,磁盘取证价值有限。
# 原则:发现被利用 → 视为完全失陷 → 重装系统,不是清理。

五、从补丁到武器,中间差了什么?

这个漏洞真正危险的地方在于信息差,技术复杂度其实比 Dirty Pipe 更低。

5 月 23 日,补丁出现在 netdev mailing list,标题平淡无奇,归类为「数据损坏修复」。没有人标注 Security、没有人分配 CVE、没有人拉响警报。

6 月 16 日,补丁合入主线,CVE 下发。

6 月 17 日,exploit 发布。

从 mailing list 到 weaponized PoC,跨度三周。直到 6 月底,大量生产系统依然暴露。

严格来说这算 -21day:防御方有 21 天的时间窗口,但没有人认出这是一道裂缝。

安全社区需要反思的不只是代码质量,还有对补丁中「无关紧要的修复」的嗅觉。

内核 page-cache 写入路径上的任何校验变更——哪怕是「修复数据损坏」——都应该自动触发安全团队的审视。因为在这个领域,数据损坏和权限提升之间的距离,往往只隔着一个 setuid 位。


六、资源链接