### runc 容器逃逸原理

1. 竞争条件

这是 runc 容器逃逸中一种经典的利用方式。以 CVE-2019-5736 为例，其核心原理是：

- 进程切换和文件句柄劫持：当我们在宿主机上执行 `docker exec` 等命令时，实际上 runc 会在容器内启动一个新的进程。在 runc 启动这个新进程到真正执行用户指定命令的这段极短的时间内，存在一个“窗口期”
- 恶意代码的快速覆盖：攻击者可以在容器内通过一个精心设计的恶意程序，持续地监控并尝试以写权限打开 runc 进程的文件句柄（`/proc/self/exe`）。一旦 runc 进程完成了权限降级，文件句柄被释放但尚未关闭，攻击者的恶意程序就会立即抢占这个句柄，并向宿主机上的 runc 二进制文件写入恶意 payload
- 获得宿主机 root 权限：当 runc 尝试执行后续命令时，它执行的不再是正常的二进制文件，而是已经被篡改的恶意代码。因为 runc 本身是以 root 权限在宿主机上运行的，所以攻击者就成功地以 root 权限执行了任意命令，实现了容器逃逸

2. 特权模式与危险配置

虽然这不是 runc 自身的漏洞，但它是最常见的容器逃逸方式之一，常常与 runc 的使用有关

- 特权容器（Privileged Container）：如果一个容器被以特权模式启动（`docker run --privileged`），它将获得几乎所有宿主机的 root 能力。这种模式下，容器内的进程可以访问宿主机上的所有设备、挂载宿主机的文件系统，甚至可以操纵内核模块。攻击者可以轻易地通过挂载宿主机根目录并使用 `chroot` 命令切换根目录，从而完全控制宿主机
- Docker Socket 挂载：另一种常见配置错误是直接将 `/var/run/docker.sock`（Docker 守护进程的 Unix Socket）挂载到容器内部。这样做的后果是，容器内的进程可以直接与 Docker 守护进程通信，相当于拥有了在宿主机上创建、运行、停止任何容器的权限。攻击者可以利用这个权限创建另一个特权容器，将宿主机根目录挂载进去，然后轻松实现逃逸

3. 文件描述符泄漏与符号链接

最近的漏洞，如 CVE-2024-21626，则利用了另一种机制：

- 工作目录和文件描述符：这个漏洞是由于 runc 在处理容器进程的启动和工作目录时存在缺陷。攻击者可以利用 `/proc/self/fd/` 这个特殊目录，通过设置容器的工作目录或创建符号链接，来访问本不应该被容器访问到的宿主机文件描述符
- 突破命名空间隔离：容器通过命名空间（Namespaces）机制来隔离文件系统、进程、网络等资源。但是，如果攻击者可以找到一种方式，让容器内的进程能够操作宿主机上的文件句柄，那么就可以绕过这些命名空间的隔离，从而读写宿主机上的任意文件，最终实现逃逸