第 54 章 云原生与容器逆向(K8s / 镜像 / 逃逸)
现代应用一半跑在容器里、编排在 K8s 上。"云原生逆向" = 分析镜像内容、容器运行时行为、K8s 控制面与工作负载、以及"容器逃逸"这一云安全核心课题。云原生的一切都是"文件+配置+运行时"三层,逆向就顺着这三层走。
📍 知识点地图 | 主题:云原生容器 | 前置:第33章 | 后续:第87章 | 核心概念:镜像、K8s、逃逸、SA token、元数据
54.1 云原生攻击面地图
┌─────────────────────────────────────────────┐
│ K8s 控制面(API Server / etcd / Scheduler) │ ← 控制权所在
├─────────────────────────────────────────────┤
│ 工作负载(Pod / Deployment / CronJob) │ ← 业务所在
├─────────────────────────────────────────────┤
│ 容器运行时(containerd / CRI-O / Docker) │ ← 隔离所在
├─────────────────────────────────────────────┤
│ 镜像仓库(Registry) │ ← 供应链所在
├─────────────────────────────────────────────┤
│ 节点/宿主(OS / Kernel) │ ← 逃逸目标
└─────────────────────────────────────────────┘
攻击路径: 应用漏洞 → 容器 → 节点 → 集群 → 云账户
(每一层跳跃 = 一个"逃逸/横向"动作)
54.2 镜像逆向(供应链起点)
镜像结构
镜像 = 分层的只读文件系统(每层 = 一个 Dockerfile 指令)
Registry 存储: manifests + blobs(层)
运行时: 层叠加 + 可写层(容器层)
逆向对象:
每层的内容(文件/二进制/配置)
构建历史(哪层加了什么)
基础镜像(OS/依赖)
ENTRYPOINT/CMD(启动命令)
ENV/ARG(环境变量,常藏密钥!)
镜像分析工具与命令
# 1. 拉取与导出
docker pull <image>
docker save <image> -o image.tar
# 或 skopeo 复制(无需 docker daemon)
skopeo copy docker://<image> dir:output
# 2. 解层查看
tar -xf image.tar
# layers/ 目录每个 tar 是一层,按序叠加
# 查看每层文件清单
tar -tf layer.tar | head -50
# 3. 深度分析(推荐工具链)
trivy image <image> # 漏洞 + 秘密扫描
syft <image> -o spdx-json # SBOM 生成
dive <image> # 逐层交互式分析(哪些层加了什么)
docker history <image> # 构建历史(含指令泄露!)
docker inspect <image> # 配置(Env/Entrypoint/Volumes)
镜像逆向检查清单
□ 基础镜像版本(是否过时/已知 CVE)
□ 层内文件(意外的二进制/脚本/后门)
□ 环境变量中的密钥/凭证(ENV 明文是常见泄露)
□ ENTRYPOINT/CMD(启动时执行什么)
□ 镜像层历史(有无可疑构建步骤)
□ 多阶段构建残留(构建器阶段的工具/源码)
□ 与 Dockerfile 声明不一致(漂移,第 33 章)
□ 非必要工具(curl/wget/nc 在最终镜像 = 攻击者瑞士军刀)
54.3 容器运行时逆向
运行时隔离机制(逃逸课题的知识基础)
Linux 容器隔离三件套:
Namespaces: 隔离视图(PID/NET/MNT/UTS/IPC/USER)
Cgroups: 限制资源(CPU/内存/设备)
Capabilities: 裁剪权限(CAP_* 集合)
逃逸的本质: 突破 Namespace/Cgroup/Capability 的边界
→ 获得宿主机视角或宿主机执行能力
运行时分析工具
# 当前容器环境
cat /proc/1/cgroup # 容器层级
cat /proc/self/status | grep Cap # 当前能力
ls -la /proc/1/root # 宿主视角试探
mount | head -30 # 挂载点(危险挂载信号)
cat /etc/hostname # 主机名(容器 vs 节点)
env # 环境变量(凭证)
# 逃逸相关检查(蓝队/分析视角)
find / -perm -4000 2>/dev/null # SUID 文件
ls -la /var/run/docker.sock # Docker socket 暴露?
cat /proc/self/mountinfo | grep -E "host|proc" # 敏感挂载
常见危险配置(逃逸的入场券)
□ Docker socket 挂载进容器 → 直接操作宿主机 Docker
□ privileged 容器 → 全部 Capability + 设备访问
□ hostPID / hostNetwork / hostIPC → 共享宿主命名空间
□ /proc 可写挂载 → 内核利用跳板
□ 服务账户 token 挂载(K8s 特有)→ 集群凭证
□ 设备直通(/dev/sd*)→ 磁盘访问
54.4 K8s 控制面逆向(集群权限游戏)
核心组件与信任路径
API Server: 一切操作的入口(认证/授权/准入)
etcd: 集群数据存储(所有秘密!)
kubelet: 节点代理(Pod 生命周期)
Controller Manager / Scheduler: 控制回路
信任链:
客户端(kubectl/CI)→ API Server → etcd/工作负载
ServiceAccount token → RBAC → 权限
K8s 权限枚举与利用(逆向视角)
# 1. 拿到 Pod 内的 serviceaccount token
cat /var/run/secrets/kubernetes.io/serviceaccount/token
ls /var/run/secrets/kubernetes.io/serviceaccount/
# 2. 用 token 探测 API 权限
curl -k -H "Authorization: Bearer $(cat token)" \
https://kubernetes.default.svc/api/v1/namespaces
# 3. 枚举 RBAC
# whoami / auth can-i 等(利用 kubectl 或 API)
kubectl auth can-i --list --token=<token>
高价值目标(拿到权限后)
□ Secrets(etcd 里全有): 数据库密码/云凭证/证书
□ Pod 创建权限 → 创建 privileged Pod → 节点逃逸
□ ConfigMap: 配置泄露
□ 集群角色绑定: 提权到 cluster-admin
□ CronJob 篡改: 持久化(周期性执行恶意负载)
□ ServiceAccount 提权: 从低权限 SA 到高权限
典型提权链(CTF/真实攻击通用)
Pod 内代码执行
→ 读取 SA token
→ 探测 API 权限(list pods/secrets?)
→ 创建 privileged Pod(若允许)或直接读 Secrets
→ 节点逃逸(privileged + hostPID)
→ 横向(节点 → 其他节点/集群凭证)
→ 云凭证(metadata API,第 54.6 节)
54.5 容器逃逸技术族谱
| 逃逸技术 | 原理 | 前置条件 |
|---|---|---|
| Docker socket 利用 | 挂载 socket → 起特权容器 | socket 挂载 |
| Privileged 逃逸 | 设备直通 + 内核利用 | privileged |
| CVE 内核逃逸 | 内核漏洞(Overlayfs/cgroups 系列) | 有漏洞内核 |
| 挂载逃逸 | hostPID → 注入宿主进程 | hostPID |
| 进程注入逃逸 | nsenter/ns 进入宿主命名空间 | hostPID/权限 |
| cgroup release_agent | cgroup 写 release_agent → 宿主执行 | cgroup 可写 |
| /proc/sysrq-trigger | 内核魔法键 | 可写 /proc/sys |
| K8s 层面逃逸 | SA token → 提权 → 新 Pod | RBAC 漏洞 |
核心思路: 容器内获得"能触及宿主"的能力
→ 能力来源: 权限(privileged/能力集)、挂载(socket/dev)、
漏洞(内核 CVE)、编排(K8s 权限)
54.6 云元数据与凭证(SSRF 的延伸)
元数据服务(云厂商专属):
AWS: http://169.254.169.254/latest/meta-data/
Azure: 169.254.169.254/metadata/instance?api-version=...
GCP: metadata.google.internal
(凭证: AWS 的 /iam/security-credentials/ 等)
利用链: SSRF/容器内代码执行 → 请求元数据 → 拿临时凭证
→ 凭证操作云 API(S3 读取/创建资源/横向)
防护: IMDSv2(AWS)、托管身份(Azure)、metadata 限制
逆向要点: 云原生应用里找"谁在调元数据"→ 它就是凭证持有者
54.7 云原生逆向工作流(综合)
目标: 分析一个云原生应用/集群
1. 镜像层: 拉取 → 解层 → 枚举文件/配置/秘密(54.2)
2. 配置层: Dockerfile/K8s manifest/Helm chart 审计
(权限/挂载/特权/网络策略)
3. 运行时: 容器内环境枚举(54.3)
4. 权限层: SA token → RBAC 探测 → 可达资源(54.4)
5. 数据层: Secrets/ConfigMap/etcd 暴露评估
6. 逃逸评估: 危险配置清单 → 逃逸可行性(54.5)
7. 凭证层: 元数据/环境变量中的云凭证(54.6)
8. 报告: 从应用到云账户的完整攻击路径
54.8 检查清单
□ 镜像层内容与秘密扫描
□ 构建历史与多阶段残留
□ 容器危险配置(privileged/socket/挂载/能力)
□ SA token 与 RBAC 权限枚举
□ Secrets/ConfigMap 暴露
□ 逃逸可行性(配置/漏洞)
□ 云元数据/凭证可达性
□ 集群横向路径(Pod→节点→集群→云)
□ 报告: 完整攻击路径图
动手练习
- 用
dive分析一个本地镜像,找出"哪些层加了什么"(含一层故意加的测试文件)。 - 在 kind/minikube 集群里创建一个"privileged 容器"(演练环境),枚举它能看到的宿主资源。
- 写一个 Pod 里的脚本:读取 SA token → 调用 API Server 枚举当前权限。
- 用 trivy 扫描一个镜像并分类结果:哪些漏洞只在"非可达路径"上(学 33.4 的可达性思维)。
深入阅读
- 仓库:
CTF-Sandbox-Orchestrator/competition-k8s-control-plane/(K8s 控制面 CTF) - 仓库:
CTF-Sandbox-Orchestrator/competition-container-runtime/(容器运行时 CTF) - 仓库:
CTF-Sandbox-Orchestrator/competition-agent-cloud/(Agent+云) - 仓库:
CTF-Sandbox-Orchestrator/competition-cloud-metadata-path/(元数据路径) - 仓库:
skills/supply-chain-security/SKILL.md(镜像/供应链) - 实战案例:
skills/field-journal/seed-016_k8s-container-escape.md(K8s 容器逃逸)