GCP service account impersonation: tracing the chain
Trace service account impersonation in Google Cloud audit logs: GenerateAccessToken, SignBlob, serviceAccountDelegationInfo, actAs and Token Creator grants.
TL;DR. Service account impersonation leaves two traces. The minting call (GenerateAccessToken, GenerateIdToken, SignBlob, SignJwt on iamcredentials.googleapis.com) is a Data Access log, so it only exists if IAM Data Access logging was on. The use of the token appears in the target service's logs with principalEmail set to the service account and the real caller in authenticationInfo.serviceAccountDelegationInfo. Read both, and look for who was granted Token Creator or Service Account User in the first place.
Impersonation is legitimate and even recommended: it replaces downloadable keys with short-lived tokens. It is also one of the most used privilege-escalation paths in Google Cloud, because the permission that allows it, iam.serviceAccounts.getAccessToken, is often granted broadly. Rhino Security Labs' GCP privilege escalation research lists getAccessToken, signBlob, signJwt and implicitDelegation as separate escalation methods.
Impersonation versus using a key
Google draws the line clearly in its impersonation documentation: impersonation involves two identities (the authenticated principal and the service account), whereas authenticating with a key involves only one. For an investigator:
| Mechanism | What identifies it in the logs |
|---|---|
| User-managed key | authenticationInfo.serviceAccountKeyName (see leaked key) |
| Impersonation (token minted via IAM Credentials API) | serviceAccountDelegationInfo on the calls, GenerateAccessToken in Data Access logs |
| Attached service account (VM, Cloud Run, Cloud Functions) | Calls as the service account, typically from the workload's IP, no key name, no delegation |
| Workload Identity Federation | principalSubject like principal://… or delegation info with a third-party principal |
The third row matters: a token stolen from a VM's metadata server (T1552.005) is not impersonation in the logs; it looks like the VM's service account, but called from somewhere else.
Trace 1: the minting call
According to Google's Service Account Credentials audit logging page, these methods are recorded under cloudaudit.googleapis.com/data_access with service iamcredentials.googleapis.com. Enabling Data Access audit logs for the IAM API also enables them for this API.
protoPayload.serviceName="iamcredentials.googleapis.com"
protoPayload.methodName=("GenerateAccessToken" OR "GenerateIdToken" OR "SignBlob" OR "SignJwt")
In these entries, principalEmail is the caller (the real actor, or the previous link in the chain) and the target service account appears in resource.labels.email_id and resourceName, as shown in Google's example logs. SignBlob and SignJwt deserve special attention: signing a JWT as a service account can be exchanged for a token, and signing blobs can forge signed URLs.
Trace 2: the calls made with the token
Any API call made with an impersonated token carries the chain:
"authenticationInfo": {
"principalEmail": "deployer@PROJECT.iam.gserviceaccount.com",
"serviceAccountDelegationInfo": [
{ "firstPartyPrincipal": { "principalEmail": "alice@example.com" } }
]
}
This is available in Admin Activity logs, which are always on. So even when the minting call is invisible, the writes made with the token still name the real caller. That is often the only trace you get.
Chains
Impersonation can be chained: A impersonates B, which impersonates C (the delegates parameter of GenerateAccessToken; each hop needs permission on the next service account, which the Token Creator role provides). The chain is recorded in serviceAccountDelegationInfo as an array. When you rebuild it:
- Start from the final call and read the delegation list.
- For each hop, look for the IAM binding that allowed it: who holds
roles/iam.serviceAccountTokenCreatoron which service account, and since when. - Check whether any of those bindings were added during the incident window (
bindingDeltasADD, see SetIamPolicy abuse).
The third step is where attacks show: a compromised low-privilege identity grants itself Token Creator on a powerful service account, then mints tokens for it (T1098.003, then T1550.001).
actAs: impersonation through a resource
The Service Account User role (roles/iam.serviceAccountUser) grants iam.serviceAccounts.actAs: the right to attach a service account to a resource. Someone with actAs on a powerful service account and compute.instances.create can start a VM running as that account and read its token from the metadata server. Google's example logs show the check as an authorizationInfo entry with "permission": "iam.serviceAccounts.actAs" and "granted": true on the resource creation call.
So when reviewing VM, Cloud Run or Cloud Functions creations during an incident, read authorizationInfo and the service account set in the request, not only who created the resource.
Red flags
- A human user or an external IP minting tokens for a service account that is normally only used by a workload.
GenerateAccessTokencalls that start right after a Token Creator grant.- Long chains with a service account in the middle that nobody owns.
SignJwtorSignBlobfrom principals that have no reason to sign anything.- Calls with delegation info from a principal outside your domains.
What the analyzer shows
In the analyzer:
sa_impersonation(medium, privilege escalation, T1550.001) fires on successfulGenerateAccessToken,GenerateIdToken,SignBlobandSignJwtcalls, excluding Google-managed service agents.token_creator_granted(high) fires onADDof Token Creator, Service Account User, Service Account Key Admin or Workload Identity User.- The Entities tab shows, for each principal, the identities it was impersonated by (from
serviceAccountDelegationInfo), and the log entry detail shows "Impersonated by".
The limit is the one above: without IAM Data Access logs, only the second trace exists, and the tool warns when an export contains no Data Access logs at all. See Data Access logs limitations.
Remediation
Remove the Token Creator / Service Account User bindings nobody needs, prefer granting them on individual service accounts rather than on the project, and review which principals can impersonate your most privileged service accounts. Google's service account permissions page lists what each role allows.
Frequently asked questions
How do I see who impersonated a service account in Google Cloud?
Two places. The token minting call (GenerateAccessToken, GenerateIdToken, SignBlob, SignJwt on iamcredentials.googleapis.com) is a Data Access log whose principalEmail is the real caller. Calls made with the resulting token list the real caller in authenticationInfo.serviceAccountDelegationInfo.
Why can't I find GenerateAccessToken in my logs?
Because it is recorded as a Data Access audit log (ADMIN_READ), which is off by default. Enabling Data Access logs for the IAM API also enables them for the Service Account Credentials API.
Further reading
- Service account impersonation — Google Cloud documentation.
- Example logs for service accounts — Google Cloud documentation.
- MITRE ATT&CK T1550.001, Application Access Token.