漏洞编号: CVE-2026-31431 别名: Copy Fail CVSS: 7.8 (High) 类型: Local Privilege Escalation (LPE) 影响范围: 所有主流 Linux 发行版(内核版本 ~2017-2026) 披露日期: 2026-04-29 发现者: Xint Code (Theori) 官方披露站: https://copy.fail/
目录
一、漏洞概述
Copy Fail 是存在于 Linux 内核加密子系统 algif_aead 模块中的一个逻辑缺陷。该漏洞位于三个子系统的交汇处:
-
AF_ALG — Linux 内核的用户态加密 socket 接口
-
splice()— 零拷贝数据传输系统调用 -
authencesn— 内核中的 AEAD(Authenticated Encryption with Associated Data)加密模板,带序列号支持
漏洞的核心问题:authencesn 模板中的 scratch-write 逻辑缺陷,当通过 AF_ALG socket 和 splice() 系统调用链式触发时,允许非特权本地用户向内核 page cache(页缓存) 写入 4 字节受控数据。
利用这一能力,攻击者可以篡改内存中的 SUID 二进制文件(如 /usr/bin/su)的 page cache 副本,从而在执行该 SUID 程序时获得 root 权限。值得注意的是:
-
磁盘上的文件不受影响,仅内存中的 page cache 副本被篡改
-
无需竞态条件(race condition),利用过程完全确定性
-
PoC 仅 732 字节,约 10 行 Python 代码即可获取 root
受影响版本: Linux 内核 4.14 ~ 6.19.11
不受影响版本(已修复):
| 内核分支 | 修复版本 |
|---|---|
| 5.10 | 5.10.254 |
| 5.15 | 5.15.204 |
| 6.1 | 6.1.170 |
| 6.6 | 6.6.137 |
| 6.12 | 6.12.85 |
| 6.18 | 6.18.22 |
| 6.19 | 6.19.12 |
| 7.0+ | 7.0 及以上 |
二、前置知识
2.1 AF_ALG — 内核加密用户态接口
Linux 内核通过 AF_ALG 地址族将内核加密框架暴露给用户态。用户态程序可以:
-
创建
AF_ALG类型的 socket:socket(AF_ALG, SOCK_SEQPACKET, 0) -
通过
bind()绑定到特定加密算法(如authencesn) -
通过
accept()创建实际的加密会话 -
使用
sendmsg()/recvmsg()或splice()进行数据传输和加密/解密操作
1 | // 典型的 AF_ALG 使用模式 |
2.2 splice() 系统调用
splice() 是 Linux 提供的零拷贝数据传输机制,可以在内核空间直接在两个文件描述符之间移动数据,无需将数据拷贝到用户态:
1 | long splice(int fd_in, loff_t *off_in, int fd_out, loff_t *off_out, |
关键特性:splice() 操作的页面直接来自 page cache,数据在内核中完成传输。
2.3 Page Cache(页缓存)
Linux 内核将磁盘文件的内容缓存在内存中,称为 page cache。当程序读取文件时:
-
内核首先检查 page cache 中是否已有该文件的页面
-
如有,直接返回(避免磁盘 I/O)
-
如无,从磁盘读取并缓存
关键安全属性: 对于同一个文件,内核通常维护单一 page cache 实例。所有进程共享同一份 page cache 数据。这意味着如果一个进程修改了 page cache 中的页面内容,所有其他进程看到的都是被修改后的数据。
2.4 AEAD 与 in-place 操作
AEAD(Authenticated Encryption with Associated Data)同时提供加密和认证。
-
Out-of-place 操作: 加密/解密时,输入和输出使用不同的缓冲区,互不影响
-
In-place 操作: 加密/解密直接在输入缓冲区上进行,输出覆盖输入(性能优化)
2017 年的 commit 72548b093ee3 将 algif_aead 的 AEAD 操作从 out-of-place 切换为 in-place,正是这一"优化"引入了漏洞。
2.5 SUID 机制
Linux 中,设置了 SUID 位的可执行文件在执行时会以文件所有者的权限运行。例如 /usr/bin/su 的所有者是 root,设置了 SUID 位,因此任何用户执行 su 时,进程都以 root 权限运行。
三、漏洞根因深度分析
3.1 三个子系统的交汇
漏洞发生在 AF_ALG、splice() 和 authencesn 三个子系统的交互处:
1 | 用户态攻击者 |
3.2 根因:In-place AEAD + splice 零拷贝的组合效应
当 splice() 将数据传递给 AF_ALG socket 时:
-
splice() 不拷贝数据,而是传递 page cache 页面的引用
-
algif_aead 在 in-place 模式下,AEAD 操作的输出直接覆盖输入缓冲区
-
authencesn 模板在处理过程中有一个 scratch buffer 的写入逻辑,在特定条件下(构造的输入),这个写入会直接作用于传入的 page cache 页面
问题在于:splice() 传递的页面可能属于任何文件在 page cache 中的映射。通过精心构造操作序列,攻击者可以使 authencesn 的 scratch-write 操作作用于目标文件(如 /usr/bin/su)在 page cache 中的物理页面。
3.3 攻击能力
通过此漏洞,攻击者获得:
-
4 字节任意写入到 page cache 中的页面
-
写入内容受攻击者控制
-
写入位置在页面内的偏移受攻击者控制
-
完全确定性,无竞态条件
四、漏洞复现
4.1 环境检查
使用虚拟机,切勿在生产环境测试。以下以 Kali Linux 为例:
1 | # 确认内核版本(需为未修补版本,约 4.10 ~ 6.19 早期) |
4.2 创建测试用户
1 | # 以 root 创建普通用户 |
4.3 运行 Exp
1 | # 克隆 PoC |
PoC 执行流程:打开 /usr/bin/su → 循环调用 c(f, offset, 4字节payload) 向 su 的 page cache 写入 shellcode → 执行被篡改的 su → root shell。整个过程无需竞态,完全确定性。
4.4 容器逃逸验证
page cache 按 inode 索引。容器内的 /usr/bin/su 来自 OverlayFS 镜像层,与宿主机的 /usr/bin/su 是不同的 inode、不同的 page cache 条目。因此:
-
同镜像容器之间可以互相提权(共享 OverlayFS lower layer,同一 inode)
-
容器直接逃逸到宿主机不行,除非挂载了宿主机文件系统
同镜像容器交叉提权
两个使用相同 base image 的容器,OverlayFS lower layer 中的文件是同一个 inode,共享 page cache:
1 | # 终端 1:启动容器 A |
1 | # 终端 2:启动容器 B(相同 image) |
1 | ┌──────────────────────────────────────────────┐ |
挂载宿主机文件系统实现逃逸
通过 -v /:/host 挂载宿主机根目录后,/host/usr/bin/su 与宿主机的 /usr/bin/su 是同一个 inode,共享 page cache。PoC 只需改两处路径:
1 | # 原始 |
验证步骤:
1 | # 宿主机:启动容器,挂载根文件系统 |
为什么需要
chroot: 执行/host/usr/bin/su拿到的 shell 虽然 UID=0,但仍在容器的 namespace 里(PID、mount、network 隔离)。通过chroot /host切换根文件系统到宿主机,才是真正的宿主机环境。也可以用nsenter -t 1 -m -u -i -n -p -- /bin/bash直接进入宿主机 PID 1 的 namespace。
总结:
| 场景 | 是否可行 | 原因 |
|---|---|---|
| 容器内普通提权 | 可以 | 篡改容器内 su 的 page cache |
| 同镜像容器交叉提权 | 可以 | OverlayFS lower layer 共享 inode |
| 容器 → 宿主机逃逸 | 不可以 | inode 不同,page cache 隔离 |
| 容器 → 宿主机(挂载宿主机 FS) | 可以 | 通过挂载点访问宿主机 inode |
五、内核源码级分析
5.1 漏洞引入:commit 72548b093ee3
2017 年的 commit 72548b093ee3 对 crypto/algif_aead.c 进行了修改,将 AEAD 操作从 out-of-place 切换为 in-place:
1 | // crypto/algif_aead.c — 修改前(out-of-place,安全) |
5.2 algif_aead.c 中的关键数据流
1 | 用户态 splice() |
5.3 authencesn scratch-write 机制
authencesn 是一个组合加密模板,通常形式为 authencesn(authenc(hmac(sha256), cbc(aes)))。其处理流程:
-
认证阶段: HMAC 计算认证标签
-
加密/解密阶段: CBC 模式 AES 加密/解密
-
Scratch buffer: 在处理过程中,需要一个临时缓冲区来保存中间结果
在 in-place 模式下,这个 scratch buffer 的写入目标不是独立的内核缓冲区,而是直接在输入页面上进行。当输入页面来自 splice() 传递的 page cache 页面时,这个写入就直接修改了 page cache。
5.4 splice() 的零拷贝语义与 page cache 关联
1 | // splice() 将 pipe 中的页面直接传递给目标 fd |
5.5 漏洞利用的内存布局
1 | 物理内存 |
5.6 为什么只有 4 字节可控
authencesn 的 scratch-write 操作在 AEAD 处理过程中只会向特定位置写入有限的字节数。通过精确控制:
-
AEAD 输入的长度和对齐
-
加密算法的参数(密钥、IV)
-
认证标签的位置
攻击者可以精确控制这 4 字节的内容和在页面内的偏移。4 字节虽然不多,但足以:
-
在 x86-64 上写入一条
jmp或call指令(5 字节以内的短跳转,结合对齐可以做到 4 字节) -
修改函数指针
-
修改 GOT 表项(如果二进制不是完全 RELRO)
六、补丁分析
6.1 修复方案
修复方案是回退 in-place AEAD 操作,恢复到安全的 out-of-place 模式:
-
上游修复 commit: 在
git.kernel.org的torvalds/linux仓库中 -
相关 stable kernel commit:
a664bf3d603d/fafe0fa2995a
核心变更:
1 | // crypto/algif_aead.c — 补丁后 |
6.2 补丁验证
1 | # 检查内核是否已修补 |
七、检测与防御
7.1 临时缓解措施
如果无法立即升级内核,可以禁用 algif_aead 模块:
1 | # 方法 1: 禁用模块加载 |
7.2 检测利用行为
可以利用以下方式检测 Copy Fail 的利用尝试:
1 | # 1. 监控 AF_ALG socket 创建(auditd 规则) |
7.3 容器环境防护
1 | # 1. 限制容器的 AF_ALG 访问(seccomp profile) |
7.4 长期建议
-
及时更新内核 — 订阅发行版的安全公告,尽快应用安全补丁
-
最小权限原则 — 限制不必要的本地用户访问
-
考虑禁用 AF_ALG — 如果业务不依赖内核加密用户态接口,建议禁用。社区对此接口的攻击面有持续争议
-
容器加固 — 使用非特权容器、seccomp profile、只读根文件系统

