tgies/copy-fail-c
GitHub: tgies/copy-fail-c
CVE-2026-31431 Linux本地提权漏洞的跨平台C语言概念验证实现,利用AF_ALG/splice页缓存篡改原语实现非特权到root的提权。
Stars: 191 | Forks: 50
# Copy Fail (CVE-2026-31431) - C 语言移植版
*[English (en)](README.md) ∙ [日本語 (ja)](README.ja.md) ∙ [简体中文 (zh-cn)](README.zh-cn.md) ∙ [한국어 (ko)](README.ko.md) ∙ [Русский (ru)](README.ru.md)*
Copy Fail Linux LPE (本地提权,CVE-2026-31431) 漏洞的跨平台 C 语言重实现,该漏洞由 Theori / Xint 于 2026-04-29 披露。请参阅 [copy.fail](https://copy.fail/) 上的官方报告,以获取完整的漏洞描述、时间线以及 Theori 的发现过程。
公开的概念验证是一个 732 字节的 Python 脚本。这个 C 语言移植版演示了同样的漏洞利用可以表示为可移植的 C 代码,可编译为 nolibc 支持的任何架构,并且项目自身的源代码中没有针对特定架构的十六进制数据块或内联汇编。
此移植版的作者:Tony Gies 。
发现与原始披露者:Theori / Xint。
## 仓库布局
```
copy-fail-c/
├── exploit.c the dropper (binary-mutation variant)
├── exploit-passwd.c the dropper (/etc/passwd UID-flip variant)
├── vulnerable.c non-destructive vulnerability checker
├── payload.c the body that gets dropped (setgid+setuid+execve sh)
├── utils.c, utils.h shared AF_ALG/splice page-cache mutation primitive
├── Makefile build orchestration
├── nolibc/ vendored from torvalds/linux tools/include/nolibc
└── README.md this file
```
运行 `make` 后:
```
├── payload tiny static ELF, embedded into the dropper as bytes
├── payload.o payload wrapped as a relocatable .o by `ld -r -b binary`
├── exploit dropper, binary-mutation variant
├── exploit-passwd dropper, /etc/passwd UID-flip variant
└── vulnerable non-destructive vulnerability checker
```
`exploit.c` 以只读方式打开目标二进制文件,然后针对内嵌 payload 的每个 4 字节滑动窗口,通过 AF_ALG 运行一次伪造的 AEAD 解密,其密文输入是通过 splice() 从目标的页缓存页面提供的。authencesn 模板的原位优化将 splice 的源页面同时视为密文输入和明文目标,因此在身份验证拒绝该请求时,(失败的)解密已经覆盖了页缓存页面。经过 4 * N 次迭代后,目标的缓存映像已被 payload 逐字节替换。
对目标调用 execve() 会加载被篡改的页面;磁盘上的 inode 仍然设置了 setuid root,因此内核会授予 root 凭证并运行该 payload。
`payload.c` 是纯粹的可移植 C 代码:`setgid(0); setuid(0); execve("/bin/sh", ...)`。nolibc 提供了 `_start`、系统调用机制以及针对各架构的寄存器处理。
第二个变体 `exploit-passwd.c` 会篡改 /etc/passwd 页缓存的四个字节,而不是 setuid 二进制文件的映像。它不需要内嵌 payload,并且适用于二进制文件篡改路径被阻塞的系统,但其提权兑现的攻击面要窄得多。
`vulnerable.c` 不是一个漏洞利用程序。它创建一个包含字符串 `init` 的本地 `testfile`,然后对该文件自身的页缓存运行相同的 `patch_chunk()` 原语,用 `vulnerable` 覆盖这些字节。如果回读的内容匹配,则当前运行的内核正处于 CVE-2026-31431 的影响窗口期内。磁盘上的 inode 从未被修改;`testfile` 在退出时被删除;页缓存的篡改也随之消散。以非特权身份运行。如果存在漏洞则退出码为 100,否则为 0。
## 构建
默认(主机架构原生):
```
make
```
交叉编译到 aarch64(或任何其他已安装交叉工具链的 Linux 架构):
```
make CC=aarch64-linux-gnu-gcc LD=aarch64-linux-gnu-ld
```
附带的 nolibc 支持的架构(根据上游):x86_64、i386、arm、aarch64、riscv32/64、mips、ppc、s390x、loongarch、m68k、sh、sparc。
nolibc 根据编译器的架构宏进行分派,因此只需选择正确的 `CC`/`LD` 即可。
构建所需:
* C 编译器(`cc`、`gcc` 或任何交叉变体)
* 支持 `ld -r -b binary` 的链接器(binutils ld 和 lld 都支持)
* 提供 `linux/if_alg.h` 和 `` 的内核 UAPI 头文件
(Debian/Ubuntu:`linux-libc-dev`;交叉变体:通常由交叉工具链包引入)
没有外部库依赖。payload 以 freestanding 模式基于 nolibc 构建;释放器仅为了 `fprintf` 和 `perror` 链接到主机的 libc。
## 架构选择
三个小型工具链特性在保持源代码可移植性和减小 payload 体积方面发挥了主要作用。
### nolibc
`nolibc/` 是内核的微型仅头文件 libc 替代品,从 torvalds/linux 的 `tools/include/nolibc/` 目录引入。它提供了 `_start`、可移植的 `syscall()` 宏以及内联系统调用包装器,并将各架构特定的寄存器约定编码在 `nolibc/arch-*.h` 中。使用 `-nostdlib -static -ffreestanding -Inolibc` 构建 payload 会生成一个微型静态 ELF 文件,该文件直接调用内核,而不会引入 glibc 启动代码、TLS 初始化或栈金丝雀机制。结果:在 x86_64 上约为 1.7 KB,在 aarch64 上约为 2.0 KB,而链接到 musl-static 的相同 `payload.c` 约为 17 KB,链接到 glibc-static 则约为 700 KB。
### `ld -r -b binary` 用于嵌入
Makefile 通过 `ld -r -b binary -o payload.o payload` 将构建好的 `payload` ELF 文件转换为 `payload.o`。链接器将输入字节逐字作为可重定位目标文件的数据段发出,并根据输入文件名合成三个符号:
```
_binary_payload_start address of first payload byte
_binary_payload_end address one past the last payload byte
_binary_payload_size absolute symbol whose value is the size in bytes
```
`exploit.c` 将前两个声明为 `extern const unsigned char[]`,并将大小计算为 `_binary_payload_end - _binary_payload_start`。
### `-Wl,-N` 加上紧凑的 `max-page-size`
payload 使用 `-Wl,-N -Wl,-z,max-page-size=0x10` 进行静态链接,这会将 `.text`/`.rodata`/`.data` 折叠到单个 LOAD 段中,文件对齐方式为 16 字节,而不是默认的按内核页对齐的每段 4 KB。这会从 `ld` 产生一个“RWX 权限”警告,但这仅供参考——payload 的运行时内存保护对其单一用途的程序来说无关紧要。如果没有此标志,相同的代码在 x86_64 上会链接到约 13 KB(主要是段间的零填充);使用此标志后,约为 1.7 KB。
## 变体与兑现 (Cashout) 可行性
本仓库提供了两个漏洞利用变体,它们共享 AF_ALG/splice 页缓存篡改原语,但以不同方式兑现为 root 权限执行。它们的可靠性特征并不等同,在分析现实世界的威胁模型时,这种差异很重要。
### 二进制文件篡改变体 (`exploit`)
使用内嵌的 payload 字节篡改目标 setuid 二进制文件的页缓存,然后执行该二进制文件。内核根据二进制文件未触及的磁盘上的 setuid 位授予 root 凭证,加载损坏的内存映像,并运行 payload。
只要攻击者能对系统上的任何 root-setuid 二进制文件执行 `open(target, O_RDONLY)`,该变体就能奏效。这种攻击基本会被将 setuid 二进制文件置于受限读取目录后的环境,以及无 setuid 设计的系统所挫败。
### /etc/passwd UID 翻转变体 (`exploit-passwd`)
篡改 /etc/passwd 页缓存的四个字节,将运行用户的 UID 字段设置为“0000”。在每个标准 Linux 系统上,/etc/passwd 都是全局可读的,因此这种篡改是普遍可行的。将其转化为 root 执行取决于某些 root 端进程是否通过 getpwnam/getpwuid 解析用户,并在没有交叉验证的情况下根据解析出的 uid 采取行动。存在许多此类消费者;其中许多会防御性地根据内核对调用 uid 的视图或磁盘上的文件所有权进行交叉核对,从而阻断了兑现。
#### 兑现可行性矩阵
| 兑现方式 | 是否需要预先的 root 设置 | 备注 |
|---|---|---|
| WSL2 会话生成 | 否 | WSL 每次会话的 `setuid(getpwnam(default_user)->pw_uid)` 不做验证。可以干净利落地工作。 |
| util-linux `su` | 否 | 宽松的调用者身份处理。 |
| shadow-utils `su` | 是 | `getpwuid(getuid())` 调用者身份检查会失败,因为篡改取消了对真实 uid 的映射。 |
| sshd(默认 `StrictModes yes`) | 是(需禁用 StrictModes) | StrictModes 要求主目录由 root 或 `pw->pw_uid` 拥有。篡改使得 pw_uid=0;磁盘所有者仍为原始 uid;不匹配导致认证拒绝。 |
| MTA 本地投递(postfix、exim 等) | 视情况而定 | 取决于 MDA 的主目录权限验证。需针对各 MTA 进行测试。 |
#### 在 `su` 失败后的枢轴转换
`exploit-passwd` 在篡改后执行 `su `,将其作为最简单的兑现方式。这对 util-linux `su` 有效,但对 shadow-utils `su` 会失败并提示“Cannot determine your user name.”。此时页缓存的篡改仍然存在,可以枢轴转换到任何其他兑现方式(例如,使用不经交叉核对、通过 getpwnam 解析用户的守护进程)。测试完成后,以 root 身份运行 `echo 3 > /proc/sys/vm/drop_caches` 以清除损坏的页缓存。
## 受影响的内核
```
floor: torvalds/linux 72548b093ee3 August 2017, v4.14
(AF_ALG iov_iter rework that
introduced the file-page write
primitive via splice into the AEAD
scatterlist)
ceiling: torvalds/linux a664bf3d603d April 2026, mainline
(reverts the 2017 algif_aead
in-place optimization; separates
source and destination scatterlists
so page-cache pages can no longer
be a writable crypto destination)
```
在此期间:每个没有向后移植修复程序的主要发行版内核都受影响。
Ubuntu、RHEL、SUSE、Amazon Linux 和 Debian 在公开披露时,其默认的云镜像内核均被确认存在漏洞。发行版级别的向后移植补丁大约在 2026-04-29 与公开披露的同时开始推出。要验证目标内核是否在窗口期内,请检查内核的 git log 或发行版的更新日志中是否存在 `a664bf3d603d`(或其发行版特定的向后移植版本)。
## 许可证与致谢
CVE-2026-31431 的发现与原始披露者:Theori / Xint。
公开报告: 。
此 C 语言移植版:Tony Gies
`nolibc/`:从 Linux 内核树引入,双重许可 LGPL-2.1-or-later OR MIT(参见 `nolibc/nolibc.h` 及各个文件的 SPDX 头信息)。
本仓库中的释放器和 payload 源代码在与其所依赖的 nolibc 树相同的双重 LGPL-2.1-or-later OR MIT 条款下发布,以使任何将整个目录引入到自己项目中的人都能保持许可的简单兼容性。
发布此漏洞利用和 payload 的目的是为了安全研究和防御性检测。如果您在未拥有或未获得明确授权测试的系统上使用它们,后果由您自行承担,与作者无关。
标签:AF_ALG, CVE-2026-31431, LPE, meg, nolibc, PoC, Proof-of-Concept, Splice, UID提权, Web报告查看器, Write-up, 二进制变异, 信息安全, 内核漏洞, 子域名枚举, 客户端加密, 数据展示, 文件覆盖, 暴力破解, 本地提权, 漏洞复现, 系统安全, 红队, 网络安全, 隐私保护