一种针对 CVE-2026-31431(“Copy Fail”)的新型利用技术,通过 Linux 内核的页缓存破坏运行中进程的可执行代码,实现 root 权限提升。
该 CVE 的所有已发布漏洞利用都是破坏数据文件——修改 /etc/passwd 以更改 root 的 UID,或修补磁盘上的 setuid 二进制文件。此漏洞利用则完全不同:它通过内核的页缓存机制破坏内存中的实时可执行代码。
| 已发布的漏洞利用 | 本漏洞利用 | |
|---|---|---|
| 目标 | 数据文件(/etc/passwd、/usr/bin/su) | 可执行代码(libc .text 段) |
| 机制 | 由程序解析的文件内容 | 由 CPU 直接执行的代码页 |
| 范围 | 单个文件 | 所有映射 libc 的进程 |
| 磁盘更改 | 是(文件内容被修改) | 否(仅内存中的页缓存) |
| 检测 | 文件完整性监控 | 对文件系统检查不可见 |
当 Linux 使用 MAP_PRIVATE 映射共享库时,它不会立即复制文件页。相反,进程的页表项直接指向页缓存物理页。复制仅在写入时发生(写时复制)。对于代码页(.text),进程永远不会写入它们——因此它们永远与页缓存保持共享。
进程 A (bash) 进程 B (su) 页缓存 (libc)
┌──────┐ ┌──────┐ ┌──────────┐
│ PTE │───────────>│ │ │ exit(): │
│ │ │ PTE │───────────>│ endbr64 │
└──────┘ └──────┘ │ push rax │
MAP_PRIVATE MAP_PRIVATE │ ... │
(干净页) (干净页) └──────────┘
↑
Copy Fail 在此写入!
CVE-2026-31431 为我们提供了对任何可读文件页缓存的受控 4 字节写入。通过破坏 libc 的代码页,我们修改了每个进程都会执行的指令。
on_exit() 函数位置——每次 4 字节,共 10 次写入exit() 使其跳转到我们的 shellcode——1 次 4 字节写入exit() 的程序(几乎所有程序)都会执行我们的 shellcode该 shellcode 执行 setuid(0) → setgid(0) → execve("/bin/sh"),从而获得 root shell。
crypto/algif_aead.c 中的逻辑错误——2017 年的原地优化(72548b093ee3)允许 splice() 将页缓存页送入 AEAD 解密。authencesn 算法的 ESN 重排将 4 个临时字节写回源 scatterlist,从而破坏被拼接文件的页缓存。# 编译
gcc -o exploit exploit.c
# 以非特权用户运行
./exploit /lib/x86_64-linux-gnu/libc-2.31.so
# 触发——运行任意程序(它会调用 exit())
su -c id
# uid=0(root) gid=0(root) groups=0(root)
process 'id' launched '/bin/sh'exploit.c — 带 shellcode 注入的完整漏洞利用copyfail_test.c — 原语验证(测试 4 字节写入)72548b093ee3)本工具仅供授权的安全研究和教育目的使用。请勿将其用于您不拥有或未经明确许可测试的系统。
Dirty Frag 披露了同一缺陷类别中的另外两种页缓存写入变体:
esp4/esp6):与 Copy Fail 相同的 authencesn 汇点,通过 IPsec ESP-in-UDP 到达。需要用户命名空间。4 字节写入。rxrpc/rxkad):在 splice 固定的页上进行 pcbc(fcrypt) 原地解密。无需命名空间。8 字节写入。本仓库中的实时代码破坏技术适用于任何这些页缓存写入原语——只需将 write4() 函数替换为 ESP 或 RxRPC 触发器即可。该技术与原语无关。