Linux CopyFail CVE-2026-31431 深度剖析:内核加密子系统逻辑缺陷与 732 字节提权脚本
2026 年 3 月 23 日,Theori 研究员 Taeyang Lee 向 Linux 内核安全团队提交了一份编号为 CVE-2026-31431 的漏洞报告,代号 "Copy Fail"。这不是一个传统的内存破坏漏洞——它没有缓冲区溢出,没有 Use-After-Free,没有竞态条件。它是一个逻辑缺陷:三个独立且各自合理的内核设计决策,在特定组合下产生了致命后果。
更令人警醒的是,这个漏洞位于 Linux 内核的 crypto 子系统——一个通常被视为"成熟、稳定、安全"的核心模块。攻击者利用它可实现 100% 稳定的本地权限提升,整个利用过程仅需 732 字节的 Python 标准库脚本。
漏洞基本信息
| 项目 | 详情 |
|---|---|
| CVE 编号 | CVE-2026-31431 |
| 漏洞代号 | Copy Fail |
| CVSS 评分 | 7.8(高危) |
| 漏洞类型 | 逻辑缺陷型漏洞(非内存破坏) |
| 漏洞位置 | Linux 内核 crypto 子系统 authencesn 模板 |
| 影响范围 | Linux Kernel 4.14+,覆盖 Ubuntu 24.04 LTS、RHEL 8/9/10、Amazon Linux 2023、SUSE 16 等 |
| 潜伏时间 | 超过 8 年(根因可追溯至 2017 年 1 月的 commit 72548b093ee3) |
| 利用效果 | 100% 稳定的本地权限提升(LPE) |
| PoC 大小 | 732 字节 Python(仅标准库) |
| 报告时间 | 2026 年 3 月 23 日 |
| 修复补丁 | commit a664bf3d603d,2026 年 4 月 1 日合入主分支 |
技术背景:三个独立合理的设计
CopyFail 是一个典型的**"涌现性漏洞"(emergent vulnerability)**——源于三个独立且各自合理的内核设计决策的叠加:
| 时间 | 代码变更 | 设计意图 | 单独审查的合理性 |
|---|---|---|---|
| 2011 年 | authencesn 算法实现 | 实现 AEAD-ESN 加密规范 | 借用目标缓冲区作临时草稿,符合 AEAD 规范 |
| 2015 年 | AF_ALG 接口向非特权用户开放 | 用户态加密加速 | 引入 splice 零拷贝支持,标准性能优化 |
| 2017 年 | commit 72548b093ee3 | in-place 解密优化 | 局部性能优化,减少内存拷贝 |
这三个变更,单独看都是正确的。但组合在一起,就产生了一个致命的语义断裂。
漏洞原理:splice() 与 AF_ALG 的致命交互
核心假设断裂
CopyFail 的本质是 scatterlist 页面来源属性与可写性之间的安全假设断裂:
┌─────────────────────────────────────────────────────────────┐
│ 漏洞核心机制 │
│ │
│ splice() 零拷贝引用 │
│ │ │
│ ▼ │
│ 只读文件的页缓存页面 ──引用──▶ 内核加密子系统 │
│ │ │ │
│ │ 页面属性:只读 │ in-place 优化 │
│ │ ▼ │
│ │ 页面链入 destination scatterlist │
│ │ │ │
│ │ ▼ 标记为可写 │
│ │ authencesn 临时写入 4 字节 │
│ │ │ │
│ │ ▼ │
│ └────────────────────── 页缓存被篡改!⚠️ │
│ │
│ 结果:只读文件在磁盘上的内容被修改(通过页缓存) │
│ │
└─────────────────────────────────────────────────────────────┘攻击链详解
第一步:splice() 零拷贝引用
splice() 系统调用将只读文件的页缓存页面以引用方式直接传入内核子系统。此时页面属性为只读,内核设计者假设这些页面不会被修改。
第二步:in-place 优化改变语义
2017 年的 in-place 优化(commit 72548b093ee3)将页缓存页面链入可写的 destination scatterlist。页缓存从"只读输入"变为"可写输出"——这是一个语义上的根本性转变,但优化代码没有检查页面来源。
第三步:authencesn 临时写入
authencesn 模板在解密时进行 HMAC 验证,需要向缓冲区写入 4 字节临时数据(scratch write)。由于 scatterlist 被标记为可写,这 4 字节的临时写入实际篡改了页缓存——实现了对页缓存的未授权修改。
第四步:页缓存回写
被篡改的页缓存最终会回写到磁盘。由于页缓存对应的是只读文件(如 setuid 二进制文件),攻击者通过修改该文件的页缓存,可以在不触发任何权限检查的情况下,改变可执行文件的内容。
为什么不是内存破坏
这是 CopyFail 最独特的地方——它不属于任何传统漏洞类型:
| 特征 | 传统内存破坏 | CopyFail |
|---|---|---|
| 写入合法性 | 越界写入触发异常 | 写入在底层完全"合法"——scatterlist 标记为可写 |
| 检测信号 | ASan/KASan 可监测 | 不产生内存越界异常信号 |
| 漏洞模式 | 匹配已知危险模式 | 不在任何已知模式库中 |
这意味着:所有传统的漏洞检测工具对此类漏洞完全无效。
| 检测方法 | 失效原因 |
|---|---|
| 模糊测试(Fuzzing) | 难以构造跨越 crypto、VFS 和 splice 三个子系统的精确输入;不产生异常信号 |
| 内存安全检测(ASan/KASan) | 不涉及内存破坏,写入语义上"合法" |
| 静态分析(规则型) | 无法识别 splice() 页缓存引用与 authencesn 临时写入的跨子系统隐含关联 |
| 传统 SAST 工具 | AST 遍历和污点分析粒度限于单函数或文件,难以追踪跨文件数据流 |
| 人工代码审查 | crypto 子系统逾数十万行,难以覆盖全部跨子系统路径 |
学术界将传统工具无法覆盖的空间称为**"语义鸿沟"(semantic gap)**——CopyFail 正位于该鸿沟的核心。
利用方式:732 字节的 Python 脚本
利用原理
利用 CopyFail 的核心思路是:
- 选择一个setuid 二进制文件(如
/usr/bin/passwd)作为目标 - 通过 splice() + AF_ALG 的组合,修改该文件的页缓存
- 将 setuid 文件的内容替换为攻击者控制的 shellcode
- 执行被修改的 setuid 文件,获得 root 权限
Python PoC 构造
以下是一个简化原理示意的 Python 脚本(非完整利用代码,完整版本 732 字节):
#!/usr/bin/env python3
"""
CopyFail (CVE-2026-31431) 原理演示 PoC
仅用于安全研究和补丁验证,禁止非法使用
完整利用代码仅 732 字节,使用 Python 标准库
"""
import socket
import os
import mmap
# 目标:一个 setuid 二进制文件
TARGET = b"/usr/bin/passwd"
# 创建 AF_ALG 套接字,绑定 authencesn 算法
alg = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
alg.bind(("aead", "authencesn(aes,sha256)"))
# 设置密钥和 IV(任意值均可,利用的是解密流程而非加密强度)
alg.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b"A" * 32)
alg.setsockopt(socket.SOL_ALG, socket.ALG_SET_IV, b"B" * 16)
alg.accept()
# 打开目标文件(只读)
fd = os.open(TARGET, os.O_RDONLY)
# 创建管道用于 splice()
r, w = os.pipe()
# 将文件内容通过 splice() 零拷贝传入 AF_ALG
# 页缓存页面被引用(只读属性)
os.sendfile(w, fd, 0, 4096)
# 通过 splice() 将管道数据传入 AF_ALG 套接字
# 关键:页缓存页面被链入可写 scatterlist
# authencesn 的 4 字节 scratch write 篡改了页缓存!
# 此处省略加密参数设置的具体代码...
# 结果:/usr/bin/passwd 的页缓存被修改
# 执行该文件即可获得 root 权限
# os.system(TARGET.decode()) # 实际利用中执行被修改的 setuid 文件
print("[+] 页缓存已篡改(原理演示)")
print("[!] 实际利用中,此处会执行被修改的 setuid 文件获得 root")完整利用的关键细节
完整的 732 字节 PoC 需要精确控制以下参数:
- 加密参数:密钥长度、IV 长度、认证标签长度必须匹配 authencesn 模板的要求
- 数据对齐:splice() 传输的数据大小需要与页大小(4KB)对齐
- 目标选择:优先选择不频繁使用的 setuid 文件,避免页缓存被其他进程频繁刷新
- 回写时机:需要确保页缓存被修改后,在目标文件执行前不会被其他操作刷新
容器逃逸风险
在云原生环境中,CopyFail 的威胁被进一步放大:
页缓存在宿主机与容器间共享。容器内的无特权用户利用 CopyFail 篡改页缓存,影响的是宿主机的全局页缓存。这意味着容器内的普通用户可以通过修改宿主机的 setuid 文件,实现容器逃逸。
Theori 的披露分两部分:
- Part 1:本地提权(LPE)
- Part 2:容器逃逸(Container Escape)
云安全团队应将 CopyFail 的容器逃逸利用路径纳入风险评估。
检测与防御
缓解方案(补丁前)
在官方补丁全面部署前,可采用以下缓解措施:
方案 1:禁用 AF_ALG aead 接口(影响用户态加密加速)
# 临时禁用 algif_aead 模块
sudo modprobe -r algif_aead
sudo echo "blacklist algif_aead" > /etc/modprobe.d/copyfail-mitigation.conf方案 2:使用 seccomp 限制 AF_ALG 套接字创建(推荐)
// seccomp 策略:禁止非特权用户创建 AF_ALG 套接字
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["socket"],
"action": "SCMP_ACT_ERRNO",
"args": [
{
"index": 0,
"value": 38,
"op": "SCMP_CMP_EQ",
"valueTwo": 0
}
]
}
]
}注意:
38是AF_ALG的值(socket.h中定义)。此策略会阻止非特权进程创建 AF_ALG 套接字,但可能影响依赖用户态加密加速的应用(如 OpenSSL 的/dev/crypto引擎)。
方案 3:容器环境加固
# Kubernetes SecurityContext:限制 capabilities
securityContext:
capabilities:
drop:
- ALL # 删除所有 capabilities,包括 CAP_NET_RAW 等
seccompProfile:
type: RuntimeDefault # 使用默认 seccomp 策略
allowPrivilegeEscalation: false检测方案
方案 1:监控 AF_ALG + splice 的组合调用
# 使用 eBPF 或 auditd 监控
# 检测进程同时调用 socket(AF_ALG) 和 splice() 的模式
# auditd 规则示例(简化)
-a always,exit -F arch=b64 -S socket -F a0=38 -k af_alg_socket
-a always,exit -F arch=b64 -S splice -k splice_call方案 2:页缓存完整性监控
#!/usr/bin/env python3
"""
CopyFail 检测脚本:监控 setuid 文件的页缓存与磁盘内容是否一致
"""
import os
import hashlib
SETUID_BINS = [
"/usr/bin/passwd",
"/usr/bin/sudo",
"/usr/bin/su",
"/usr/bin/chsh",
"/usr/bin/chfn",
"/usr/bin/newgrp",
]
def get_pagecache_hash(path):
"""通过 mmap 读取页缓存内容(绕过磁盘读取)"""
with open(path, 'rb') as f:
# mmap 读取的是页缓存,而非磁盘
mm = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
data = mm.read(4096) # 读取第一页
mm.close()
return hashlib.sha256(data).hexdigest()
def get_disk_hash(path):
"""通过 direct I/O 读取磁盘内容(绕过页缓存)"""
# 使用 O_DIRECT 绕过页缓存
fd = os.open(path, os.O_RDONLY | os.O_DIRECT)
try:
# 注意:O_DIRECT 需要扇区对齐的缓冲区
data = os.read(fd, 4096)
finally:
os.close(fd)
return hashlib.sha256(data).hexdigest()
def check_integrity():
for path in SETUID_BINS:
if not os.path.exists(path):
continue
try:
pagecache_hash = get_pagecache_hash(path)
# 简化:实际应使用 O_DIRECT 读取磁盘
# 此处用 stat + md5sum 作为替代
disk_hash = os.popen(f"md5sum {path}").read().split()[0]
if pagecache_hash != disk_hash:
print(f"[ALERT] {path}: 页缓存与磁盘内容不一致!")
print(f" 可能遭受 CopyFail 攻击")
except Exception as e:
print(f"[ERROR] 检查 {path} 失败: {e}")
if __name__ == "__main__":
check_integrity()注意:上述检测脚本为原理示意,实际生产环境应使用更精确的 direct I/O 实现,并考虑性能影响。
补丁验证
# 检查内核是否已修复
grep -r "a664bf3d603d" /proc/version
grep "CVE-2026-31431" /proc/version # 部分发行版会标记
# 或检查内核版本
uname -r
# 修复版本:
# Linux 6.14.1+
# Linux 6.13.8+
# Linux 6.12.18+
# Linux 6.6.64+ LTS
# Linux 6.1.130+ LTS
# Linux 5.15.178+ LTS
# Linux 5.10.234+ LTS
# Linux 4.19.325+ LTS
# Linux 4.14.340+ LTS
# 运行 PoC 验证补丁(在测试环境)
python3 copyfail-poc.py
# 修复后的内核应返回权限错误或无效果历史脉络:页缓存漏洞的十年演进
CopyFail 的发现建立在安全社区对页缓存漏洞模式十年成熟认知的基础上:
| 时间 | 漏洞 | 核心机制 | 贡献 |
|---|---|---|---|
| 2016 | Dirty COW (CVE-2016-5195) | 竞态条件写时复制篡改页缓存 | 揭示页缓存可篡改 |
| 2022 | Dirty Pipe (CVE-2022-0847) | 管道缓冲区注入页缓存 | 展示非文件系统路径篡改页缓存 |
| 2026 | CopyFail (CVE-2026-31431) | splice() 引用 + 加密子系统写入 | 跨子系统语义断裂 |
三个漏洞的演进揭示了页缓存攻击的成熟化:从竞态条件(不稳定)到管道注入(需特定条件)再到加密子系统组合(100% 稳定)。
人与 AI 协同的发现范式
CopyFail 的发现过程代表了安全研究的新范式:
| 阶段 | 执行者 | 职能 | 耗时 |
|---|---|---|---|
| 攻击面识别 | 人类研究员 (Taeyang Lee) | 基于 kernelCTF 经验提出核心假设:"AF_ALG + splice = 直通加密子系统的后门" | 数小时 |
| 规模化扫描 | Xint Code AI 平台 | 跨文件深度关联分析,遍历 algif_aead 与全部加密模板交互路径 | ~1 小时 |
| 结果验证 | 人类 + AI | 精准定位 authencesn 逻辑缺陷,验证 PoC | 数小时 |
核心洞察:
人类研究员的方向设定(识别 AF_ALG + splice 组合攻击面) + AI 平台的规模执行(1 小时完成跨文件深度扫描) = 发现传统工具无法触及的"语义鸿沟"漏洞。
Carnegie Mellon 的 Brumley 教授指出:这种跨子系统语义推理能力的突破表明,发现深层逻辑缺陷的成本可能已下降约一个数量级。
总结
CopyFail(CVE-2026-31431)是一个教科书级的跨子系统逻辑缺陷:
- 位置:内核 crypto 子系统 authencesn 模板
- 本质:三个独立合理设计(2011 authencesn + 2015 splice + 2017 in-place)的组合爆炸
- 类型:逻辑缺陷而非内存破坏,不在任何已知漏洞模式库中
- 利用:732 字节 Python 标准库脚本,100% 稳定 LPE
- 云原生风险:页缓存共享导致容器逃逸
- 发现范式:人类方向设定 + AI 规模执行的协同
对防御者的核心启示:
- ⚠️ 传统安全工具对此类漏洞完全无效——需要重新审视跨子系统交互的安全性
- ⚠️ 页缓存仍然是 Linux 内核的脆弱面——从 Dirty COW 到 Dirty Pipe 再到 CopyFail,十年了
- ⚠️ 云环境的隔离假设需要加固——页缓存共享打破了容器与宿主机的边界
- ✅ 立即升级内核——补丁已合入所有 LTS 分支
- ✅ 容器环境启用 seccomp——限制 AF_ALG 套接字创建是最小侵入的缓解
参考资源
- Theori / Xint Code 披露报告 — 人与 AI 协同的漏洞分析新范式
- Linux 内核 commit a664bf3d603d — 官方修复补丁
- Linux 内核 commit 72548b093ee3 — 引入 in-place 优化的原始提交
- CVE-2026-31431 MITRE 记录 — 官方 CVE 信息
- kernelCTF — Taeyang Lee 的经验来源
免责声明:本文仅用于安全研究和防御目的。未经授权利用漏洞攻击他人系统是违法行为。请在合法授权的环境中进行安全测试。完整 PoC 代码未公开,本文仅提供原理分析和检测方法。

