GCP service account impersonation: borrowing identity in Google Cloud
In GCP, the right to generate tokens for a service account lets an attacker act as it, no key required. Here is how impersonation and key abuse chain up the IAM hierarchy toward project owner, why it is so quiet, and how to bound it.
In Google Cloud, service accounts are the workhorse identities - the non-human principals that run your workloads - and the way they are abused is not theft, it is impersonation. With the right permission, an identity can generate credentials for a service account and act as it, inheriting its roles, often without ever touching a key file. The attacker does not pick the lock; they are handed the authority to become the key.
The mental model that matters for GCP privilege escalation is that the dangerous asset is not the service account key, it is the permission to impersonate. Defenders instinctively hunt for leaked keys - static secrets in a repo, an exported JSON credential - and those are real. But the quieter, more powerful path needs no key at all, and it is granted by an IAM role that looks innocuous until you trace what it can reach. If you are only guarding the keys, you are guarding the loud half.
How does impersonation actually work?
Through a specific permission that mints credentials on demand. iam.serviceAccounts.getAccessToken, granted by roles such as Service Account Token Creator, lets a principal request a short-lived access token for a service account and then operate as it. An attacker who lands an identity holding that role over a more privileged service account simply asks for a token and inherits everything that account can do - no key file to steal, no password to crack, just an API call that GCP is designed to answer.
There are related primitives that widen the toolkit. signJwt and getOpenIdToken let a principal sign assertions or mint identity tokens as the service account, useful for reaching services that trust those. And the older, cruder path still exists: exporting a long-lived service account key as a static JSON credential. All of it is valid-account abuse, T1078.004, in a Google-specific form - the attacker is not exploiting a bug, they are using identity features exactly as built, from an identity that should never have had that reach.
Why does it chain up the hierarchy so well?
Because GCP IAM inherits downward, so a single well-placed grant becomes reach over everything beneath it. Roles granted at the organization, folder, or project level flow down to the resources under them. A Service Account Token Creator role assigned high in the hierarchy, or a service account that itself holds broad project roles, turns a modest starting identity into a ladder: impersonate the first account, inherit its roles, use those to impersonate a more privileged account, and climb toward project owner - and, because the hierarchy spans projects, hop sideways into projects the original identity never had any business in.
Exported keys make the whole thing worse, because a long-lived static secret that leaks is a permanent, portable foothold that does not expire the way an impersonation token does. The combination - inheritable grants plus static keys - is why a foothold in a low-value corner of an organization can escalate to owner and to Cloud Storage exfiltration across projects. The escalation is not a chain of exploits; it is a chain of legitimate impersonations that IAM permitted because the grants were too broad and too high.
What does an impersonation chain look like end to end?
Concretely, it is a series of legitimate token requests that each climb one rung. The attacker starts with a modest foothold identity - a compromised developer account, a leaked credential for a low-privilege service account, an over-permissioned CI principal. That identity happens to hold Service Account Token Creator over a second service account, so the attacker calls generateAccessToken and is now operating as the second account, with its roles.
The second account, it turns out, was granted a broad role at the project level for convenience - or holds token-creator over a third, more privileged account. Either way the attacker inherits more reach, and repeats the move. Because IAM roles inherit down the org, folder, and project hierarchy, a token-creator grant placed high up means one of these hops lands on an account with reach across multiple projects. A few impersonations later the attacker is operating as an identity with project owner, and from there they read the Cloud Storage buckets and exfiltrate.
Nothing in that chain is an exploit. Every step is an API GCP is designed to answer, called by an identity that was granted the reach - just granted it too broadly, or too high in the hierarchy, or over an account that itself held too much. The chain is the sum of individually defensible grants that no one traced end to end. That is why the defense is to audit the grants as a graph of who can become whom, not to look for a single bad permission.
Why is it so quiet?
Because every step uses normal IAM APIs and produces tokens that look like the service account doing its job. There is no malformed call, no exploit signature, no anomaly at the protocol level. A getAccessToken request is the same request automation makes constantly, and the activity performed with the resulting token is indistinguishable from the service account’s legitimate work - unless you have established which principals are supposed to impersonate which accounts.
That baseline is what most environments lack, and its absence is what makes impersonation stealthy. Without knowing that principal A never legitimately impersonates service account B, a token-creation call from A to B is just another line in the audit log. The attacker is hiding in the space between “this is a normal API” and “no one is checking who is calling it about whom.” Detection is therefore less about spotting bad requests and more about knowing what normal impersonation looks like so the abnormal stands out.
How do you detect it?
By auditing the grants and alerting on the calls, against a baseline of expected impersonation. Start with the standing risk: audit who holds Service Account Token Creator and over which accounts, because that inventory is the map of who can become whom. Then instrument the activity: alert on generateAccessToken and signJwt calls, especially those targeting privileged service accounts, and treat a principal impersonating an account it has no baseline reason to touch as a lead. Watch for new service account key creation, since a fresh exported key is often either the setup or the persistence for this attack. And review Cloud Audit Logs for impersonation chains and cross-project access - the tell of escalation is a sequence of impersonations climbing toward owner or reaching into a new project.
How do you contain and prevent it?
Contain by cutting the borrowed authority, then bound the primitives so it cannot recur. In the moment: remove the token-creator grant the attacker used, disable the abused service account or rotate and delete its keys, and review what it accessed - the Cloud Storage reads, the cross-project calls - to scope the exfiltration. Cutting the token-creator path is the equivalent of revoking the session, because it is the authority the attacker is actually using.
To prevent it, bound the design. Prefer short-lived impersonation over exported long-lived keys, so there is no static secret to leak. Grant Service Account Token Creator narrowly - per specific account, to specific principals - and never broadly or high in the hierarchy where it inherits into reach over everything. Disable service account key creation with an org policy in the environments where you can, removing the crude static-key path entirely. And apply least privilege at the project level rather than at the org or folder level, so a single grant does not flow down into projects it was never meant to touch. The through-line for GCP is that the key you most need to guard is not a key at all - it is the permission to become someone else, and it is only as safe as it is narrow.
Frequently asked questions
What is GCP service account impersonation?
It is using the permission iam.serviceAccounts.getAccessToken - granted by roles such as Service Account Token Creator - to mint short-lived credentials for a service account and operate as it, inheriting its roles. Unlike stealing an exported key, impersonation needs no key file; the principal simply requests a token. Related primitives include signJwt and getOpenIdToken. In ATT&CK terms it is valid-account abuse, T1078.004, with a Google-specific shape.
Why does impersonation chain so well in GCP?
Because GCP IAM is hierarchical - roles granted at the organization, folder, or project level inherit downward. A token-creator role high in the hierarchy, or a service account that itself holds broad project roles, lets an attacker hop from a modest identity to a powerful one and then across projects. Exported keys make it worse, since they are long-lived static secrets that can leak and be reused.
Why is service account impersonation hard to detect?
Because it uses normal IAM APIs and produces tokens that look exactly like the service account doing its ordinary job. There is no malformed request and no obvious anomaly unless you have baselined which principals legitimately impersonate which accounts. Without that baseline, the getAccessToken call and the resulting activity blend into normal automation.
How do you detect and investigate it?
Audit who holds Service Account Token Creator and over which accounts, and alert on generateAccessToken and signJwt calls, especially toward privileged service accounts. Watch for new service account key creation, and review Cloud Audit Logs for impersonation chains and cross-project access. The signal is a principal impersonating an account it has no baseline reason to, or a chain of impersonations climbing toward owner.
How do you prevent it?
Bound the primitives. Prefer short-lived impersonation over exported long-lived keys, grant Service Account Token Creator narrowly (per specific account, never broadly or high in the hierarchy), disable service account key creation with an org policy where you can, and apply least privilege at the project level rather than at the org or folder level so roles do not inherit down to everything.