CVE-2026-31431(Copy Fail)漏洞FAQ(下)——漏洞技术机理、历史坐标与战略启示篇

时间 :  2026年05月01日

声明:本文是基于安天CERT工程师构建FAQ清单,基于多个智能体的协同完成,同时对部分内容进行了人工修订和改写。虽然我们已对信息进行人工复核,但基于其中信息量较大,AI生成内容可能存在事实偏差或时效性误差,技术验证无法短期完成,建议读者交叉验证。如发现有错误或质疑,请直接留言,我们验证后会对网站版FAQ继续修订。
编者按
2026年4月29日,韩国安全公司Theori及其AI安全研究平台Xint公开披露了Linux内核本地权限提升漏洞CVE-2026-31431,代号为"Copy Fail"。安天CERT正在密切跟踪和响应该漏洞。

该漏洞存在于Linux内核加密子系统中,影响范围覆盖2017年至今几乎所有主流Linux发行版。攻击者仅需本地普通用户权限,利用一个不足800字节的Python脚本,即可在无竞争条件下稳定获取root权限,该漏洞通过污染page cache中的setuid程序实现执行劫持,并在特定环境配置下具备容器逃逸潜力。

安天CERT编制本FAQ旨在为不同受众提供精准、可操作的威胁认知与处置指引。
本文结构说明:
■上篇:主要面向公众、网络管理者、网络用户、行业主管部门及企业IT决策者。侧重漏洞的基本认知、影响范围判断、资产排查、修复缓解措施与威胁态势评估,行文力求通俗准确,可直接用于内部通报与管理汇报作为参考资料。
■下篇(本文):面向安全研究者、分析工程师、内核开发者及高级蓝队人员。侧重漏洞的底层技术机理、利用链拆解、与历史漏洞的对比分析,以及对Linux内核安全审计机制的战略反思,技术深度较高。

两篇内容互补,引用来源一致,均基于公开披露信息、oss-security邮件列表、上游内核补丁及社区实测反馈进行交叉验证。

一、漏洞技术机理与根因

Q1:该漏洞涉及Linux内核的哪些具体子系统或机制(AF_ALG、splice、页缓存等)?

A:CVE-2026-31431是一个典型的跨子系统emergent vulnerability,涉及五个独立内核子系统的异常耦合:

图片

图注:CVE-2026-31431(Copy Fail)技术架构图

跨子系统耦合型emergent vulnerability。红色箭头表示攻击路径,从用户态 Python PoC 经系统调用层(socket/splice/sendmsg)穿透至内核子系统,最终通过 algif_aead 的 in-place 优化缺陷向页缓存写入 payload,实现 root 提权。

各子系统角色:

子系统 核心机制 在漏洞中的角色
AF_ALG 用户态加密接口(socket地址族) 向非特权用户暴露内核scatterlist操作能力,提供从用户态到内核加密子系统的合法路径
algif_aead AEAD算法接口模块 2017年引入的in-place优化导致req->src == req->dst,破坏了源/目标缓冲区隔离
authencesn AEAD算法实现 执行AEAD解密时,向destination scatterlist写入AAD的seqno_lo字段(4字节)
splice() 零拷贝数据传输 将页缓存页面的引用(而非副本)直接传入AF_ALG socket,保持页面的"页缓存身份"
Page Cache 文件页缓存 被错误地放入可写scatterlist,成为攻击者的写入目标

Q2:AF_ALG加密接口在该漏洞利用链条中扮演何种角色?

A:AF_ALG是漏洞利用的入口点(Entry Point)和能力放大器(Capability Amplifier)。

AF_ALG基础:

  • AF_ALG(Address Family ALGorithm)是Linux内核于2011年引入的套接字地址族,允许用户态程序通过标准BSD socket API调用内核crypto API。
  • 支持的算法类型:skcipher(对称加密)、aead(认证加密)、hash(哈希)、rng(随机数)。
  • 无需root权限即可创建AF_ALG套接字(只要内核启用了CONFIG_CRYPTO_USER_API_AEAD)。

在漏洞中的三重角色:

  • 能力暴露(Capability Exposure):向普通用户态程序开放了访问内核scatterlist机制的能力。正常情况下,用户态无法直接操作页缓存的物理页面。
  • 边界突破(Boundary Crossing):提供了从用户态到内核scatterlist的合法路径。攻击者无需内核模块、无需/dev/mem、无需/proc/kcore,仅通过标准socket API即可触达内核内部数据结构。
  • 算法触发(Algorithm Trigger):通过绑定authencesn(hmac(sha256),cbc(aes))算法,algif_aead执行解密时会向目标scatterlist写入数据,从而将"只读页缓存页"转化为"可写攻击面"。

关键代码路径:

// 用户态创建AF_ALG socket
                        int sock = socket(AF_ALG, SOCK_SEQPACKET, 0);

                        // 绑定AEAD算法
                        struct sockaddr_alg sa = {
                            .salg_family = AF_ALG,
                            .salg_type = "aead",
                            .salg_name = "authencesn(hmac(sha256),cbc(aes))"
                        };
                        bind(sock, (struct sockaddr *)&sa, sizeof(sa));

                        // 通过splice()将文件页传入socket
                        splice(fd_in, NULL, sock, NULL, size, SPLICE_F_MOVE);

Q3:splice()系统调用为何成为触发该漏洞的关键路径?

A:splice()是连接页缓存与AF_ALG socket的关键桥梁,其"零拷贝"特性是漏洞触发的必要条件。

splice()机制:

  • splice()是Linux 2.6.17引入的零拷贝数据传输系统调用,用于在两个文件描述符之间移动数据,无需经过用户态缓冲区。
  • 当fd_in是普通文件时,splice()直接传递其页缓存页面(page cache pages)的引用,而非复制数据内容。
  • 页缓存页面通常是只读的(引用计数高,映射为只读)。

触发漏洞的关键差异:

数据传输方式 是否经过页缓存 页缓存页是否被传入AF_ALG 是否触发漏洞
read() + write() ❌ 否(数据被复制到用户态缓冲区) ❌ 否
sendfile() ❌ 否(通常用于网络socket) ❌ 否
splice() ✅ 是(直接传递页缓存页引用) ✅ 是
mmap() + 直接写入 ❌ 否(用户态无法直接写入页缓存) ❌ 否

为什么是splice()而非其他方式?

  • read()会将页缓存数据复制到用户态缓冲区,用户态再write()到socket时,数据已进入新的匿名内存页,不再是页缓存页。
  • splice()不复制数据,直接传递页缓存页面的引用,保持了页面的"页缓存身份"(struct page的mapping字段指向address_space)。
  • 只有splice()能够将"属于页缓存的只读页面"直接送入AF_ALG的处理路径,使得algif_aead错误地将页缓存页当作可写目标。

Q4:攻击者如何利用该漏洞向"只读"或"受信任"文件的页缓存(page cache)写入数据?

A:攻击过程分为四个阶段,形成一个完整的利用链:

阶段一:目标选择
攻击者选择系统上任意可读的setuid二进制文件(如/usr/bin/su)。该文件的页缓存已存在于内存中(系统启动后首次执行时加载)。

阶段二:AF_ALG请求构造

# 创建AF_ALG socket
                        sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)

                        # 绑定authencesn算法
                        sock.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))

                        # 设置AAD认证标签长度(16字节)
                        sock.setsockopt(socket.SOL_ALG, socket.ALG_SET_AEAD_AUTHSIZE, 16)

阶段三:splice页缓存页

# 打开目标文件(只读)
                        target_fd = os.open("/usr/bin/su", os.O_RDONLY)

                        # 通过splice将页缓存页传入AF_ALG socket
                        # 关键:splice传递的是页缓存页的引用,而非数据副本
                        os.splice(target_fd, None, sock.fileno(), None, 4096, os.SPLICE_F_MOVE)

阶段四:触发写入

# 发送消息触发AEAD解密
                        # 内核执行authencesn解密时,向dst scatterlist(即页缓存页)写入AAD的seqno_lo
                        sock.sendmsg_afalg(op=socket.ALG_OP_DECRYPT, iv=b"x"*16, assoclen=16)

写入的数据特征:

  • 写入位置:目标文件页缓存的特定偏移处(AAD结构中的seqno_lo字段位置)
  • 写入内容:4字节受控数据(AAD结构中的seqno_lo字段,即bytes 4–7)
  • 写入次数:可重复执行,逐步覆盖更多字节,实现任意代码注入

Q5:为何篡改页缓存中的setuid二进制文件能够实现从普通用户到root的权限跨越?

A:利用的是Linux setuid机制与页缓存执行的特性组合。

setuid机制:

  • setuid(Set User ID)是Unix/Linux的文件权限机制,允许普通用户以文件所有者的权限执行程序。
  • /usr/bin/su、/usr/bin/sudo、/usr/bin/passwd等文件通常属于root,且设置了setuid位(权限4755)。
  • 普通用户执行这些程序时,进程的有效UID(euid)临时提升为0(root)。

攻击逻辑链:

  1. 攻击者篡改/usr/bin/su的页缓存副本 → 在su的代码路径中注入恶意指令
  2. 攻击者执行/usr/bin/su → 内核加载/usr/bin/su时,优先使用内存中的页缓存副本 → 磁盘文件未被修改,因此文件权限、校验和均正常
  3. su在setuid机制下以root权限(euid=0)运行 → 触发攻击者植入的payload → 执行攻击者控制路径(如execve("/bin/sh"))

为何页缓存篡改有效:

  • 内核在执行文件时,首先检查页缓存中是否已有该文件的页面(通过inode->i_mapping的address_space查找)。
  • 如果页缓存中存在(且未被回收),内核直接使用页缓存中的副本,不会重新从磁盘读取。
  • 攻击者修改的是页缓存中的副本,磁盘上的原始文件保持不变。
  • 因此ls -l、sha256sum、AIDE、Tripwire等工具检查磁盘文件时显示完全正常。
  • 由于修改的是页缓存而非磁盘,不触发inotify/fanotify,不改变磁盘文件校验和,传统完整性监控完全失效。

Q6:该漏洞利用是否依赖竞态条件(race condition),其稳定性与Dirty COW等历史漏洞相比如何?

A:不依赖竞态条件,稳定性远超Dirty COW。

历史漏洞稳定性对比

漏洞 竞争条件 成功概率 利用复杂度 PoC体积 跨版本适配
CVE-2026-31431 (Copy Fail) ❌ 无 单发成功率极高 极简 732字节Python ✅ 无需偏移
CVE-2022-0847 (Dirty Pipe) ❌ 无 单发成功率极高 极简 ~400字节C ⚠️ 需特定版本
CVE-2016-5195 (Dirty COW) ✅ 需要 需多次尝试 中等 较复杂 ⚠️ 需多次尝试
CVE-2021-4034 (PwnKit) ❌ 无 单发成功率极高 极简 ~200字节C ✅ 通用

Copy Fail的稳定性来源

  • 确定性执行路径:socket() → bind() → splice() → sendmsg()是顺序执行的系统调用,无并发操作、无时序依赖。
  • 无内核状态竞争:不依赖内核内存布局、不依赖特定时序、不依赖CPU核心数、不依赖系统负载。
  • 跨版本通用:不需要针对不同内核版本调整偏移地址或ROP gadget,同一PoC可在所有受影响版本上运行。
  • 纯Python实现:732字节的Python脚本即可完成利用,无需编译C代码,无需内核模块,无需特定编译器。
  • VFS绕过:写入路径完全绕过VFS层,不触发任何文件系统访问控制钩子(DAC/MAC均无效)。

二、历史坐标与战略启示

Q7:Copy Fail与Dirty COW(CVE-2016-5195)、Dirty Pipe(CVE-2022-0847)在触发路径、利用门槛和稳定性上有何异同?

A:

综合对比表

对比维度 CVE-2026-31431 (Copy Fail) CVE-2022-0847 (Dirty Pipe) CVE-2016-5195 (Dirty COW)
发现时间 2026年4月 2022年2月 2016年10月
潜伏周期 ~9年(2017–2026) ~2年(Linux 5.8+) ~9年(2007–2016)
漏洞类型 内核逻辑缺陷 内核逻辑缺陷 内核竞态条件
触发路径 AF_ALG + splice + 页缓存 Pipe buffer flags 滥用 COW页竞态条件
涉及子系统 加密子系统、splice、页缓存 Pipe、页缓存 MMU、COW、页缓存
竞争条件 ❌ 无 ❌ 无 ✅ 需要
利用门槛 极低(Python脚本) 极低(C程序) 中等
PoC体积 732字节Python ~400字节C 较复杂
稳定性 单发成功率极高 单发成功率极高 需多次尝试
跨版本适配 ✅ 无需偏移 ⚠️ 需特定内核版本 ⚠️ 需多次尝试
写入目标 页缓存(不脏页) 页缓存(不脏页) COW私有映射
磁盘痕迹 ❌ 无 ❌ 无 ⚠️ 可能有
隐蔽性 ⭐⭐⭐⭐⭐极高 ⭐⭐⭐⭐高 ⭐⭐⭐中等
VFS绕过 ✅ 是 ✅ 是 ❌ 否
容器逃逸 ✅ 是(披露方声称) ❌ 否 ❌ 否
利用后持久化 ❌ 无(重启即消失) ❌ 无(重启即消失) ⚠️ 可能持久化
修复复杂度 回退in-place优化 限制pipe flags 修复COW竞态

关键差异分析
Copy Fail vs Dirty Pipe:

  • 相似性:两者都利用页缓存的"不脏页"特性,修改内存中的文件副本而不触及磁盘,因此均绕过传统文件完整性监控。
  • 差异性:Copy Fail的触发路径更"冷门"(AF_ALG加密接口),Dirty Pipe利用的是更通用的pipe机制。Copy Fail额外具备容器逃逸能力(页缓存跨容器共享),而Dirty Pipe仅限于单主机内的页缓存篡改。

Copy Fail vs Dirty COW:

  • 相似性:两者都潜伏约9年,都涉及页缓存/内存管理机制,都影响了大量Linux发行版。
  • 差异性:Dirty COW需要竞争条件,利用不稳定,成功率依赖运气和系统状态;Copy Fail是确定性漏洞,单发成功率极高。Dirty COW修改的是COW私有映射(可能持久化到磁盘),Copy Fail修改的是全局页缓存(重启即消失,更隐蔽)。

Q8:漏洞源自密码加密安全机制,为什么对应安全机制反过来会成为安全软肋?

A:这是一个关于"安全机制自身成为攻击面"的深刻案例。

密码学安全机制的原始目的:

  • AF_ALG的设计初衷是将内核经过FIPS认证的加密实现暴露给用户态,避免用户态库(如OpenSSL)自行实现加密算法可能引入的漏洞。
  • algif_aead的in-place优化旨在减少内存复制开销,提升AEAD解密性能约10-20%。
  • splice()的零拷贝设计旨在减少数据在内核态与用户态之间的复制,提升I/O性能。

为何成为安全软肋:

  • 过度信任内部路径:内核开发者假设"通过AF_ALG传入的数据缓冲区"与"普通文件页缓存"是互斥的。即,AF_ALG处理的是"匿名内存页",而非"文件-backed页缓存页"。但splice()打破了这一假设。
  • 性能优化破坏安全不变量:in-place优化的核心假设是"源缓冲区与目标缓冲区是同一内存区域,且该区域是可写的"。但当splice()传入页缓存页时,这一假设失效——页缓存页是"只读的",却被当作"可写的目标缓冲区"。
  • 抽象层泄漏:AF_ALG抽象了加密操作,但泄漏了底层scatterlist的实现细节。攻击者不需要理解AEAD算法,只需要理解"向目标scatterlist写入数据"这一副作用。
  • 最小权限原则的违背:AF_ALG向所有非特权用户开放了操作内核scatterlist的能力。在微内核或能力系统设计中,此类操作应仅限于特权进程或经过显式授权的进程。

启示:安全机制的设计必须考虑"被误用"的场景。性能优化不应以破坏安全不变量为代价,且任何向用户态暴露内核内部机制(如scatterlist、页缓存引用)的接口,都应经过严格的安全边界分析。

Q9:该漏洞长达九年的潜伏周期对Linux内核代码审计和回归测试机制有何警示意义?

A:

警示一:跨子系统耦合的盲区
Copy Fail涉及五个独立子系统(AF_ALG、algif_aead、splice、authencesn、Page Cache)的交互。传统的代码审计通常由子系统维护者各自负责,难以发现跨子系统的emergent vulnerability。

改进建议:

  • 建立跨子系统交互测试矩阵,重点测试"A子系统输出作为B子系统输入"的边缘场景。
  • 对splice()这类"万能连接器"系统调用,建立与所有可能目标子系统(字符设备、socket、加密接口、BPF map等)的组合测试。
  • 引入"数据流追踪"审计:追踪页缓存页从splice()到AF_ALG到authencesn的完整路径,验证每一环节的访问权限。

警示二:性能优化的安全代价
2017年的in-place优化(req->src == req->dst)从性能角度看是合理的改进,但破坏了"源缓冲区与目标缓冲区隔离"的安全不变量。

改进建议:

  • 对涉及"共享缓冲区"、"零拷贝"、"in-place"的性能优化,强制进行安全性评审。
  • 引入"不变量检查":在优化后的代码路径中,通过BUG_ON()或WARN_ON()断言src != dst或page_is_writable(dst)。
  • 建立"性能优化安全审查清单",要求优化提交者明确说明"该优化破坏了哪些安全假设,以及这些假设在其他代码路径中是否仍然成立"。

警示三:回归测试覆盖不足
内核测试套件(kselftest、LKFT)未能覆盖"splice()传入页缓存页到AF_ALG AEAD解密"这一边缘场景。

改进建议:

  • 扩展kselftest,增加splice()与各类字符设备、socket、加密接口的组合测试。
  • 引入组合模糊测试(Combinatorial Fuzzing):如Syzkaller应扩展覆盖AF_ALG + splice() + sendmsg()的组合。
  • 建立"危险系统调用组合"黑名单,自动生成并执行fuzz测试用例。

警示四:"冷门"子系统的安全债务
AF_ALG自2011年引入后,安全研究关注度远低于网络、文件系统子系统。近15年的代码积累形成了巨大的"安全债务"。

改进建议:

  • 对向用户态暴露内核内部机制的接口(如AF_ALG、io_uring、BPF、perf)进行专项安全审计。
  • 建立"用户态可触发的内核路径"清单,定期审计。
  • 引入"攻击面预算"概念:每个新的用户态-内核态接口必须评估其增加的攻击面,并配套相应的测试和监控。

警示五:AI辅助审计的价值与局限
Copy Fail由Xint的AI辅助代码审计工具在约1小时内从crypto/子系统中发现,表明AI在规模化扫描和模式识别方面具有优势。

改进建议:

  • 将AI辅助审计纳入内核安全流程,作为人工审计的前置筛选器。
  • 对AI发现的高危候选漏洞进行优先人工复核。
  • 但不应过度依赖AI:AI发现的是"候选漏洞",最终的验证、利用开发和修复仍需人类专家完成。

Q10:针对此类由"多机制耦合"引发的emergent vulnerability,未来的漏洞挖掘和防御体系应如何演进?

A:

漏洞挖掘演进方向

方向 具体措施 优先级
组合模糊测试 扩展Syzkaller等fuzzer,重点测试系统调用组合(如splice() + socket() + sendmsg())
静态分析增强 开发跨子系统的数据流分析工具,检测"只读数据被当作可写目标"的模式
形式化验证 对关键内核路径(如scatterlist处理)使用Coq/Isabelle进行形式化证明
AI辅助审计 训练大语言模型识别"in-place优化"、"零拷贝"、"src==dst"等高风险代码模式
红队持续测试 建立内核红队,定期针对"用户态可触发的内核路径"进行对抗性测试
供应链审计 对内核crypto子系统进行专项审计,类似OpenSSL的Heartbleed后审计

防御体系演进方向

方向 具体措施 优先级
运行时微隔离 对AF_ALG、io_uring等高风险接口实施默认禁用+ 按需启用策略
eBPF实时防护 部署eBPF探针,监控异常的系统调用组合(如splice()到AF_ALG)
页缓存完整性 研究内核级页缓存完整性保护机制,防止用户态触发的页缓存写入
供应链安全 在CI/CD中集成内核漏洞扫描,确保容器镜像和固件使用安全内核版本
零信任内核 即使在内核内部,也坚持"最小权限"原则,限制子系统间的数据流
快速修复能力 内核Live Patching、容器镜像热更新等技术应成为基础设施标配

战略启示
Copy Fail揭示了现代操作系统的一个根本性挑战:性能优化与安全性之间的张力。零拷贝、in-place计算、共享内存等优化技术极大地提升了系统性能,但也破坏了传统的安全边界。

未来的内核设计应遵循以下原则:

  • "默认安全"优于"默认性能":高风险优化应默认关闭,仅在显式配置后启用。例如,AF_ALG的in-place优化可以是一个启动参数选项,而非默认行为。
  • "可审计性"作为设计约束:任何新的用户态-内核态接口必须附带可审计的日志路径。内核应提供"系统调用组合审计"能力,记录跨子系统的数据流。
  • "快速修复"能力:内核Live Patching、容器镜像热更新、固件OTA等技术应成为基础设施标配。九年潜伏期的根本原因之一是"修复成本过高"(需要重启、需要发行版协调),如果内核具备真正的在线修复能力,此类漏洞的窗口期将大幅缩短。
  • "攻击面最小化":向用户态暴露的内核接口应遵循"最小必要"原则。AF_ALG向所有非特权用户开放全部加密算法,这种"过度开放"的设计哲学需要重新审视。