01 / 对象身份
命名空间隔离,不等于文件副本
runC 进入容器命名空间执行初始化。符合权限条件时,/proc 的可执行文件 magic link 可关联命名空间外的文件对象;路径不可见与对象不可达并不是一回事。
容器运行时边界研究
这是我在工业界开展的技术研究:从 runC 的只读保护与共享内核之间的信任边界出发,研究受限条件下的 ELF 文件注入,以及如何保留程序原有行为。
KCon 2022 · 原演讲题名 · 考虑到攻击危害,源码未公开
阮博男 · 绿盟科技星云实验室
北京 · 技术分享
运行时与内核的信任边界
依据公开文章与演讲展示
研究从两个问题展开:只读保护依赖什么?文件内容被改变以后,原有运行时行为是否还会继续?
01 / 对象身份
runC 进入容器命名空间执行初始化。符合权限条件时,/proc 的可执行文件 magic link 可关联命名空间外的文件对象;路径不可见与对象不可达并不是一回事。
02 / 保护前提
2019 年修补采用只读 bind mount,阻止普通写入并降低复制成本。2022 年 Dirty Pipe 改变的是内核页缓存,绕过这一用户空间保护前提;它不要求旧 runC 漏洞仍未修复。
03 / 工程约束
覆盖整个二进制会破坏运行时。研究改用静态 ELF 文件注入:受文件大小、可用空间与 PIE / ASLR 约束,新增逻辑之后仍回到原入口继续执行。
来源:技术文章 §2、§4;slides 第 6–14、19–25 页。以下展示对应 2022 年公开研究中的历史机制,不据此判断当前 runC 版本。 WP ↗
选择运行时保护与写入条件,逐步观察:引用了什么对象,哪一层阻止写入,宿主机原文件是否仍在路径上。
CONTAINER / 容器侧
只读挂载上的可执行文件
与宿主机原文件分离
HOST / 宿主机侧
只读挂载与宿主机 runC 关联同一文件对象
同一文件对象
选择保护与条件,然后逐步观察文件引用如何穿过容器边界。
保护在何处生效
只读限制改变访问策略,未必创建独立文件副本。推进流程,比较它与 memfd 的边界。
来源:WP §2.3、slides 第 12–14 页。memfd 方案先复制文件并封印;只读方案保留同一对象。2019 年的成本与兼容性讨论解释了方案选择。封印副本在本文范围内隔离宿主机原文件,不代表抵御任意内核漏洞。
研究的改进不是替换整个 runC,而是在受限空间内重定向入口,再恢复原控制流。运行正常,不等于文件可信。
原入口直接进入 runC 的原有逻辑。
静态文件注入 / 研究示意
这里展示文件结构与控制流关系。研究讨论的是运行前修改 ELF 文件,而非向运行中进程注入内存。
Dirty Pipe 不能增大文件,也不能从页面边界开始覆盖。PIE / ASLR 让实际加载地址变化,因此回到原入口需要考虑运行时地址关系。图中不包含载荷、偏移或利用实现。
用于说明当时的空间约束;本页不提供载荷内容。
文中测试构建可容纳该长度;来自特定样本的节布局。
不足以沿用同一位置。研究另探可复用区域,并验证该样本后续运行。
来源:WP §4.2–4.3;slides 第 20–25 页。字节数是当时特定构建的布局与实现值,不是成功率、性能成绩或跨版本保证。文件布局的兼容性仍需逐构建核验。 Slides ↗ · Dirty Pipe 原始分析 ↗
业务容器与镜像供应链的进入时机不同。切换场景,查看研究前提、演示结果与可观察的防御位置。
已具备容器内执行能力。在运行时再次进入该容器的历史场景中,进程与文件引用的关系成为研究观察点。
Slides 第 16、26 页 · WP §3.1、实验一 ↗
DEVELOPMENT
业务安全与最小权限决定进入容器的难度。文章建议尽量采用 rootless,减少后续利用能够依赖的权限。
DEPENDENCIES
文章建议镜像扫描与白名单。镜像路线的运行时构建与加载条件必须核验,不能将所有镜像部署都视为相同场景。
RUNTIME
更新内核与运行时,监控宿主机 runC 修改和异常进程行为。容器还能启动,并不足以证明运行时文件未被改变。
场景与演示:WP §3.1、§4「效果测试」,slides 第 16、26–27 页。防御观点:WP §5,slides 第 31–32 页;此处忠实呈现当时研究建议。
KCon 2022 工业技术研究演讲。文章《进退维谷:runC 的阿克琉斯之踵》记录保护方案、ELF 文件注入约束与两种场景的演示。研究没有独立项目名称,页面沿用原演讲题名。
考虑到攻击危害,我没有公开这项研究的源码。这里通过原创图形解释公开资料中的机制,并保留原始研究的前提与适用范围。