Copy Fail: 通过页面缓存投毒破坏容器隔离
现代容器化环境的安全性依赖于一个假设,即 namespaces 和 cgroups 为工作负载提供了稳健的边界。然而,最近披露的一个名为 "Copy Fail" 的漏洞打破了这一假设,其目标是 Linux 内核的一个基本组件:页面缓存(page cache)。与依赖脆弱的竞态条件或 Use-After-Free (UAF) 漏洞来实现代码执行的传统内核漏洞不同,Copy Fail 提供了一种确定性的原语,用于重写缓存的文件内容,使攻击者能够在 pod 之间进行横向移动,甚至完全逃逸到宿主机。
Copy Fail 的机制
从本质上讲,Copy Fail 是一个本地权限提升漏洞,它利用了内核代码中处理 IPSec ESP Extended Sequence Numbers (authencesn) 时存在的内存损坏缺陷。该功能通过 AF_ALG sockets 向非特权用户开放,这是 Linux 内核密码学子系统的用户态接口。
通过误导内核将页面缓存的可变引用视为可丢弃的临时内存,攻击者可以使用 splice(2) 对任何可读文件背后的页面缓存执行受控的 4 字节写入。这允许攻击者修改文件的缓存版本,而无需更改存储在物理磁盘上的字节。由于该写入绕过了标准的记账机制和 overlayfs 的 "copy-up" 机制,修改直接发生在共享的底层文件上。
\n## 为什么容器容易受到攻击
容器隔离是通过 mount、network、PID、user 和 IPC namespaces 实现的。至关重要的是,这些 namespaces 均未创建每个容器独立的页面缓存。内核的页面缓存是整个系统共享的。
在 Kubernetes 环境中,容器镜像由只读的底层层(layers)组成。为了节省空间,容器运行时(如 containerd 或 CRI-O)通过内容哈希对这些层进行去重。如果同一节点上的两个不同 pod 共享同一个基础镜像(例如,debian:bookworm-slim 或 python:3.12-slim),它们将共享相同的底层宿主机 inode 和 address_space。
当 Copy Fail 改变页面缓存中的一个 folio 时,所有指向该相同 address_space 的容器中的文件描述符都将看到修改后的字节。这创造了两个主要的攻击向量:
场景 1:跨容器投毒
在这种情况下,拥有一个 pod 中的代码执行权限(或仅仅是拥有创建 pod 的权限)的攻击者可以针对广泛共享的基础层。
- 目标选择: 攻击者识别一个常见文件,例如
site-packages中的 Python 模块或基础层中的共享库(如glibc)。 - 写入: 使用 Copy Fail,攻击者通过链式 4 字节写入来修补页面缓存中的目标文件。
- 触发: 当同一节点上的第二个无关 pod 导入该模块或执行该库时,它会从缓存中加载被投毒的字节并执行攻击者的代码。
这使得攻击者能够破坏一个经过加固的后端 pod,仅仅因为它与同一节点上被攻破或由攻击者控制的 pod 共享了基础镜像。此外,如果攻击者拥有 pods/create 权限,他们可以故意在受害者节点上调度一个 pod 并拉取相同的基础镜像来触发投毒。
场景 2:容器逃逸至宿主机 Root
Copy Fail 也可以用于实现从非特权容器到宿主机的完全逃逸。这一路径类似于 "Dirty Pipe" 逃逸模式:
- 强制
runc执行: 攻击者在容器内部使用指向/proc/self/exe的 shebang 覆盖/bin/sh。当管理员运行kubectl exec时,runc被调用并被固定在容器的 PID namespace 中。 - 定位与投毒: 攻击者识别
runc进程并打开其/proc/<pid>/exe符号链接。由于runc是从宿主机 bind-mounted 的,这为访问页面缓存中宿主机的runc二进制文件提供了一条路径。 - 执行: 攻击者使用 Copy Fail 覆盖
runc的 ELF header,使其包含恶意负载。下次runc在宿主机上执行(通过探测、pod 启动或另一个exec)时,恶意代码将以宿主机 root 权限运行。
检测与缓解措施
Copy Fail 最危险的方面之一是其对传统安全工具的不可见性。由于磁盘上的字节保持不变,镜像注册表扫描器(Trivy, Clair)、无代理磁盘扫描器以及文件完整性监控器(AIDE, Tripwire)都会报告系统是干净的的。
有效防御措施
| 防御机制 | 有效性 | 备注 |
|---|---|---|
| Kernel Patching | 高 | 唯一的永久修复方法是将宿主机内核更新到已修复的版本。 |
| Seccomp Profiles | 高 | 阻止 socket(AF_ALG, ...) 可以移除该漏洞利用原语。 |
| gVisor / Kata Containers | 高 | 这些工具提供独立的内核或 microVM,消除了共享页面缓存。 |
| Managed MicroVMs (Fargate) | 高 | 每个 pod 拥有独立的内核,可防止跨 pod 投毒。 |
| Runtime EDR | 部分 | 可以检测利用后的行为或内存中的页面不匹配。 |
结论
Copy Fail 表明,Linux 页面缓存的共享性质是容器安全中的一个重大架构盲点。虽然 namespaces 提供了资源的逻辑隔离,但内核底层的内存管理仍然是一个全局资源。对于需要强多租户环境的组织,该漏洞强化了这一观点:容器不应被用作主要的安全性边界,而基于 VM 的隔离(或像 gVisor 这样的沙箱运行时)对于保护高风险工作负载至关重要。