732 字节。任何发行版。Root。
Linux 内核
authencesn加密模板中的一个直线逻辑缺陷,使无特权的本地用户能够对任何可读文件(包括 setuid 二进制文件)的页缓存执行精确、受控的 4 字节写入。无需竞争条件。无需重试。无需重新编译。自 2017 年以来发布的每个主流 Linux 发行版均可获得 root 权限。
📄 技术分析文章 · 🔗 内核补丁 · 🛡️ CVSS:严重
| 发行版 | 内核版本 |
|---|---|
| Ubuntu 24.04 LTS | 6.17.0-1007-aws |
| Amazon Linux 2023 | 6.18.8-9.213.amzn2023 |
| RHEL 10.1 | 6.12.0-124.45.1.el10_1 |
| SUSE 16 | 6.12.0-160000.9-default |
以上四个发行版均使用相同的 732 字节 Python 脚本成功获取 root 权限,无需任何修改。
AF_ALG 将内核的加密子系统暴露给无特权的用户空间。splice() 通过引用将文件数据传输到管道中 — 直接传递页缓存页面,无需复制。当用户将文件 splice 到 AF_ALG AEAD 套接字时,套接字的输入散列表持有该文件内核缓存页面的实时引用。
在 algif_aead.c 中,2017 年的就地优化将 AAD 和密文从 TX 散列表复制到 RX 缓冲区,但通过 sg_chain() 按引用链接了认证标签页面,然后设置 req->src = req->dst:
输入 SGL: [ AAD | CT | Tag ]
^
└─ sg_chain() → 仍指向页缓存页面
输出 SGL: [ AAD | CT ] ──→ [ Tag(页缓存页面)]
(RX 缓冲区) (从 TX SGL 链接)
req->src ──┐
├──→ 相同的组合散列表
req->dst ──┘
来自 splice() 的页缓存页面现在位于可写目标散列表中,仅通过偏移边界与合法写入区域分隔。API 中没有任何机制强制算法必须保持在边界内。
authencesn 的越界临时写入authencesn 是 IPsec 用于 64 位扩展序列号(ESN)支持的 AEAD 包装器。为了重新排列 ESN 字节以进行 HMAC 计算,它使用调用者的目标缓冲区作为临时空间 — 包括在 assoclen + cryptlen 偏移处的写入,该偏移位于认证标签边界之外:
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 字节(seqno_lo)。在就地 AF_ALG 路径中,scatterwalk 从 RX 缓冲区跨越到链接的页缓存标签页面。内核通过 kmap_local_page 映射页缓存页面,并直接写入目标文件的缓存副本。
随后 HMAC 失败(密文是伪造的),recvmsg() 返回错误 — 但 4 字节写入永久保留。
没有任何单一变更本身是错误的。该漏洞存在于三者交汇之处。
默认目标是 /usr/bin/su,一个存在于所有已测试发行版上的 setuid-root 二进制文件。
步骤 1 — 套接字设置
打开 AF_ALG 套接字,绑定到 authencesn(hmac(sha256),cbc(aes))
设置密钥。接受请求套接字。(无需任何特权。)
步骤 2 — 写入循环(每个 4 字节 shellcode 块执行一次)
sendmsg() → AAD 字节 [4:8] 携带要写入的 4 字节(seqno_lo)
splice() → 将目标文件的页缓存页面传入 AF_ALG 套接字
recv() → 触发解密 → authencesn 将 seqno_lo 写入页缓存
(recvmsg 返回错误;写入持久保留)
步骤 3 — 执行
execve("/usr/bin/su")
内核从(现已损坏的)页缓存加载二进制文件
Setuid-root 二进制文件执行注入的 shellcode → UID 0
a = socket.socket(38, 5, 0) # AF_ALG, SOCK_SEQPACKET
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
# ... 设置密钥,接受请求套接字 u ...
u.sendmsg([b"A"*4 + payload_chunk], [cmsg_headers], MSG_MORE)
os.splice(target_fd, pipe_wr, offset)
os.splice(pipe_rd, alg_fd, offset)
u.recv(...) # 触发页缓存写入
更新到包含补丁 a664bf3d603d的内核。该修复将 algif_aead.c 恢复为异地操作:req->src 指向 TX SGL;req->dst 指向 RX 缓冲区。来自 splice() 的页缓存页面保持只读。将它们链接到可写目标的 sg_chain() 机制已被移除。
// 之前(易受攻击):src 和 dst 共享相同的散列表
aead_request_set_crypt(&areq->cra_u.aead_req, rsgl_src, rsgl_src, used, ctx->iv);
// 之后(已修复):src 为 TX SGL,dst 为 RX 缓冲区 — 完全分离
aead_request_set_crypt(&areq->cra_u.aead_req, tsgl_src, rsgl_dst, used, ctx->iv);
禁用 algif_aead 内核模块:
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
rmmod algif_aead 2>/dev/null
或者通过工作负载配置文件中的 seccomp 策略阻止 AF_ALG 套接字创建。
容器环境注意事项: 由于页缓存跨主机共享,此漏洞可跨越容器边界。请在节点级别应用缓解措施,而不仅仅是每个 Pod。完整的 Kubernetes 逃逸详情请参见第 2 部分。
Theori 研究员 Taeyang Lee 通过之前的内核 CTF 工作发现,AF_ALG + splice() 创建了一条路径,使无特权的用户空间能够将页缓存页面直接送入加密子系统 — 并且散列表页面来源是一个未被充分探索的漏洞类别。
研究团队使用 Xint Code 将这一洞察扩展到整个 crypto/ 子系统,使用了以下操作员提示:
"这是 Linux crypto/ 子系统。请检查所有可从用户空间系统调用到达的代码路径。注意一个关键观察:splice() 可以将只读文件(包括 setuid 二进制文件)的页缓存引用传递到加密 TX 散列表中。"
经过大约一小时的自动化分析,Copy Fail 是严重性最高的输出。在同一扫描中发现的其他漏洞仍在协调披露中。
第 2 部分:从 Pod 到宿主机 — Copy Fail 如何逃逸每个主流云 Kubernetes 平台。即将推出。
| 属性 | 详情 |
|---|
| 确定性 | 直线逻辑缺陷 — 无竞争条件、无时间窗口、无重试 |
| 可移植性 | 相同脚本、相同字节,适用于所有已测试的发行版和架构 |
| 极小 | 732 字节 Python 脚本,仅使用标准库(os、socket、zlib)。需要 Python 3.10+ 以支持 os.splice |
| 隐蔽性 | 被破坏的页面永远不会被标记为脏。磁盘上的校验和不变;仅修改内存中的页缓存 |
| 跨容器 | 页缓存在整个系统中跨容器边界共享 — 这也是一个 Kubernetes 节点逃逸原语(参见第 2 部分) |
| 变量 | 控制方式 |
|---|
| 目标文件 | 当前用户可读的任何文件 |
| 写入偏移 | assoclen、splice 偏移和 splice 长度 |
| 写入值 | sendmsg() 中提供的 AAD 字节 4–7(seqno_lo) |
| 年份 | 事件 |
|---|
| 2011 | authencesn 被添加到内核(a5079d084f8b),用于 IPsec ESN 支持。临时写入存在但无害 — 只有内部 xfrm 层调用它,且 AAD 位于单独的散列表中。 |
| 2015 | AF_ALG 获得 AEAD 支持。authencesn 转换为新的 AEAD 接口(104880a6b470),引入了 assoclen + cryptlen 写入偏移。仍为异地操作:页缓存页面位于 src(只读)。尚不可利用。 |
| 2017 | 在 algif_aead.c 中添加就地优化(72548b093ee3)。req->src = req->dst。页缓存标签页面被链接到可写目标中。漏洞形成。 |
| 2026-03-23 | 报告给 Linux 内核安全团队。 |
| 2026-04-01 | 补丁合并到主线。 |
| 2026-04-22 | 分配 CVE-2026-31431。 |
| 2026-04-29 | 公开披露。 |
| 日期 | 事件 |
|---|
| 2026-03-23 | 向 Linux 内核安全团队报告漏洞 |
| 2026-03-24 | 收到初步确认 |
| 2026-03-25 | 提出并审查补丁 |
| 2026-04-01 | 补丁提交到主线内核 |
| 2026-04-22 | 分配 CVE-2026-31431 |
| 2026-04-29 | 公开披露 |