> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/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/fsprotect/edges/gcp/gcp_can_create_sa_keys.md).

# GCP\_CAN\_CREATE\_SA\_KEYS

## Summary

|                            |                                                                   |
| -------------------------- | ----------------------------------------------------------------- |
| **FSProtect ACL Alias**    | GCP\_CAN\_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_CAN_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>


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/gcp/gcp_can_create_sa_keys.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
