ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Kubernetes Goat Internal API 组件深度解析:从镜像构建到 SSRF 实战利用

2026/9/17 2:40:20 拓冰建站 浏览量
Kubernetes Goat Internal API 组件深度解析:从镜像构建到 SSRF 实战利用 Kubernetes Goat Internal API 组件深度解析从镜像构建到 SSRF 实战利用【免费下载链接】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-goatKubernetes Goat 是一个故意设计为存在漏洞Vulnerable by Design的 Kubernetes 集群环境用于在交互式沙箱中学习与演练 Kubernetes 安全。Internal APIinternal-api正是该环境中的一个核心攻击面组件它以 Node.js Express 实现了一个脆弱的内部 API 代理服务通过把用户输入直接拼进curl子进程执行为 SSRF服务端请求伪造等云原生攻击提供了真实可复现的靶点。阅读本文后你将完整掌握该组件的镜像构建与发布流程、源码级漏洞原理、集群内部署方式以及在 Kubernetes Goat 场景中的实战利用路径。组件定位Kubernetes Goat 中的内部代理攻击面Internal API 是 Kubernetes Goat 基础设施infrastructure/ 目录中的一个 Docker 容器组件。其镜像名为madhuakula/k8s-goat-internal-api是一个运行在集群内部的 HTTP 代理服务外部攻击者或场景学习者可以向它提交任意的 URL 端点、HTTP 方法与自定义请求头由它代为发起请求并返回响应。从集群网络拓扑来看它承担着两层角色集群内微服务代理它能够访问 Kubernetes 集群内部网络是探测内网服务如metadata-db等仅集群内可达的 Service的跳板SSRF 漏洞载体其实现方式决定了它天然存在服务端请求伪造问题这正是场景 3SSRF 到云元数据/内网服务的核心利用入口。关联文档 infrastructure/internal-api/README.md 明确说明此 Docker 容器是 Kubernetes Goat 的一部分并给出了镜像构建与推送的标准流程。镜像构建与发布流程关联文档给出了该组件的两个核心镜像运维命令二者均为标准的 Docker 工作流docker build -t madhuakula/k8s-goat-internal-api .该命令在 infrastructure/internal-api/ 目录下执行会依据该目录中的 Dockerfile 构建镜像。构建完成后可以将镜像推送到镜像仓库供集群拉取docker push madhuakula/k8s-goat-internal-api该命令将madhuakula/k8s-goat-internal-api镜像推送到 Docker Hub。值得注意的是整个 Kubernetes Goat 的部署setup-kubernetes-goat.sh默认依赖这些预构建的公开镜像直接拉取使用因此本地重新构建镜像通常仅在定制或离线场景下才需要。构建细节Dockerfile 剖析infrastructure/internal-api/Dockerfile 展示了镜像的完整构建过程FROM node:alpine LABEL MAINTAINERMadhu Akula INFOKubernetes Goat WORKDIR /usr/src/app COPY code/package*.json ./ RUN npm install \ apk add --no-cache curl COPY code/* ./ EXPOSE 3000 CMD [ npm, start ]几个关键点基础镜像使用node:alpine体积小巧适合作为工具型容器显式安装curl通过apk add --no-cache curl在 Alpine 中安装 curl。这是漏洞成立的必要前提——server.js 正是通过spawnSync(curl, ...)来发起请求的如果容器内没有 curl整个代理功能将不可用端口约定EXPOSE 3000与源码中server.listen(3000)一致也与集群 Service 的targetPort: 3000对应启动方式CMD [ npm, start ]对应 package.json 中start: node server.js脚本。依赖清单package.json 声明了运行时依赖dependencies: { body-parser: ^1.20.1, express: ^4.19.2 }仅有两个依赖Express 4 负责 HTTP 路由body-parser 负责解析 POST 请求体JSON 与 urlencoded 两种格式见下节源码。npm start启动入口为node server.js。源码级漏洞原理剖析服务端实现一个不设防的代理infrastructure/internal-api/code/server.js 是全部业务逻辑所在全文不足 50 行const http require(http); const express require(express); const path require(path); const bodyParser require(body-parser); const { spawnSync } require(child_process); const app express(); app.use(bodyParser.urlencoded({ extended: true })); app.use(bodyParser.json()); app.use(express.json()); app.use(express.static(express)); app.get(/, function (req, res) { res.sendFile(path.join(__dirname /index.html)); }); app.post(/, function (req, res) { var endpoint req.body.endpoint, method req.body.method || GET, headers req.body.headers || {}; const child spawnSync(curl, [endpoint, -H, headers, -X, method]); if (child.stdout) { res.send(child.stdout) } else if (child.err) { res.send(child.err) } else { res.send(child.stderr) } // ...被注释掉的 fetch 实现见下文 }); const server http.createServer(app); const port 3000; server.listen(port); console.debug(Server listening on port port);逐行分析可以提炼出三个核心机制1. 请求解析body-parser应用同时启用了body-parser.urlencoded、body-parser.json和express.json()意味着前端既可以提交表单编码数据也可以提交 JSON 数据前端实际使用的是 JSON见 index.html 的fetch调用。2. 关键漏洞spawnSync(curl, ...)命令拼接POST /处理器从请求体中取出三个字段endpoint目标 URL无任何协议白名单、域名校验或 SSRF 防护methodHTTP 方法默认GETheaders自定义请求头。随后这些值被原样拼入spawnSync(curl, [endpoint, -H, headers, -X, method])执行。这里存在两类问题SSRF服务端请求伪造endpoint完全由攻击者控制服务端 curl 可访问云厂商实例元数据服务例如http://169.254.169.254/latest/meta-data/AWS 等平台——这是场景 3 中访问云元数据的关键路径集群内部 DNS 可达的服务例如http://metadata-db、http://metadata-db/latest/secrets/kubernetes-goat——仅集群内可达的 Service 也会被代理访问容器同 Pod 内其他容器、节点网络上的任意地址。命令注入风险面headers参数被直接作为-H参数传入虽然没有经过 shell 解释spawnSync默认以数组参数形式调用不经过 shell不存在直接的 shell 命令注入但从安全审计角度将不可信输入传入子进程始终是高风险模式。3. 输出盲区响应处理逻辑存在一个有趣的缺陷if (child.stdout) { res.send(child.stdout) } else if (child.err) { res.send(child.err) } else { res.send(child.stderr) }分支判断顺序依次为stdout、err、stderr。其中child.err在 Node.js 的spawnSync返回对象中并非标准字段标准字段是error因此实际上当 stdout 为空、stderr 有内容时才可能走到res.send(child.stderr)分支。从源码结构可以推断这属于刻意/无意保留的宽松处理保证绝大多数请求响应无论成功与否都会回传方便攻击者直接看到代理结果。被注释掉的正确实现server.js 末尾保留了一段被注释掉的fetch实现// fetch(endpoint, { // method: method, // headers: headers, // }) // .then(res res.json()) // .then(json res.send(json)) // .catch(json res.send(json));从源码结构看这段注释暗示了作者对通过 curl 子进程发起请求与通过 Node.js fetch 发起请求两种方案的取舍——无论哪种方案只要不对endpoint做校验SSRF 漏洞都依然存在。最终采用spawnSync(curl)的实现更贴近真实世界中为了省事直接调用系统工具的脆弱代码风格这正是本场景希望传递的教学信息。前端交互界面infrastructure/internal-api/code/index.html 是一个基于 Tailwind CSS 的单页界面标题为 Internal API Proxy Service包含三个输入框输入项占位符示例提交后映射到Endpointhttps://api.github.comreq.body.endpointMethodGETreq.body.methodCustom HeaderContent-Type: application/jsonreq.body.headers页面底部的 JavaScript 通过fetch(/, { method: POST, ... })将三个字段以 JSON 形式提交到服务端并把响应文本渲染到 Response Output 区域。值得注意的是默认占位符直接展示了https://api.github.com这样的公网地址说明设计意图是让使用者自由输入任意 URL——包括内网地址与元数据地址。集群内部署Deployment 与 Service该组件在集群中的部署清单位于 scenarios/internal-proxy/deployment.yaml由 Kubernetes Goat 的安装脚本自动应用kubectl apply -f scenarios/internal-proxy/deployment.yaml对应 setup-kubernetes-goat.sh 中的安装步骤。多容器 Pod 设计该 Deployment名为internal-proxy-deployment采用 sidecar 模式一个 Pod 内运行两个容器容器镜像端口资源限制limitsinternal-apimadhuakula/k8s-goat-internal-api3000cpu 50m / memory 60Miinfo-appmadhuakula/k8s-goat-info-app5000cpu 30m / memory 50Miinfo-app容器对应 infrastructure/info-app/与 internal-api 共享同一个 Pod 网络命名空间因此通过代理访问http://127.0.0.1:5000即可触达同 Pod 内的 info-app 服务——这正是场景 3 中探测同容器其他端口服务的第一步见下文实战部分。三种暴露方式清单中定义了三个 Kubernetes 对象覆盖了不同的访问层级Deploymentinternal-proxy-deploymentselector 标签为app: internal-proxy负责 Pod 编排ClusterIP Serviceinternal-proxy-api-service端口 3000 → targetPort 3000仅集群内可达供 SSRF 场景中由攻击者借道访问NodePort Serviceinternal-proxy-info-app-service端口 5000 → targetPort 5000nodePort: 30003将 info-app 暴露到集群节点端口便于直接访问验证。其中 internal-api 本体没有直接暴露 NodePort对外访问依赖 access-kubernetes-goat.sh 中的端口转发export POD_NAME$(kubectl get pods --namespace default -l appinternal-proxy -o jsonpath{.items[0].metadata.name}) kubectl port-forward $POD_NAME --address 0.0.0.0 1232:3000 /dev/null 21 即将 Pod 的 3000 端口转发到本机 1232 端口对应 access-kubernetes-goat.sh使学习者通过浏览器访问http://127.0.0.1:1232即可打开 Internal API Proxy 界面。实战场景SSRF 到云元数据与集群内服务在 Kubernetes Goat 中internal-api 组件是场景 3SSRF的主战场。该场景的完整演练文档位于 guide/docs/scenarios/scenario-3/scenario-3.md其核心故事是利用应用漏洞SSRF访问云实例元数据以及集群内部服务元数据信息。场景入口与目标场景入口通过端口转发访问http://127.0.0.1:1232即 Internal API Proxy 界面场景目标在 metadata secrets 中获取k8s-goat-FLAG标志值。攻击路径三步走第一步探测同 Pod 内其他服务利用代理访问http://127.0.0.1:5000方法 GET可以确认同 Pod 内的info-app服务正在运行并返回 HTTP 响应。这一步验证了 Pod 网络命名空间共享这一 Kubernetes 特性同时为后续扩大攻击面提供了信息基础。第二步借道集群 DNS 访问内网 ServiceKubernetes 原生服务发现为攻击者提供了便利任意 Service 都可以通过servicename.namespace.svc.cluster.local或简写形式同命名空间下直接用服务名在集群内被解析。利用代理访问http://metadata-db可以触达一个仅集群内可达的元数据微服务。第三步枚举密钥并解码标志对metadata-db返回的键值逐层枚举后最终在http://metadata-db/latest/secrets/kubernetes-goat端点发现 Base64 编码的标志值echo -n azhzLWdvYXQtY2E5MGVmODVkYjdhNWFlZjAxOThkMDJmYjBkZjljYWI | base64 -d解码后可得到明文标志格式为k8s-goat-*。metadata-db组件的实现见 infrastructure/metadata-db/一个用 Go 编写的模拟云元数据服务的微服务。云元数据探测可选分支场景文档同时指出169.254.169.254是各主流云厂商AWS、GCP、Azure、Digital Ocean 等提供的实例元数据地址。若集群运行在真实云环境上攻击者可以继续利用代理访问http://169.254.169.254/latest/meta-data/获取实例元数据若运行在本地如 kind、minikube则跳过此步专注于集群内部服务枚举。安全启示与加固建议从该组件的实现可以提炼出对真实世界云原生应用的安全启示任何转发/代理类服务都必须做目标校验对endpoint实施协议白名单仅允许 http/https、域名/IP 黑名单封锁169.254.169.254等链路本地地址、集群内网网段、metadata-db等敏感服务名或维护显式的允许列表避免将不可信输入直接拼入子进程命令即使使用spawnSync数组形式不经 shell规避了直接的命令注入也应优先使用语言内建的 HTTP 客户端并对重定向curl 的-L与 DNS 重绑定攻击保持警惕最小化暴露面internal-api 仅通过 ClusterIP 在集群内暴露外部依赖临时port-forward访问——这种内网服务不直接暴露到公网的默认配置正是值得在真实环境中推广的做法。Kubernetes Goat 的安全扫描报告如 guide/docs/security-reports/checkov.md中针对internal-proxy/deployment.yaml也列出了多项 Checkov 检查项包括缺少安全上下文、未固定镜像标签、使用默认命名空间、缺少存活/就绪探针等可以作为进一步加固该部署清单的参考清单。小结Internal API 组件虽然代码量极小却浓缩了 Kubernetes Goat 的核心教学价值一个真实风格的脆弱服务如何在集群网络中被一步步利用——从容器内同 Pod 探测到 Kubernetes 服务发现再到云元数据访问最终拿到标志值。理解其镜像构建、源码实现与部署清单你就能在本地复现完整的 SSRF 攻防链路并将同样的防护意识迁移到真实业务中。继续深入可以查看 场景 3 完整文档 以及同系列的 场景 16RBAC 越权后者展示了从该服务账户出发的另一种提权路径。【免费下载链接】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),仅供参考