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/,因此容器内可以访问:
ls /public_data/docker/containers/# 列出所有容器 IDhostconfig.json 记录了 --privileged、卷挂载(Binds)、额外 capability 等运行时参数。
第二步:篡改配置
找到自己容器的 hostconfig.json,将 Privileged 改为 true,并追加宿主机根目录的挂载:
"Privileged": true,"Binds": ["/data:/public_data", "/:/host_data"]修改不会立即生效——Docker daemon 重启时才会重新读取这些文件。等待宿主机重启或 daemon 重启即可。
第三步:挂载宿主机硬盘
容器重启后以特权模式运行,且宿主机根目录挂载在 /host_data。此时可以直接 chroot 进去:
chroot /host_data /bin/bash这等价于在宿主机上获得了 root shell。
第四步:注入 SSH 公钥
在宿主机 root 的 authorized_keys 中写入自己的公钥:
echo "<your-public-key>" >> /host_data/root/.ssh/authorized_keys之后即可直接 SSH 登录宿主机,无需密码,权限为 root。
为什么这条路径很隐蔽
- 不依赖任何 CVE,纯粹利用配置错误
- 修改是持久化的,重启后生效
- 只需要能写
/public_data/docker/containers/下的文件 - 从挂载配置看,
/data:/public_data是”数据共享”,很容易被忽视
如何发现和修复
检查是否存在类似暴露:
# 查看所有容器的挂载,确认有没有把 Docker 数据目录挂进去docker inspect $(docker ps -q) | jq '.[].HostConfig.Binds'修复:
- 不要把 Docker 数据目录(
/var/lib/docker/或自定义路径)放在共享卷路径下 - 只把实际需要的子目录挂进容器,而不是父目录
- 如果数据目录和 Docker 数据目录在同一个父路径下,把 Docker 数据根改到别处(
/etc/docker/daemon.json中的data-root):
{ "data-root": "/var/lib/docker"}- 定期检查各容器的
Privileged状态:
docker inspect $(docker ps -q) | jq '.[].Name, .[].HostConfig.Privileged'