Announcement

👇Official Account👇

Welcome to join the group & private message

Article first/tail QR code

Skip to content

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 72548b093ee3in-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 的核心思路是:

  1. 选择一个setuid 二进制文件(如 /usr/bin/passwd)作为目标
  2. 通过 splice() + AF_ALG 的组合,修改该文件的页缓存
  3. 将 setuid 文件的内容替换为攻击者控制的 shellcode
  4. 执行被修改的 setuid 文件,获得 root 权限

Python PoC 构造

以下是一个简化原理示意的 Python 脚本(非完整利用代码,完整版本 732 字节):

python
#!/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 需要精确控制以下参数:

  1. 加密参数:密钥长度、IV 长度、认证标签长度必须匹配 authencesn 模板的要求
  2. 数据对齐:splice() 传输的数据大小需要与页大小(4KB)对齐
  3. 目标选择:优先选择不频繁使用的 setuid 文件,避免页缓存被其他进程频繁刷新
  4. 回写时机:需要确保页缓存被修改后,在目标文件执行前不会被其他操作刷新

容器逃逸风险

在云原生环境中,CopyFail 的威胁被进一步放大:

页缓存在宿主机与容器间共享。容器内的无特权用户利用 CopyFail 篡改页缓存,影响的是宿主机的全局页缓存。这意味着容器内的普通用户可以通过修改宿主机的 setuid 文件,实现容器逃逸

Theori 的披露分两部分:

  • Part 1:本地提权(LPE)
  • Part 2:容器逃逸(Container Escape)

云安全团队应将 CopyFail 的容器逃逸利用路径纳入风险评估。

检测与防御

缓解方案(补丁前)

在官方补丁全面部署前,可采用以下缓解措施:

方案 1:禁用 AF_ALG aead 接口(影响用户态加密加速)

bash
# 临时禁用 algif_aead 模块
sudo modprobe -r algif_aead
sudo echo "blacklist algif_aead" > /etc/modprobe.d/copyfail-mitigation.conf

方案 2:使用 seccomp 限制 AF_ALG 套接字创建(推荐)

json
// 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
        }
      ]
    }
  ]
}

注意:38AF_ALG 的值(socket.h 中定义)。此策略会阻止非特权进程创建 AF_ALG 套接字,但可能影响依赖用户态加密加速的应用(如 OpenSSL 的 /dev/crypto 引擎)。

方案 3:容器环境加固

yaml
# Kubernetes SecurityContext:限制 capabilities
securityContext:
  capabilities:
    drop:
      - ALL  # 删除所有 capabilities,包括 CAP_NET_RAW 等
  seccompProfile:
    type: RuntimeDefault  # 使用默认 seccomp 策略
  allowPrivilegeEscalation: false

检测方案

方案 1:监控 AF_ALG + splice 的组合调用

bash
# 使用 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:页缓存完整性监控

python
#!/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 实现,并考虑性能影响。

补丁验证

bash
# 检查内核是否已修复
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 的发现建立在安全社区对页缓存漏洞模式十年成熟认知的基础上:

时间漏洞核心机制贡献
2016Dirty COW (CVE-2016-5195)竞态条件写时复制篡改页缓存揭示页缓存可篡改
2022Dirty Pipe (CVE-2022-0847)管道缓冲区注入页缓存展示非文件系统路径篡改页缓存
2026CopyFail (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)是一个教科书级的跨子系统逻辑缺陷

  1. 位置:内核 crypto 子系统 authencesn 模板
  2. 本质:三个独立合理设计(2011 authencesn + 2015 splice + 2017 in-place)的组合爆炸
  3. 类型:逻辑缺陷而非内存破坏,不在任何已知漏洞模式库中
  4. 利用:732 字节 Python 标准库脚本,100% 稳定 LPE
  5. 云原生风险:页缓存共享导致容器逃逸
  6. 发现范式:人类方向设定 + AI 规模执行的协同

对防御者的核心启示

  • ⚠️ 传统安全工具对此类漏洞完全无效——需要重新审视跨子系统交互的安全性
  • ⚠️ 页缓存仍然是 Linux 内核的脆弱面——从 Dirty COW 到 Dirty Pipe 再到 CopyFail,十年了
  • ⚠️ 云环境的隔离假设需要加固——页缓存共享打破了容器与宿主机的边界
  • 立即升级内核——补丁已合入所有 LTS 分支
  • 容器环境启用 seccomp——限制 AF_ALG 套接字创建是最小侵入的缓解

参考资源


免责声明:本文仅用于安全研究和防御目的。未经授权利用漏洞攻击他人系统是违法行为。请在合法授权的环境中进行安全测试。完整 PoC 代码未公开,本文仅提供原理分析和检测方法。

上次更新于: