严重性: CVSS 7.8(高危)
类型: 本地权限提升
引入版本: Linux 4.14(2017 年)
修复版本: 提交 a664bf3d603d
CVE-2026-31431 是 Linux 内核 algif_aead 模块中的一个本地权限提升漏洞——该模块是内核用户态加密 API(AF_ALG)的 AEAD 套接字接口。
该漏洞允许非特权用户利用 Linux 内核加密 API 中的地址族 AF_ALG,对页缓存执行受控的 4 字节写入,而页缓存承载着文件的内存副本。通过替换页缓存中可读可执行文件的内存副本中的代码,当任何特权进程随后运行该被篡改的文件版本时,攻击者即可提升用户权限。
与需要竞争条件的 Dirty Cow(CVE-2016-5195)不同,Copy Fail 是一个直线型逻辑缺陷。它无需竞争、重试或易崩溃的时序窗口即可触发。
提交 72548b093ee3(“crypto: algif_aead - switch to in-place processing”)引入了一项优化,在 aead_recvmsg() 中设置 req->src == req->dst,从而省去了单独的输出缓冲区分配。
该优化导致 req->src 和 req->dst 指向一个合并的 scatterlist。因此,来自 splice() 调用的页缓存页面被错误地直接链接到可写的目标 scatterlist 中。
内核的 AEAD API 定义了一个明确的输出契约:目标缓冲区接收 AAD || 明文,恰好为 assoclen + (cryptlen - authsize) 字节。API 中没有任何机制强制执行此契约,也没有任何文档将其列为要求。该设计假设每个 AEAD 算法都会将其写入限制在预期的目标范围内。但有一个算法并未如此。
crypto/
├── algif_aead.c ← 原地 scatterlist 缺陷(主要)
└── authencesn.c ← 越界临时写入(触发点)
crypto/algif_aead.c — 原地赋值/* 存在漏洞 — 2017 年后的原地路径 */
static int aead_recvmsg(struct socket *sock, struct msghdr *msg,
size_t ignored, int flags)
{
/* ... */
/*
* 当用户态对文件调用 splice(2) 时,areq->tsgl 包含
* 该文件的页缓存页面。将 dst = src 会将这些页缓存
* 页面作为可写输出缓冲区交给加密引擎。
*/
aead_request_set_crypt(req,
areq->tsgl, /* src */
areq->tsgl, /* dst — 相同指针;根本原因 */
areq->cryptlen, iv);
}
crypto/authencesn.c — 越界临时写入authencesn 模板是 IPsec 用于扩展序列号(ESN)支持的 AEAD 包装器。IPsec 使用 64 位序列号,分为高半部分(seqno_hi,AAD 的第 0–3 字节)和低半部分(seqno_lo,第 4–7 字节)。线上格式仅携带 seqno_lo;seqno_hi 是隐式的。
/* crypto/authencesn.c — 超出输出边界的临时写入 */
static int crypto_authenc_esn_decrypt(struct aead_request *req)
{
/* ... */
/*
* authencesn 将 seqno_hi 临时存储在 req->dst 中
* 偏移量 (assoclen + cryptlen) 处 — 超出合法输出
* 区域一个字。
*
* 正常操作下:命中内核拥有的内存。
* 使用原地 scatterlist 时:命中页缓存页面。
*/
scatterwalk_map_and_copy(&seqno_hi,
req->dst,
req->assoclen + req->cryptlen, /* 越界偏移量 */
sizeof(seqno_hi), /* 4 字节 */
1 /* 写入 */);
/* authencesn 在失败路径上不会恢复这些字节 */
}
上游修复: 提交 a664bf3d603d — 回滚 2017 年的优化。
crypto/algif_aead.c — 分离的 scatterlist/* 已修复 — 分离的 src 和 dst */
aead_request_set_crypt(req,
areq->tsgl, /* src — 可能包含页缓存页面;只读 */
areq->rsgl, /* dst — 新分配的内核缓冲区;可写 */
areq->cryptlen, iv);
效果: 页缓存页面保留在 src 中,加密引擎仅对其进行读取。authencesn 在 assoclen + cryptlen 处的临时写入现在落入进程拥有的内核分配缓冲区中。任何页缓存页面都无法从写入路径到达。
以下描述了 4 字节写入如何实现完全权限提升。
| 步骤 | 操作 |
|---|---|
| 1 | 打开一个配置为 authencesn(hmac(sha256),cbc(aes)) 的 AF_ALG 套接字 |
| 2 | 将可读的 setuid 二进制文件(例如 /usr/bin/sudo)splice() 到套接字中 — 这会将其页缓存页面放入 areq->tsgl |
| 3 | 精心构造 assoclen + cryptlen,使临时写入偏移量与二进制文件缓存映像中的目标字节对齐 |
| 4 | 调用 recvmsg() — 触发解密路径,authencesn 将 4 个攻击者控制的字节写入页缓存 |
| 5 | 磁盘上的文件保持不变;完整性检查器、inotify 和 inode 时间戳均不受影响 |
| 6 | 随后对该二进制文件的任何执行都会映射已修改的页缓存页面 — 修补后的代码以 euid=0 运行 |
该写入最终落在内存中被拼接文件缓存数据中,完全绕过了文件的权限机制。
./scripts/get_maintainer.pl crypto/algif_aead.c
# Herbert Xu <[email protected]>
# [email protected]
./scripts/checkpatch.pl 0001-crypto-algif_aead-revert-inplace.patch
crypto: algif_aead - restore separate src/dst scatterlists in recvmsg
Commit 72548b093ee3 ("crypto: algif_aead - switch to in-place
processing") set req->src == req->dst inside aead_recvmsg(). When
the caller uses splice(2), the source scatterlist contains page
cache pages of the spliced file. With the in-place optimization,
those same pages become the writable destination passed to the
AEAD transform.
authencesn writes 4 bytes at offset (assoclen + cryptlen) into
req->dst as scratch space for Extended Sequence Number
rearrangement and does not restore them on the error path. With
page cache pages present in dst, this write bypasses the VFS
permission layer and corrupts the in-memory image of the spliced
file without modifying the on-disk inode.
An unprivileged local user can exploit this to overwrite
arbitrary bytes in the page cache of any readable setuid binary
and escalate privileges to root.
Restore separate src and dst scatterlists so that page cache
pages are confined to the read-only source path.
Fixes: 72548b093ee3 ("crypto: algif_aead - switch to in-place processing")
Cc: [email protected] # 4.14+
Reported-by: Taeyang Lee <[email protected]>
Signed-off-by: Your Name <[email protected]>
git send-email 发送git send-email \
[email protected] \
[email protected] \
[email protected] \
0001-crypto-algif_aead-revert-inplace.patch
| 元素 | 用途 |
|---|---|
subsystem: file - verb phrase 主题 | 必需格式;不超过 72 个字符 |
Fixes: <sha> ("original title") | 触发自动稳定版回溯工具 |
Cc: [email protected] | 标记补丁以进行 LTS 分支回溯 |
Reported-by: | 归功于发现者 |
Signed-off-by: | 开发者来源证书 — 必需 |
受影响的内核涵盖 Linux 4.14 至 7.0-rc、所有 6.18.22 之前的 6.18.x 以及 6.19.12 之前的 6.19.x。
临时缓解措施(大多数系统无需重启):
echo "install algif_aead /bin/false" > /etc/modprobe.d/disable-algif.conf
rmmod algif_aead 2>/dev/null || true
此变通方案不影响 dm-crypt/LUKS、kTLS、IPsec/XFRM、OpenSSL、GnuTLS、NSS 或 SSH。
xint.io/blog/copy-fail-linux-distributionsnvd.nist.gov/vuln/detail/CVE-2026-31431a664bf3d603d(主线)72548b093ee3(Linux 4.14,2017 年)