Runc 容器逃逸漏洞 (CVE-2025-31133 等) 详细分析


1. 摘要

2025 年 11 月,安全社区披露了影响 runc 的三个高危漏洞:CVE-2025-31133、CVE-2025-52565 和 CVE-2025-52881。这组漏洞共同构成了一个严重的漏洞集群,其核心在于利用了 runc 在容器生命周期管理中对 Linux /proc 文件系统 (procfs) 的特权操作。

这三个漏洞涵盖了 runc 不同的缺陷,但最终目标都是滥用在主机上以 root 权限运行的 runc 进程,使其向主机 /proc 文件系统中的敏感 “gadgets” 写入数据。成功利用这些漏洞可导致灾难性后果,包括完全的容器逃逸(在主机上获得 root 权限的远程代码执行)或主机拒绝服务 (DoS)(通过触发内核崩溃)。

2. 共同的攻击向量:/proc

要深入理解这组漏洞的严重性,必须首先解构它们共同的攻击目标:Linux 的 /proc 文件系统(procfs)。

procfs 并不是一个存储在磁盘上的真实文件系统。它是一个伪文件系统,作为 Linux 内核数据结构和运行时配置参数的动态接口。对 procfs 中特定”文件”的写入操作,实际上是在调用内核函数或修改内核的实时状态。

在标准的容器创建流程中,runc 进程在容器命名空间设置完成之前,是在主机命名空间中以完全的 root 权限运行的。这意味着 runc 进程对主机的 procfs 拥有无限制的读写权限。这三个漏洞的共同核心就是劫持 runc 进程的这一特权访问,欺骗它向攻击者选择的 procfs 路径写入数据。

核心攻击 “Gadgets”

攻击者主要针对两个高价值的 procfs 写入目标,它们被称为”gadgets”:

  1. Gadget 1: /proc/sys/kernel/core_pattern (实现 RCE)
  • 机制: 此文件控制着 Linux 系统在进程崩溃时如何处理核心转储 (coredump)。如果该文件的内容以管道符 | 开头,内核会将其后的字符串解释为一个可执行程序的路径。在进程崩溃时,内核会以 root 权限(因为 coredump 是由内核处理的,不属于任何用户命名空间)执行该程序,并将 coredump 数据作为标准输入传递给它。
  • 利用: 攻击者只需将该文件的内容改写为例如 |/path/to/malicious/script(其中脚本位于容器可访问的挂载点),然后攻击者在容器内触发任何进程崩溃,即可在主机上获得 root 权限的任意代码执行 (RCE)。

2. Gadget 2: /proc/sysrq-trigger (实现 DoS)

  • 向此文件写入特定的字符(例如 c)会立即触发内核崩溃 (Kernel Panic) 和系统重启。因此可以利用该文件实现 DoS 攻击

利用前提条件

所有这三种攻击都依赖于一个关键前提:攻击者必须能够诱使 runc 启动一个具有自定义挂载配置的容器。虽然这听起来像是一个高权限操作,但安全公告明确指出,这种配置不仅可以通过恶意的 Kubernetes Pod 定义或 docker run 命令实现,还可以通过 Dockerfile 中的 RUN --mount=... 指令来触发。

目前推测可以通过类似 docker buildx build 的方式来实现,比如先通过 RUN --mount=... 执行到 maskedPath 逻辑,再通过 buildx build 的方式实现条件竞争。

3. 漏洞分析 (1):CVE-2025-31133

  • CVSS 评分: 7.3 (High) (CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H)
  • 受影响版本: 所有已知的 runc 版本

根本原因

runc 提供了一个名为 maskedPaths 的安全功能,其目的是在容器内部”屏蔽”某些高度敏感的主机 procfssysfs 文件(例如 /proc/kcore,它提供了对所有物理内存的访问),使它们在容器内不可读写。

runc 的预期(正常)流程

runcmaskedPaths 功能的目的是使容器内的某些 procfs 路径(例如 /proc/kcore)变得”不可见”或”无害”。开发者的想法是:

  1. 目标: 阻止容器内的进程读取 /proc/kcore
  2. 技术: 使用 mount(2) 系统调用,将一个无害的、空白的设备(即 /dev/null)“贴”在容器内的 /proc/kcore 路径上。
  3. 预期结果: 容器内的进程(例如 cat /proc/kcore)实际上访问的是 /dev/null,因此什么也读不到,从而实现了”屏蔽”。
  4. runc 实现”屏蔽”的技术手段存在缺陷:它通过将容器内部的 /dev/null 设备的 inode 绑定挂载 (bind-mount) 到目标 maskedPaths 路径(例如 /container/rootfs/proc/kcore)上来实现。

利用方法 (Exploitation Method)

此漏洞是一个典型的时间-检查-时间-使用 (TOCTOU) 竞争条件漏洞。

  1. 步骤 1 (准备): 攻击者在容器内(或通过并行容器)持续监视其 /dev/null 文件的状态。
  2. 步骤 2 (竞争):runc 进程检查容器的 /dev/null(确认其存在且为设备文件)和 runc 进程实际执行 mount(2) 系统调用之间的微小时间窗口内,攻击者将容器内的 /dev/null 快速替换为一个指向恶意目标的符号链接 (symlink)。例如:
  • unlink("/dev/null")
  • symlink("/proc/sys/kernel/core_pattern", "/dev/null")

3. 步骤 3 (触发): runc 进程继续执行 mount("/dev/null", "/container/rootfs/proc/kcore",...)

4. 步骤 4 (结果):mount(2) 系统调用被传递一个符号链接作为其源时,Linux 内核会自动解引用 (dereference) 该符号链接。因此,runc 进程(在内核层面)实际执行的命令是 mount("/proc/sys/kernel/core_pattern", "/container/rootfs/proc/kcore",...)

5. 最终影响: 更糟糕的是,runc 认为它是在挂载 /dev/null,因此它会将这个挂载设置为可读写。攻击者因此在容器内部(位于 /proc/kcore)获得了一个对主机 /proc/sys/kernel/core_pattern 文件的可写挂载点。攻击者随后向该文件写入 RCE payload(例如 |/path/to/shell),并触发一次进程崩溃,从而完成容器逃逸。

PS: 研究人员还发现了该漏洞的第二种利用方式:如果攻击者在竞争窗口中删除了 /dev/nullrunc 的代码逻辑会有意地忽略这个错误并继续执行,导致 maskedPath 操作完全失败(成为一个 no-op)。这虽然不会导致 RCE,但会使 /proc/kcore/proc/timer_list 等敏感文件不再被屏蔽,导致潜在的信息泄露。

漏洞深度分析 (2):CVE-2025-52565

  • CVSS 评分: 7.3 (High) (CVSS:4.0/AV:L/AC:L/AT:P/PR:L/UI:A/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H)
  • 受影响版本: runc >= 1.0.0-rc3

根本原因:/dev/console 绑定挂载的逻辑缺陷

在创建容器时,runc 需要为其设置一个控制台(/dev/console)。它的实现方式是:runc 在主机上请求一个新的 PTY(伪终端)设备(例如 /dev/pts/5),然后将这个主机上的 PTY 设备绑定挂载到容器命名空间内的 /dev/console 路径。

利用方法 (Exploitation Method)

该漏洞在概念和应用上与 CVE-2025-31133 几乎一致,同样是利用 TOCTOU 竞争条件。

  1. 步骤 1 (准备): 攻击者需要预测或(通过并行容器)发现在主机上即将被 runc 使用的 PTY 路径(例如 /dev/pts/5)。
  2. 步骤 2 (竞争):runc 创建该 PTY 设备和 runc 执行 mount(2) 将其绑定到容器的 /dev/console 之间的时间窗口内,攻击者将主机上的 PTY 路径( /dev/pts/5 )替换为一个指向恶意”gadget”的符号链接。例如:
  • unlink("/dev/pts/5")
  • symlink("/proc/sysrq-trigger", "/dev/pts/5")

3. 步骤 3 (触发): runc 进程执行 mount("/dev/pts/5", "/container/rootfs/dev/console",...)

4. 步骤 4 (结果): 内核解引用该符号链接。runc 进程(在内核层面)实际执行的命令是 mount("/proc/sysrq-trigger", "/container/rootfs/dev/console",...)

5. 最终影响: 攻击者在容器内获得了对主机 /proc/sysrq-trigger 文件的写访问权(通过写入容器内的 /dev/console)。攻击者只需在容器内执行 echo c > /dev/console,即可触发主机内核崩溃,实现强大的 DoS 攻击。

这个漏洞在时机上甚至比 CVE-2025-31133 更具优势:/dev/console 的绑定挂载操作发生在 runc 应用 maskedPathsreadonlyPaths 之前。这意味着,即使 runc 配置将 /proc/sysrq-trigger 明确列为”只读”或”掩码”,此漏洞依然可以成功,因为它在这些安全措施生效之前就已经完成了恶意挂载。

漏洞深度分析 (3):CVE-2025-52881

根本原因:CVE-2019-16884 的修复不完整

要理解 CVE-2025-52881,必须回溯到 CVE-2019-16884。

  • 历史背景 (CVE-2019-16884): 攻击者可以欺骗 runc,使其将本应写入 /proc/self/attr/current(用于应用 AppArmor 或 SELinux 策略)的 LSM 标签,写入到一个由攻击者控制的、位于 tmpfs 上的 procfs 文件。这导致 LSM 策略_根本没有被应用_到容器进程上。
  • 不完整的修复: 针对 CVE-2019-16884 的修复非常有限。它仅仅是增加了一个检查,以”验证 runc 写入 LSM 标签的目标_确实是一个 procfs 文件_”。
  • CVE-2025-52881 的利用: 攻击者不再使用 tmpfs 上的假文件,而是通过共享挂载和竞争条件, runc 的写入重定向到_另一个真实的、但无害的_ procfs 文件,例如 /proc/self/sched(进程调度器信息)。
  • 结果: runc 准备写入 LSM 策略。它检查目标(现已被重定向到 /proc/self/sched)。runc 问:“这是 procfs 上的一个真实文件吗?“。答案是”是”。检查通过。runc 随即将 LSM 策略数据(例如 AppArmor 配置文件名)写入 /proc/self/sched。内核接受了这次写入(因为它是一个有效的 procfs 文件),但这对 LSM 策略应用来说是一个无操作 (no-op)。LSM 策略被丢弃,从未被应用。

利用方法

此漏洞具有两种强大的利用模式:

  1. 影响 1:LSM 绕过
  • 利用: 攻击者使用上述方法,将 runc 写入 /proc/self/attr/current 的操作重定向。
  • 结果: runc 认为它已经成功应用了 AppArmor 或 SELinux 配置文件,但实际上该配置文件已被丢弃。容器进程将以完全不受 LSM 限制的状态(即 unconfined)启动。这使它成为一个完美的”解锁器”,用于与其他漏洞(如 31133 和 52565)链接。
  1. 影响 2:直接的容器逃逸/DoS
  • 利用: 这个”写入重定向”漏洞并_不_局限于 LSM 标签。它适用于 runc/proc 的_所有_写入操作,包括 sysctl(系统控制)参数(写入到 /proc/sys/...)。
  • 结果: 攻击者可以劫持一个 runc 写入的、无害的 sysctl 参数(例如设置 net.ipv4.ip_local_port_range),并将其重定向到恶意的 procfs “gadget”,例如:/proc/sys/kernel/core_pattern (实现 RCE)。重定向到 /proc/sysrq-trigger (实现 DoS)。

这使得 CVE-2025-52881 本身就是一个极其强大的、可独立完成容器逃逸或 DoS 攻击的漏洞。

结论

CVE-2025-31133、CVE-2025-52565 和 CVE-2025-52881 共同揭示了现代容器运行时中一个深刻的、系统性的安全缺陷。它们的核心是利用了 runc 在设置容器环境时对 procfs 的特权操作与竞争条件和符号链接滥用的结合。

最大的警示:LSM 的失效

本次事件最重大的教训是,AppArmor 和 SELinux 等被广泛信赖的 Linux 安全模块 (LSM) 在面对配置良好、针对其实现逻辑的运行时攻击(如 CVE-2025-52881)时,是脆弱且可以被完全绕过的

此外,SELinux 对 CVE-2025-31133 的(由于重新标记导致的)无效性和 AppArmor 对 CVE-2025-52565 的(由于默认策略宽松导致的)无效性同样令人警醒。组织不能再将 LSM 视为容器安全的”银弹”。

补丁分析与修复策略

OpenContainers 项目发布了三个已打补丁的 runc 版本,以修复所有三个漏洞:

  • runc v1.4.0-rc.3
  • runc v1.3.3
  • runc v1.2.8

由于漏洞可能会发生在 CI/CD 流水线的容器构建( docker build )期间,因此存在镜像构建能力的服务需要特别注意。

参考

  1. https://seclists.org/oss-sec/2025/q4/138
  2. https://github.com/advisories/GHSA-qw9x-cqr3-wc7r
  3. https://github.com/advisories/GHSA-9493-h29p-rfm2

评论