> For the complete documentation index, see [llms.txt](https://docs.forestall.io/forestall/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/forestall/edges/gcp/gcp_create_sa_keys.md).

# GCP\_CREATE\_SA\_KEYS

## Summary

|                            |                                                                   |
| -------------------------- | ----------------------------------------------------------------- |
| **Forestall ACL Alias**    | GCP\_CREATE\_SA\_KEYS                                             |
| **GCP Alias**              | Persistence & Impersonation                                       |
| **Affected Object Types**  | Service Accounts                                                  |
| **Exploitation Certainty** | Certain                                                           |
| **Granting Roles**         | `roles/owner`, `roles/editor`, `roles/iam.serviceAccountKeyAdmin` |

## Description

`GCP_CREATE_SA_KEYS` indicates that an identity can call `iam.serviceAccounts.keys.create` on a GCP **Service Account**. This is one of the most impactful persistence mechanisms in GCP: creating a service account key produces a long-lived JSON credential file that is valid until explicitly revoked, has no expiry by default, and can be used from anywhere on the internet — completely independent of GCP's infrastructure.

Unlike short-lived tokens (which expire in 1 hour), a SA JSON key represents **untethered persistence**. An attacker who creates and exfiltrates a key retains access even after being detected and removed from IAM policy bindings, until the key itself is revoked.

**Key abuse scenarios:**

* Create a new JSON key for a high-privilege SA and exfiltrate it for persistent access.
* Backdoor a high-privilege SA with a key under attacker control before being removed.
* Create keys on multiple SAs for redundant persistence.

## Identification

### gcloud CLI

```bash
# Find who has SA key creation rights at project level
PROJECT_ID="my-project"
gcloud projects get-iam-policy $PROJECT_ID --format=json | \
  jq '.bindings[] | select(.role | test("owner|editor|serviceAccountKeyAdmin")) | {role: .role, members: .members}'

# List existing keys for all service accounts (look for user-managed keys)
for SA in $(gcloud iam service-accounts list --project=$PROJECT_ID --format="value(email)"); do
  KEYS=$(gcloud iam service-accounts keys list --iam-account=$SA --managed-by=user --format="table(name, validAfterTime, validBeforeTime)" 2>/dev/null)
  if [ ! -z "$KEYS" ]; then
    echo "=== $SA ==="
    echo "$KEYS"
  fi
done
```

```bash
# Check for recently created keys (last 7 days)
gcloud logging read \
  'protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"' \
  --project=$PROJECT_ID \
  --freshness=7d \
  --format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.resourceName)"
```

### GCP Console

1. Open **GCP Console** → **IAM & Admin** → **Service Accounts**.
2. Click on a service account.
3. Select the **Keys** tab to see all user-managed keys and their creation dates.

## Exploitation

### gcloud CLI

```bash
# Create a new key for a high-privilege service account
SA_EMAIL="high-priv@target-project.iam.gserviceaccount.com"
gcloud iam service-accounts keys create /tmp/stolen_key.json \
  --iam-account=$SA_EMAIL

# Verify the key works
gcloud auth activate-service-account --key-file=/tmp/stolen_key.json
gcloud auth print-access-token

# After exfiltration, use the key from any machine
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/stolen_key.json
# Now all gcloud/API calls authenticate as the target SA
gcloud projects list
```

The JSON key works from any internet-connected machine and persists until explicitly revoked — even if the original attacker identity is removed from all IAM policies.

## Mitigation

1. **Disable SA key creation with org policy:** `constraints/iam.disableServiceAccountKeyCreation`.
2. **Use Workload Identity Federation** instead of SA keys for non-GCP workloads.
3. **Audit and rotate existing user-managed keys** — delete any key not tied to an active use case.
4. **Set key expiry policies** using `constraints/iam.serviceAccountKeyExpiryHours`.
5. **Alert on key creation** immediately.

## Detection

| Log Type       | Method                    | Key Fields                                                                                |
| -------------- | ------------------------- | ----------------------------------------------------------------------------------------- |
| Admin Activity | `CreateServiceAccountKey` | `resource.type=service_account`, `methodName=google.iam.admin.v1.CreateServiceAccountKey` |

```bash
gcloud logging read \
  'protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"' \
  --project=$PROJECT_ID \
  --format="table(timestamp, protoPayload.authenticationInfo.principalEmail, protoPayload.resourceName)"
```

Alert on:

* Any `CreateServiceAccountKey` call — key creation should be rare and intentional.
* Key creation on high-privilege SAs (Cloud Build SA, default compute SA, owner-bound SAs).
* Key creation outside of known automation pipelines.

## References

* <https://cloud.google.com/iam/docs/creating-managing-service-account-keys>
* <https://cloud.google.com/iam/docs/best-practices-service-accounts#manage-keys>
* <https://cloud.google.com/iam/docs/workload-identity-federation>
