Summary
| Forestall ACL Alias | GCP_SET_SA_IAMPOLICY |
| GCP Alias | IAM & Hierarchy Control |
| Affected Object Types | Service Accounts |
| Exploitation Certainty | Certain |
| Granting Roles | roles/owner, roles/iam.securityAdmin, roles/iam.serviceAccountAdmin |
Description
GCP_SET_SA_IAMPOLICY indicates that an identity can call setIamPolicy on a specific GCP Service Account resource. Unlike setting IAM policy at the project or org level, this edge targets a single service account’s allow policy — but the impact is equally severe when the target SA holds elevated privileges.
The primary exploitation path is granting roles/iam.serviceAccountTokenCreator on the target SA to a controlled identity. This allows the attacker to generate short-lived OAuth2 access tokens and ID tokens for the target SA from anywhere, without needing to be running on GCP infrastructure.
Key abuse scenarios:
- Grant
roles/iam.serviceAccountTokenCreatoron a high-privilege SA → directly generate tokens (GCP_IMPERSONATE_SA). - Grant
roles/iam.serviceAccountUser→ attach the SA to a VM you spin up (GCP_ACT_AS_SA). - Add a backdoor identity as a Service Account Admin for persistence.
Identification
gcloud CLI
# List IAM policy on a specific service accountPROJECT_ID="my-project"SA_EMAIL="high-priv@${PROJECT_ID}.iam.gserviceaccount.com"gcloud iam service-accounts get-iam-policy $SA_EMAIL --format=json | \ jq '.bindings[] | {role: .role, members: .members}'# Find all service accounts in a project and their IAM policiesfor SA in $(gcloud iam service-accounts list --project=$PROJECT_ID --format="value(email)"); do echo "=== $SA ===" gcloud iam service-accounts get-iam-policy $SA --format=json 2>/dev/null | \ jq '.bindings[]? | {role: .role, members: .members}'doneGCP Console
- Open GCP Console → IAM & Admin → Service Accounts.
- Click on the target service account.
- Select the Principals with access tab to view who has access to this SA.
Exploitation
gcloud CLI
# Grant token creator on a privileged SA to escalategcloud iam service-accounts add-iam-policy-binding $SA_EMAIL \ --role="roles/iam.serviceAccountTokenCreator"
# Now generate a token for the high-privilege SAgcloud auth print-access-token --impersonate-service-account=$SA_EMAILCompound path: GCP_SET_SA_IAMPOLICY → grant roles/iam.serviceAccountTokenCreator → GCP_IMPERSONATE_SA → call any GCP API as the target SA.
Mitigation
- Restrict
roles/iam.serviceAccountAdmin— this role includessetIamPolicyon service accounts; assign only to privileged automation. - Audit SA-level IAM policies for unexpected members:
for SA in $(gcloud iam service-accounts list --project=$PROJECT_ID --format="value(email)"); doPOLICY=$(gcloud iam service-accounts get-iam-policy $SA --format=json 2>/dev/null)echo "$POLICY" | jq --arg sa "$SA" '. + {sa: $sa} | select(.bindings | length > 0)'done
- Use Workload Identity Federation instead of SA keys to reduce the need for broad SA IAM grants.
- Limit service account creation with
constraints/iam.disableServiceAccountCreationwhere applicable.
Detection
| Log Type | Method | Key Fields |
|---|---|---|
| Admin Activity | SetIamPolicy |
resource.type=service_account, methodName=google.iam.admin.v1.SetIamPolicy |
gcloud logging read \ 'resource.type="service_account" AND protoPayload.methodName="google.iam.admin.v1.SetIamPolicy"' \ --project=$PROJECT_ID \ --format="table(timestamp, protoPayload.authenticationInfo.principalEmail, resource.labels.email_id)"Alert on:
SetIamPolicyon service accounts by non-automation identities.- New
roles/iam.serviceAccountTokenCreatororroles/iam.serviceAccountUserbindings on privileged SAs.