LWN:安全路径遍历的挑战

关注了就能看到更多这么棒的文章哦~

By *Daroc Alden*, January 6, 2026
 Gemini translation
https://lwn.net/Articles/1050887/

LPC

Aleksa Sarai 作为 runc 容器运行时 (runc container runtime) 的维护者,一直面临着不断出现的安全挑战。最近,runc 又发现了一个安全漏洞 (CVE-2025-52565),该漏洞可以追溯到 Linux 上处理文件路径的难题。Sarai 在 2025 年 Linux Plumbers Conference (LPC) 上发表了演讲(幻灯片;视频),探讨了 runc 在处理路径遍历漏洞 (path-traversal vulnerabilities) 时遇到的一些问题,并呼吁大家使用他正在开发的用于安全路径遍历的库 libpathrs。 

Sarai 首先定义了什么是路径安全。他表示,有两种路径安全与 runc 相关。第一种是“常规”路径安全,适用于任何处理文件的应用程序:在对路径进行操作时,其中的某个组件可能会意外发生变化。例如,一个程序可能正使用绝对路径从某个目录读取多个文件,结果路径中间的一个目录被更改了,导致程序看到的是来自不同目录的文件组合。这种检查时间与使用时间 (Time-of-Check to Time-of-Use, TOCTTOU) 错误在路径处理代码中屡见不鲜。他展示了一张幻灯片,列出了自 2017 年以来 runc 中的 14 个不同 CVE,所有这些漏洞都涉及此类问题。LWN 曾报道过 2024 年和 2019 年发生的两次特别值得关注的相关漏洞。Sarai 补充说,第二种路径安全涉及虚拟文件系统 (virtual filesystems) 的特性,需要单独处理。 

Sarai 指出,目前对常规路径安全有一些部分解决方案。例如,openat2() 系统调用允许程序员向内核指定应如何处理某些类型的歧义。但另一方面,openat2() 经常被 seccomp() 拦截,因为其参数之一是指针。因此,唯一真正的替代方案是在用户空间实现路径遍历,即手动打开路径上的每个目录。这虽然“相当棘手,但确实可行”。例如,Go 语言最近在其标准库中增加了对这种遍历的支持。Sarai 建议使用 libpathrs,这是他编写的一个采用 MPL 与 LGPL 许可 (MPL-and-LGPL-licensed) 的 Rust 库(带有 C 绑定),旨在正确处理这种复杂的操作。 

除了大多数应用程序面临的问题外,runc 还需要处理 Sarai 所称的“严格”路径安全 (strict path safety)。一些虚拟文件系统,如 procfs,在安全使用时需要格外注意细节。在普通文件系统上,人们通常不在乎具体打开了哪个 i 节点 (inode),只要它位于正确的路径即可。但在 procfs 上,操作程序预期的确切文件至关重要:“覆盖挂载 (overmounts) 或伪造挂载可能会诱导我们执行危险操作。”例如,如果用户通过绑定挂载 (bind-mount) 将另一个文件挂载到 /proc/self/exe 路径,那么使用该路径重新执行自身的程序就会出现严重错误。 

这并不是一个新问题。对于常规文件,可以使用 openat2() 的 RESOLVE_NO_XDEV 选项来避免跨越文件系统边界。但由于“某种原因”,这不适用于 procfs 的魔术链接 (magic links)(如 /proc/self/exe)。[Sarai 后来写信向我澄清,原因其实很简单:因为 /proc/self/exe 指向另一个文件系统上的可执行文件,RESOLVE_NO_XDEV 自然会拦截它。关键在于确保魔术链接没有被覆盖挂载为指向“错误”的其他文件系统的引用。] 解决方案是使用文件系统挂载 API (filesystem mount API) 获取一个任何其他进程都无法干扰的 procfs 根目录的文件描述符。 

然而,这种方案在嵌套的用户命名空间 (user namespaces) 中并不奏效。因此,需要在嵌套命名空间中运行的程序(如 runc,当用户想要嵌套容器时)必须采取极其细致的检查,以确保特定路径没有被覆盖挂载。 

Sarai 预计会有人怀疑这是否真的是个问题,理由是只有 root 权限才能执行挂载。但在 runc 的语境下,事实并非完全如此。当然,首先要处理挂载命名空间的问题。但还有一个事实是,runc 被大量不同的高级容器程序用作后端。这些程序通常给予用户很大的灵活性来配置容器挂载,这可能导致在 runc 工作时发生非特权挂载,有时甚至允许程序逃逸容器。他说,runc 中几乎所有的漏洞都是误配置 Bug。有时 runc 可以通过识别明显的无效配置来防止这些问题,而有时则需要对高级程序进行补丁,以从源头上避免生成有问题的配置。 

随后,他通过一系列随堂测验展示了配置容器时遇到的困难。runc 最近的两个漏洞 CVE-2025-31133 和 CVE-2025-52565,都涉及利用挂载和符号链接诱导 runc 让容器访问 /proc/sys/kernel/core_pattern,这是用于配置内核核心转储 (core-dump) 处理程序的 procfs 文件。向该文件写入内容可能会导致进程从容器内部逃逸。 

Sarai 在演讲中没有详细说明为什么设置核心转储处理程序会导致逃逸,但他可能指的是 Christian Brauner 在 5 月份强调的问题。当内核启动配置好的核心转储处理程序时,它是以根命名空间的全特权运行的;如果容器能将核心转储处理程序配置为其控制的文件,这实际上就允许它接管整个系统。 

runc 的解决方案是对特殊 i 节点进行更严格的校验,转向使用 libpathrs 进行路径遍历,并使用 TIOCGPTPEER 校验控制台文件是否真的是控制台文件,而不是被悄悄覆盖挂载的普通文件。但这项工作也启发了一些潜在的内核更改,使编写此类路径处理代码更加安全。Sarai 建议为 openat2() 添加 RESOLVE_NO_DOTDOT 选项,以彻底禁止在路径中使用“..”。他还表示,阻止对 procfs 魔术链接的所有覆盖挂载也会有所帮助;虽然自内核 6.12 版本起大多数此类挂载已被阻止,但在安全领域,“大多数”和“全部”是完全不同的概念。 

对于编写用户态应用程序的人员,Sarai 的建议是切换到更多基于文件描述符的设计,而不是依赖路径。理想情况下,使用 openat2() 或 libpathrs 来处理路径操作。他说,每一个处理路径名的系统调用都潜在危险。 

现场一位听众询问 Sarai 对安全处理 cgroupfs(用于操作控制组的虚拟文件系统)路径有何建议。Sarai 回答说,openat2() 的 RESOLVE_NO_XDEV 标志在确保操作留在 cgroupfs 内部方面非常有帮助。第 1 版控制组“很烦人”,他对于如何正确处理它们没有太多建议。但对于第 2 版控制组,他建议打开文件系统根目录的文件描述符并检查 i 节点号。如果正确,就能更确定程序正在与真正的 cgroupfs 交互。 

另一位听众询问他是如何发现这些 CVE 的。他回答说:“不是通过模糊测试 (fuzzing) 之类的手段,只是靠人工审查。”2018 年,他“提供了一个非常通用的脚本”,用于创建可能导致路径遍历问题的场景。从那时起,人们一直在钻研 runc,并逐渐演进攻击手段,以触及日益隐蔽的边界情况。他说,每一年的 CVE 往往都是前一年攻击手段的演进。 

还有人问,他是否认为使用 procfs 和 cgroupfs 等虚拟文件系统来提供内核接口是一个错误。Sarai 回答说:“确实,有几种攻击如果是采用系统调用设计的接口就永远不会发生,这确实让人深思。”但另一方面,虚拟文件系统也确实带来了一些好处。例如,LXC 容器运行时有一个伪造的 procfs 实现 (fake procfs implementation),可以帮助不了解控制组的程序识别进程和内存限制。 

这并没能说服所有人,他们指出拦截系统调用的方法也是存在的。然而,此时会议时间已到,讨论延伸到了走廊。 

[ 感谢 Linux 基金会,作为 LWN 的旅行赞助商,资助了前往东京参加 Linux Plumbers Conference 的行程。 ] 

LWN 评论概述

评论区深入探讨了 Linux 挂载模型的复杂性及其安全影响。有用户建议限制挂载点(如统一放在 /mnt 下)以减少用户态处理的奇技淫巧,认为过多的挂载灵活性增加了安全负担。针对 Sarai 提出的内核改进方案,有人探讨了 RESOLVE_NO_DOTDOT 与现有 RESOLVE_BENEATH 的区别,并吐槽了 seccomp 因为指针参数而禁用 openat2 这种“为了安全而阻碍安全”的讽刺现象。此外,开发者们讨论了“万物皆文件”模型的局限性,建议在内核接口设计中更多采用系统调用而非虚拟文件系统(如 PIDFD_SELF),以规避路径遍历风险。最后,关于“配置错误是否算漏洞”也引发了争论,部分用户认为应由管理员负责,而另一些人则强调 runc 这种底层组件应具备更强的防御性设计。 

  全文完
 LWN 文章遵循 CC BY-SA 4.0 许可协议。 

欢迎分享、转载及基于现有协议再创作~

长按下面二维码关注,关注 LWN 深度文章以及开源社区的各种新近言论~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值