针对 Linux algif_aead / authencesn 页缓存(page-cache)临时写入错误的检测器与概念验证 LPE(本地提权),该漏洞于 2026-04-29 披露。
漏洞披露文章:https://xint.io/blog/copy-fail-linux-distributions
仅可在您拥有或已明确获准评估的主机上使用。该 LPE 会修改内存状态(页缓存),但该技术是真实的权限提升——在未经授权的系统上运行它在大多数司法管辖区属于违法行为。
algif_aead 以就地(in-place)方式执行 AEAD 操作(req->src == req->dst)。当源数据通过 splice() 从常规文件送入时,目标散列表(scatterlist)包含对该文件页缓存页的引用——即内核将写入这些页。随后 authencesn(hmac(sha256), cbc(aes)) 算法会对该目标执行一次 4 字节的“临时”(scratch)写入,写入 AAD 的 seqno_lo 字段(sendmsg 提供的 AAD 的第 4–7 字节),从而破坏该文件的页缓存副本。
由于磁盘上的文件从未被修改,因此不存在磁盘上的签名痕迹;只有共享页缓存的读取者才能观察到这种破坏。/etc/passwd 和 /usr/bin/su 都是全局可读的,因此非特权本地用户可以破坏运行中内核对其任一文件的视图。
受影响版本:包含提交 72548b093ee3(就地 AEAD,2017 年)且未应用上游回退(revert)的内核。披露确认 Ubuntu 24.04 LTS、Amazon Linux 2023、RHEL 14.3 和 SUSE 16 受影响,但底层原语早于该范围。
| 文件 | 用途 |
|---|---|
test_cve_2026_31431.py | 非破坏性检测器。在临时目录中对哨兵文件进行操作;绝不触碰系统二进制文件。 |
exploit_cve_2026_31431.py | LPE。将运行中用户的 UID 翻转为 0(借助 /etc/passwd 的页缓存),随后调用 su 获取 root shell。 |
两个脚本均为纯 Python 3.10+ 标准库实现。
# 1. 检测
python3 test_cve_2026_31431.py
# 退出码 0 = 不受影响,2 = 受影响,1 = 测试错误
# 2. 利用(交互式 — su 会提示输入您自己的密码)
python3 exploit_cve_2026_31431.py --shell
python3 test_cve_2026_31431.py
其工作流程:
AF_ALG 及 authencesn(hmac(sha256),cbc(aes)) 算法。sendmsg+cmsg 内联发送 8 字节 AAD,将 seqno_lo 设置为标记 PWND,然后通过 os.splice() 将哨兵页缓存页中的 32 字节送入 AF_ALG 操作套接字。recv() 驱动解密。认证检查以 EBADMSG 失败;但临时写入无论如何都会触发。输出类别:
Precondition not met(前置条件未满足)——AF_ALG 或 authencesn 不可用。退出码 0。VULNERABLE to CVE-2026-31431(受 CVE-2026-31431 影响)——标记 PWND 已落入被 splice 的页面。退出码 2。Page cache MODIFIED via in-place AEAD splice path(经由就地 AEAD splice 路径修改了页缓存)——页面被写入,但标记未落在预期位置。按受影响处理。退出码 2。Page cache intact(页缓存完整)——已修补。退出码 0。检测器绝不触碰 /usr/bin/su、/etc/passwd 或其所创建临时目录之外的任何其他文件,且该文件在退出时会被删除。
python3 exploit_cve_2026_31431.py # 仅修补,打印后续步骤
python3 exploit_cve_2026_31431.py --shell # 修补并执行 `su <user>`
其工作流程:
/etc/passwd 中查找运行中用户的 UID 行,并定位 4 字符 UID 字段的字节偏移量。write4,将 UID 替换为 0000。pwd.getpwnam(user) 确认 libc 现在报告 UID 为 0。--shell 时,执行 execvp("su", ["su", user])。输入您自己的密码。PAM 会对照 /etc/shadow(未触碰)进行验证,随后 setuid(getpwnam(user).pw_uid) 将落到 0。write4。nscd、sssd、systemd-userdbd)屏蔽对 /etc/passwd 的读取。如果修补后 getpwnam 仍返回真实 UID,请重启或绕过缓存,或选择其他用户。/etc/passwd 页面必须在修补与 su 执行之间保持驻留缓存的。实际上,在任何内存压力正常的系统上这都是可靠的。磁盘上的 /etc/passwd 未发生变化。
干跑(不带 --shell 的 exploit_cve_2026_31431.py)会在退出时通过 POSIX_FADV_DONTNEED 自动驱逐被破坏的页面,因此 UID→名称查找会立即恢复正常。
使用 --shell 之后,该页面会保持被破坏状态,直到您将其清除。在被破坏期间,任何将 UID 1000→名称解析的操作(例如 ls、文件管理器、scp/sftp 所有权检查)都会失败或显示数字 ID。清除方法:
# 非特权 - 请求驱逐 /etc/passwd 的页缓存:
python3 -c "import os; fd=os.open('/etc/passwd', os.O_RDONLY); \
os.posix_fadvise(fd, 0, 0, os.POSIX_FADV_DONTNEED); os.close(fd)"
# 从 root shell 执行:
echo 3 > /proc/sys/vm/drop_caches
重启也会清除该状态。
write4 的工作原理sendmsg([8-byte AAD], cmsg=[ALG_SET_OP=DECRYPT, ALG_SET_IV, ALG_SET_AEAD_ASSOCLEN=8],
flags=MSG_MORE)
splice(target_fd, pipe_w, 32, offset_src=file_offset)
splice(pipe_r, op_fd, 32)
recv(op_fd) # EBADMSG; 临时写入已经完成
AAD 第 4–7 字节(seqno_lo)的 4 个字节由 authencesn 写入目标散列表,而在此代码路径上,该目标正是我们从 target_fd splice 出来的页缓存页。页面内的落点偏移量对应我们传给 splice() 的 offset_src。
在修补后的内核到达您的发行版之前:
sudo tee /etc/modprobe.d/disable-algif-aead.conf <<<'install algif_aead /bin/false'
sudo rmmod algif_aead 2>/dev/null
应用后,test_cve_2026_31431.py 应报告 Precondition not met 并退出码为 0。
上游修复将就地 AEAD 操作回退为异地(out-of-place)操作,使页缓存页不再进入可写散列表。