Copy Fail:Linux 内核 AF_ALG splice Page Cache 污染提权漏洞

文档版本:v1.0 · 更新日期:2026-05-18 适用对象:安全研究员、系统管理员、DevSecOps 团队


漏洞速览

属性
CVE编号CVE-2026-31431
漏洞别名Copy Fail
CVSS评分7.8 HIGH
CVSS向量AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
漏洞类型CWE-416(Use After Free)
攻击向量本地
权限要求低权限
用户交互不需要
公开日期2026-04-29
PoC状态已公开(Python脚本 732字节)
补丁版本主线已合并
临时缓解可禁用 algif_aead 模块

关键时间线:2026-04-29 Theori 团队 Xint Code 公开披露 → 2026-04-29 CISA KEV 收录 → 2026-05-01 各发行版发布补丁


漏洞简介

CVE-2026-31431(别名”Copy Fail”)是 Linux 内核 AF_ALG 子系统中 algif_aead 模块存在的一个本地提权漏洞,CVSS 评分 7.8(HIGH)。该漏洞由 Theori 团队的安全研究员 Xint Code 于 2026 年 4 月 29 日公开披露,并于同日被 CISA 列入已知可利用漏洞目录(KEV)。

漏洞的根本成因可追溯至 2017 年合入主线的一个提交 72548b093ee3。该提交将 AF_ALG AEAD(Authenticated Encryption with Associated Data)套接字的加解密操作切换为就地(in-place)处理模式,即加密或解密结果直接写入原始缓冲区而非分配新的内存。这一设计变动引入了一个隐蔽的内存管理缺陷:当 splice() 系统调用将管道页面(pipe page)注入到套接字接收路径后,内核在进行就地解密时并未取得该页面缓冲区的独占所有权。

这意味着非特权进程可以通过精心构造的操作序列,在解密完成后依然保留对已解密页面的读取引用。利用这一残留引用,攻击者能够对页面缓存(Page Cache)进行受控的 4 字节写入,从而篡改关键系统文件的内容。受影响的内核版本范围从 4.14 一直延续至 7.0-rc,覆盖自 2017 年以来几乎所有主流 Linux 发行版。该漏洞具有确定性利用特征(非竞态条件),完整的提权利用代码仅需一个 732 字节的 Python 脚本即可实现。更为隐蔽的是,攻击过程不会在磁盘上留下任何痕迹,传统的文件完整性监控(FIM)方案无法检测此类攻击。


技术原理

本漏洞的利用链涉及以下关键步骤,每一步都利用了 Linux 内核中特定子系统的设计特性:

攻击链路

  1. 创建 AF_ALG AEAD 套接字:攻击者通过 socket(AF_ALG, SOCK_SEQPACKET, 0) 创建一个 algif_aead 类型的加密套接字,并绑定到内核中的 AEAD 算法(如 authenc(hmac(sha256),cbc(aes)))。

  2. splice() 注入管道页面:利用 splice() 系统调用将管道(pipe)的内存页面注入到 AF_ALG 套接字的接收缓冲区中。该页面此时由管道和套接字共享,引用计数为 2。

  3. 就地解密不取独占所有权:当对套接字执行 AEAD 解密操作时,内核的 aead_recvmsg() 路径在就地处理模式下直接将解密结果写入共享的管道页面,而不会通过 get_page() / pin_user_pages() 等方式获取页面的独占引用。

  4. 非特权进程保留读取引用:由于内核未独占页面所有权,攻击者进程仍通过管道文件描述符保留对已解密页面的合法读取引用。此时该页面包含了解密后的明文数据,但同时仍被页面缓存视为有效内容。

  5. 通过保留引用写入受控数据:攻击者利用保留的管道写入端,向已解密的页面写入最多 4 字节的受控数据。这 4 字节被内核页面缓存系统接受并视为合法修改。

  6. 篡改 SUID 二进制获取 root:如果目标页面恰是某个 SUID 二进制文件(如 /usr/bin/passwd)的页面缓存,攻击者可将关键代码路径替换为 shellcode,下次执行该 SUID 二进制时即获得 root 权限。

关键内核组件交互

用户态进程
    │
    ├─ socket(AF_ALG, SOCK_SEQPACKET, 0)  ──►  AF_ALG 子系统
    │                                              │
    ├─ bind() + setsockopt()  ────────────►  algif_aead 模块
    │                                              │
    ├─ splice(pipe_fd, ..., socket_fd, ...) ──►  共享管道页面注入
    │                                              │
    ├─ read()/recvmsg()  触发解密  ─────────►  内核 AEAD API
    │   (就地模式,未独占页面)                      │
    │                                              ▼
    ├─ write(pipe_fd, payload, 4)  ────────►  Page Cache 污染
    │                                              │
    └─ execve(被篡改的SUID二进制)  ──────────►  root shell

关键代码示例——套接字创建与 splice 注入

#include <linux/if_alg.h>
#include <sys/socket.h>
#include <sys/splice.h>
#include <unistd.h>
 
int main(void) {
    // 步骤1: 创建 AF_ALG 套接字
    struct sockaddr_alg sa = {
        .salg_family = AF_ALG,
        .salg_type   = "aead",
        .salg_name   = "authenc(hmac(sha256),cbc(aes))"
    };
    int alg_sock = socket(AF_ALG, SOCK_SEQPACKET, 0);
    bind(alg_sock, (struct sockaddr *)&sa, sizeof(sa));
 
    // 设置 AEAD 密钥
    setsockopt(alg_sock, SOL_ALG, ALG_SET_KEY, key, key_len);
 
    // 通过 accept() 获取操作套接字
    int op_sock = accept(alg_sock, NULL, 0);
 
    // 步骤2: splice() 将管道页面注入套接字
    int pipefd[2];
    pipe(pipefd);
 
    // 向管道写入页面大小的数据
    char buf[4096] = {0};
    write(pipefd[1], buf, 4096);
 
    // splice 管道页面到 AF_ALG 套接字
    splice(pipefd[0], NULL, op_sock, NULL, 4096, SPLICE_F_MOVE);
 
    // 步骤3-5: 触发解密并利用残留引用写入 [PoC 核心逻辑]
    // 解密操作通过 read()/recvmsg() 触发,内核就地解密后
    // 攻击者仍可通过 pipefd[1] 写入受控数据污染 Page Cache
 
    return 0;
}

受影响版本

发行版/产品受影响版本修复版本备注
Linux Kernel (mainline)4.14 ~ 7.0-rc≥ 已合入补丁的版本自 2017 年起受影响
Ubuntu 24.04受影响已修复-
Amazon Linux 2023受影响已修复-
RHEL 10.1受影响已修复-
SUSE 16受影响已修复-
Ubuntu 26.04N/AN/A不受影响
RHEL 7/8 (3.x/4.x 内核)N/AN/A不受影响(内核版本低于受影响范围)

漏洞影响场景

场景一:普通 Linux 主机本地提权

  • 前提条件:攻击者已获得目标主机上的低权限 Shell(如通过 Web 应用漏洞、弱口令等)
  • 利用过程:上传 732 字节 Python PoC 脚本,使用低权限用户身份执行,篡改 SUID 二进制页面缓存,获取 root 权限
  • 最坏影响:完全控制主机,窃取所有用户数据、植入持久化后门、横向移动至内网其他资产

场景二:Kubernetes 集群容器逃逸 + 主机 root

  • 前提条件:攻击者已攻破集群中的一个 Pod,获得容器内低权限代码执行能力
  • 利用过程:在容器内执行 PoC(容器共享宿主机内核),通过 Page Cache 污染篡改宿主机上的 SUID 二进制或内核模块依赖文件,逃逸至宿主机并获取 root
  • 最坏影响:控制宿主机节点后进一步攻破整个集群,访问所有 Secrets、ConfigMap,接管 API Server

场景三:CI/CD 流水线执行节点沦陷

  • 前提条件:攻击者通过供应链攻击或恶意 PR 在 CI/CD Runner 中注入恶意代码
  • 利用过程:在 CI Job 执行环境中运行利用脚本,提权后篡改构建产物(如植入后门的二进制发布包)、窃取签名密钥
  • 最坏影响:污染软件供应链,将后门分发至所有下游用户;窃取代码签名证书和镜像仓库凭证

场景四:云虚拟机横向提权

  • 前提条件:云租户在共享宿主机上拥有一台低权限虚拟机实例
  • 利用过程:在 VM 内执行 PoC 获取 root,进而尝试利用其他漏洞突破虚拟化隔离
  • 最坏影响:若配合其他虚拟化逃逸漏洞,可能威胁同宿主机上的其他租户数据

修复措施

推荐修复(根本解决)

通过升级内核至已合入补丁的版本,从根本上消除漏洞。

Ubuntu / Debian

sudo apt update && sudo apt upgrade -y
sudo reboot

RHEL / AlmaLinux / Rocky Linux

sudo dnf update kernel -y
sudo reboot

SUSE / openSUSE

sudo zypper update kernel-default -y
sudo reboot

Fedora

sudo dnf upgrade kernel -y
sudo reboot

升级完成后务必重启系统使新内核生效。可使用 uname -r 确认当前运行的内核版本。

临时缓解(无法立即升级时)

如无法立即升级内核,可通过禁用 algif_aead 内核模块进行临时缓解:

# 立即卸载模块(如当前已加载)
sudo rmmod algif_aead 2>/dev/null || true
 
# 配置 modprobe 阻止模块加载
echo "install algif_aead /bin/false" | sudo tee /etc/modprobe.d/disable-algif-aead.conf
 
# 更新 initramfs(使配置在重启后生效)
sudo update-initramfs -u

注意:禁用 algif_aead 模块不会影响以下功能:dm-crypt / LUKS 磁盘加密、kTLS 内核 TLS、IPsec / XFRM、OpenSSL 用户空间加密、GnuTLS、SSH。上述组件均使用内核中独立的加密子系统(如 dm-crypt 使用 crypto API 但不依赖 algif_aead),不受本次模块禁用影响 [待验证]。


一键检查与执行临时修复脚本

检查脚本

#!/bin/bash
# CVE-2026-31431 Copy Fail 漏洞检查脚本
set -euo pipefail
 
echo "=============================================="
echo " CVE-2026-31431 (Copy Fail) 漏洞状态检查"
echo "=============================================="
 
# 1. 检查当前内核版本
KERNEL_VER=$(uname -r)
echo ""
echo "[1] 当前内核版本: $KERNEL_VER"
MAJOR=$(echo "$KERNEL_VER" | cut -d. -f1)
MINOR=$(echo "$KERNEL_VER" | cut -d. -f2)
 
if [ "$MAJOR" -lt 4 ] || { [ "$MAJOR" -eq 4 ] && [ "$MINOR" -lt 14 ]; }; then
    echo "    → 内核版本低于受影响范围,不受漏洞影响"
    exit 0
fi
 
echo "    → 内核版本处于受影响范围内 (4.14 ~ 7.0-rc)"
 
# 2. 检查 algif_aead 模块是否加载
echo ""
echo "[2] algif_aead 模块加载状态:"
if lsmod | grep -q "^algif_aead "; then
    echo "    → ⚠ 模块已加载!系统存在漏洞风险"
    MODULE_LOADED=1
else
    echo "    → ✓ 模块未加载"
    MODULE_LOADED=0
fi
 
# 3. 检查 modprobe 黑名单配置
echo ""
echo "[3] modprobe 黑名单配置:"
if [ -f /etc/modprobe.d/disable-algif-aead.conf ]; then
    echo "    → ✓ 黑名单文件已存在: /etc/modprobe.d/disable-algif-aead.conf"
    cat /etc/modprobe.d/disable-algif-aead.conf
else
    echo "    → ⚠ 未配置黑名单"
fi
 
echo ""
echo "=============================================="
if [ "$MODULE_LOADED" -eq 1 ]; then
    echo " 结论: 系统存在风险,建议立即执行修复"
else
    echo " 结论: 模块未加载但仍建议配置黑名单防护"
fi
echo "=============================================="

临时修复脚本

#!/bin/bash
# CVE-2026-31431 Copy Fail 临时修复脚本
set -euo pipefail
 
BACKUP_DIR="/root/copy-fail-backup-$(date +%Y%m%d-%H%M%S)"
 
echo "=============================================="
echo " CVE-2026-31431 (Copy Fail) 临时修复"
echo "=============================================="
 
# 安全检查
if [ "$(id -u)" -ne 0 ]; then
    echo "错误: 请使用 root 权限运行此脚本"
    exit 1
fi
 
# 备份当前配置
mkdir -p "$BACKUP_DIR"
echo "[*] 备份当前配置至: $BACKUP_DIR"
lsmod > "$BACKUP_DIR/lsmod-before.txt" 2>/dev/null || true
cp /etc/modprobe.d/disable-algif-aead.conf "$BACKUP_DIR/" 2>/dev/null || true
 
# 卸载模块
echo "[*] 卸载 algif_aead 模块..."
if rmmod algif_aead 2>/dev/null; then
    echo "    ✓ 模块已卸载"
else
    echo "    → 模块未加载或卸载失败(可忽略)"
fi
 
# 配置 modprobe 黑名单
echo "[*] 配置 modprobe 黑名单..."
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif-aead.conf
echo "    ✓ 已写入黑名单配置文件"
 
# 更新 initramfs
echo "[*] 更新 initramfs..."
update-initramfs -u 2>/dev/null || {
    # 兼容不同发行版的 initramfs 更新方式
    if command -v dracut &>/dev/null; then
        dracut --force 2>/dev/null
    elif command -v mkinitcpio &>/dev/null; then
        mkinitcpio -P 2>/dev/null
    else
        echo "    ⚠ 未识别 initramfs 工具,请手动更新"
    fi
}
echo "    ✓ initramfs 更新完成"
 
# 验证
echo ""
echo "[*] 验证修复结果..."
if lsmod | grep -q "^algif_aead "; then
    echo "    ⚠ 警告: 模块仍加载中"
else
    echo "    ✓ 模块未加载"
fi
 
if grep -q "install algif_aead" /etc/modprobe.d/disable-algif-aead.conf 2>/dev/null; then
    echo "    ✓ 黑名单配置已生效"
else
    echo "    ⚠ 警告: 黑名单配置可能未生效"
fi
 
echo ""
echo "=============================================="
echo " 修复完成!"
echo " 备份目录: $BACKUP_DIR"
echo ""
echo " 如需回滚,请执行:"
echo "   sudo rm /etc/modprobe.d/disable-algif-aead.conf"
echo "   sudo modprobe algif_aead"
echo "   sudo update-initramfs -u"
echo "=============================================="
echo ""
echo " 建议: 尽早安排内核升级,从根本上消除漏洞"

参考资源