Skip to content

[New Rule] GCP IAM Service Account Impersonation Role Granted#6215

Open
Aryu-RU wants to merge 4 commits into
elastic:mainfrom
Aryu-RU:add-gcp-service-account-impersonation-rule
Open

[New Rule] GCP IAM Service Account Impersonation Role Granted#6215
Aryu-RU wants to merge 4 commits into
elastic:mainfrom
Aryu-RU:add-gcp-service-account-impersonation-rule

Conversation

@Aryu-RU
Copy link
Copy Markdown

@Aryu-RU Aryu-RU commented May 30, 2026

Issue link(s): None yet. Per CONTRIBUTING, new rules ideally start from a New rule issue — happy to open one if preferred; a clear summary is provided below.

Summary - What I changed

Adds a new new_terms detection rule, GCP IAM Service Account Impersonation Role Granted, under rules/integrations/gcp/.

The rule fires when a SetIamPolicy operation on a GCP service account (google.iam.admin.v1.SetIAMPolicy) grants an impersonation role — roles/iam.serviceAccountTokenCreator, roles/iam.serviceAccountUser, or roles/iam.serviceAccountOpenIdTokenCreator. These grants let a principal mint access/identity tokens for, or actAs, the target service account, enabling privilege escalation to a higher-privileged identity and key-less persistence that survives credential rotation.

Because the GCP integration maps protoPayload.request/response to the flattened fields gcp.audit.request / gcp.audit.response (and does not surface serviceData.policyDelta), the granted role is read from gcp.audit.response.bindings.role (and gcp.audit.request.policy.bindings.role when present). The two queried sub-fields are registered in detection_rules/etc/non-ecs-schema.json under logs-gcp*, mirroring how the Kubernetes rules register kubernetes.audit.requestObject.* sub-fields.

Why it's novel (dedup): No existing GCP rule references SetIamPolicy, impersonation roles, or token-creator grants. The closest rule, persistence_gcp_key_created_for_service_account.toml, only catches CreateServiceAccountKey — a policy-binding backdoor never creates a key, so that rule does not fire. Verified across all files in rules/integrations/gcp/.

Scoping / FP notes: Tightly scoped to the specific method plus three named impersonation roles, and uses new_terms on (principal_email, resource_name) over a 14-day window to suppress steady-state IaC/CI-CD noise. Known false positives (Terraform/CI-CD/onboarding grants) are documented in false_positives and the investigation guide, with exception guidance.

MITRE: T1098 / T1098.003 (Additional Cloud Roles) under Persistence (TA0003) and Privilege Escalation (TA0004).

How To Test

  • python -m detection_rules validate-rule rules/integrations/gcp/persistence_gcp_service_account_impersonation_role_grant.toml passes.
  • python -m detection_rules test / pytest tests/ — full suite green locally (219 passed, 19 skipped).
  • In-stack: grant roles/iam.serviceAccountTokenCreator to a test principal on a service account and confirm the resulting gcp.audit SetIAMPolicy event matches the rule.

Checklist

  • Added a label for the type of pr: Rule: New
  • Automated testing was updated or added to match the most common scenarios (non-ECS schema entry added; full suite passes)
  • Documentation and comments were added for features that require explanation (investigation guide and false-positive guidance included)

Contributor checklist

  • Have you signed the contributor license agreement? Yes
  • Have you followed the contributor guidelines? Yes

References

@cla-checker-service
Copy link
Copy Markdown

cla-checker-service Bot commented May 30, 2026

💚 CLA has been signed

@Aryu-RU
Copy link
Copy Markdown
Author

Aryu-RU commented May 30, 2026

Signed the CLA

Copy link
Copy Markdown
Contributor

@Samirbous Samirbous left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Aryu-RU do you have an example of test event log?

@Aryu-RU
Copy link
Copy Markdown
Author

Aryu-RU commented Jun 1, 2026

Thanks @Samirbous — applied both suggestions:

  • Unquoted event.action so the v* version wildcard matches, consistent with the other GCP rules.
  • Switched the new terms field to ["user.id"]. Good catch — I confirmed in the gcp.audit ingest pipeline that authenticationInfo.principalEmail/principalSubject are renamed to user.email/user.id (and gcp.audit.authentication_info is emptied), so keying on the actor's user.id is the right, populated field; the target service account in resource_name added no value as you noted.

Example event — a service-account-level SetIAMPolicy granting roles/iam.serviceAccountTokenCreator. To be transparent: this is a representative event built from Google's documented Cloud Audit Log structure and cross-checked against the gcp.audit pipeline test fixtures in elastic/integrations; it is not captured from a live cluster, as I don't have a GCP → Elastic pipeline running. The field shapes match what the integration produces.

Raw Cloud Audit Log:

{
  "protoPayload": {
    "@type": "type.googleapis.com/google.cloud.audit.AuditLog",
    "authenticationInfo": {
      "principalEmail": "intruder@example.com",
      "principalSubject": "user:intruder@example.com"
    },
    "requestMetadata": {
      "callerIp": "203.0.113.74",
      "callerSuppliedUserAgent": "google-cloud-sdk gcloud/470.0.0"
    },
    "serviceName": "iam.googleapis.com",
    "methodName": "google.iam.admin.v1.SetIAMPolicy",
    "authorizationInfo": [
      { "resource": "projects/-/serviceAccounts/103515", "permission": "iam.serviceAccounts.setIamPolicy", "granted": true }
    ],
    "resourceName": "projects/-/serviceAccounts/deploy-prod@my-project.iam.gserviceaccount.com",
    "request": {
      "@type": "type.googleapis.com/google.iam.v1.SetIamPolicyRequest",
      "resource": "projects/-/serviceAccounts/deploy-prod@my-project.iam.gserviceaccount.com",
      "policy": {
        "bindings": [
          { "role": "roles/iam.serviceAccountTokenCreator", "members": ["user:intruder@example.com"] }
        ]
      }
    },
    "response": {
      "@type": "type.googleapis.com/google.iam.v1.Policy",
      "bindings": [
        { "role": "roles/iam.serviceAccountTokenCreator", "members": ["user:intruder@example.com"] }
      ]
    }
  },
  "resource": { "type": "service_account", "labels": { "project_id": "my-project", "email_id": "deploy-prod@my-project.iam.gserviceaccount.com" } },
  "timestamp": "2026-06-01T10:15:30.123456Z",
  "severity": "NOTICE",
  "logName": "projects/my-project/logs/cloudaudit.googleapis.com%2Factivity"
}

After the GCP integration normalizes it, the rule matches on event.action: google.iam.admin.v1.SetIAMPolicy, event.outcome: success, and gcp.audit.request.policy.bindings.role: roles/iam.serviceAccountTokenCreator (also present in gcp.audit.response.bindings.role), with user.id: "user:intruder@example.com" as the new-terms value.

@Aryu-RU Aryu-RU requested a review from Samirbous June 1, 2026 09:46
Aryu-RU added 2 commits June 1, 2026 17:56
New Terms rule that flags when an impersonation role
(serviceAccountTokenCreator, serviceAccountUser, or
serviceAccountOpenIdTokenCreator) is granted on a service account via
SetIamPolicy. Such grants let a principal mint tokens for or act as the
target service account, enabling privilege escalation and key-less
persistence. Registers the flattened request/response policy binding
role sub-fields in the non-ECS schema so the query validates.
Unquote the SetIAMPolicy event.action so the version wildcard matches,
and key the new terms field on user.id (the authenticated principal).
The GCP audit pipeline renames principalEmail/principalSubject to
user.email/user.id and empties gcp.audit.authentication_info, so the
previous principal_email field would not be populated. Updates the
investigation guide accordingly.
@Aryu-RU Aryu-RU force-pushed the add-gcp-service-account-impersonation-rule branch from 47a7864 to 0cead63 Compare June 1, 2026 09:57
Copy link
Copy Markdown
Contributor

@Samirbous Samirbous left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thank you for the adjustments, left few suggestion, otherwise LGTM.

Comment thread detection_rules/etc/non-ecs-schema.json Outdated
Use the logs-gcp.audit-* data stream index (and matching non-ECS schema
key), reduce the new terms history window to now-10d, add contributor to
the author list, and update the description wording.
@Aryu-RU
Copy link
Copy Markdown
Author

Aryu-RU commented Jun 1, 2026

Thanks for the review and approval @Samirbous! Applied all the suggestions:

  • index["logs-gcp.audit-*"] (and updated the matching non-ecs-schema.json key).
  • history_window_startnow-10d.
  • Added myself to the author array.
  • Reworded the description (dropped the stale "principal and target service account pair" phrasing now that the new terms field is just user.id).

validate-rule and the rule test suite pass locally.

@Aryu-RU Aryu-RU requested a review from Samirbous June 1, 2026 10:59
account, or to act as it when deploying resources. Adversaries who have obtained sufficient privileges may grant
themselves or an attacker-controlled principal one of these roles to impersonate a higher-privileged service account,
escalating privileges and establishing durable, key-less persistence that survives credential rotation. This is a New
Terms rule that alerts when the granting principal has not been observed performing this action in the last weeks.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Terms rule that alerts when the granting principal has not been observed performing this action in the last weeks.
Identifies when a service account impersonation role is granted on a Google Cloud Platform (GCP) service account via a
SetIamPolicy operation. Roles such as "roles/iam.serviceAccountTokenCreator", "roles/iam.serviceAccountUser", and
"roles/iam.serviceAccountOpenIdTokenCreator" allow a principal to mint access or identity tokens for the target service
account, or to act as it when deploying resources. Adversaries who have obtained sufficient privileges may grant
themselves or an attacker-controlled principal one of these roles to impersonate a higher-privileged service account,
escalating privileges and establishing durable, key-less persistence that survives credential rotation. This is a New

No markdown rendering in rule description field.

Comment on lines +72 to +75

## Setup

The GCP Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule."""
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
## Setup
The GCP Fleet integration, Filebeat module, or similarly structured data is required to be compatible with this rule."""
"""

A build time field related_integrations will be added to this rule artifact at build time. We can remove this setup note. If you'd like to keep it, just move to dedicated setup field.

See python -m detection_rules view-rule RULEPATH for related_integrations field if curious.

tags = [
"Domain: Cloud",
"Data Source: GCP",
"Data Source: Google Cloud Platform",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"Data Source: Google Cloud Platform",
"Data Source: Google Cloud Platform",
"Data Source: GCP Audit Logs",

Copy link
Copy Markdown
Contributor

@terrancedejesus terrancedejesus left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition, thanks @Aryu-RU !

Add the GCP Audit Logs data source tag, replace markdown backticks with
quotes in the description (which does not render markdown), and drop the
setup note since related_integrations is added at build time.
@Aryu-RU
Copy link
Copy Markdown
Author

Aryu-RU commented Jun 4, 2026

Thanks @terrancedejesus — all three applied in c020944:

  • Description: replaced the markdown backticks around the role names with double quotes, since the description field doesn't render markdown.
  • Setup note: removed it (going with related_integrations at build time rather than a dedicated setup field).
  • Tags: added "Data Source: GCP Audit Logs" (placed alongside the existing Data Source: GCP / Google Cloud Platform tags to match the other GCP rules).

validate-rule and the rule test suite pass locally.

@Aryu-RU Aryu-RU requested a review from terrancedejesus June 4, 2026 14:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants