GCP Workforce Pool Created or Updated


Query · python

METHODS = [
    "google.iam.admin.v1.WorkforcePools.CreateWorkforcePool",
    "google.iam.admin.v1.WorkforcePools.UpdateWorkforcePool",
]


def rule(event):
    return event.deep_get("protoPayload", "methodName", default="") in METHODS


def title(event):
    actor = event.deep_get(
        "protoPayload", "authenticationInfo", "principalEmail", default="<ACTOR_NOT_FOUND>"
    )
    workforce_pool = event.deep_get(
        "protoPayload", "request", "workforcePool", "name", default=""
    ).split("/")[-1]

    resource = organization_id = event.get("logName", "<LOG_NAME_NOT_FOUND>").split("/")

    organization_id = resource[resource.index("organizations") + 1]

    return (
        f"GCP: [{actor}] created or updated workforce pool "
        f"[{workforce_pool}] in organization [{organization_id}]"
    )


def alert_context(event):
    return event.deep_get("protoPayload", "request", "workforcePool", default={})

Analyst notes

Ensure that the Workforce Pool creation or modification was expected. Adversaries may use this to persist or allow additional access or escalate their privilege.

Raw source GCP Workforce Pool Created or Updated · Panther Python
Esc
Published by panther-labs/panther-analysis ↗, licensed under Apache 2.0 ↗. Reproduced here unmodified.
AnalysisType: rule
Filename: gcp_workforce_pool_created_or_updated.py
RuleID: "GCP.Workforce.Pool.Created.or.Updated"
DisplayName: "GCP Workforce Pool Created or Updated"
Enabled: true
LogTypes:
  - GCP.AuditLog
Tags:
  - Account Manipulation
  - Additional Cloud Roles
  - GCP
  - Privilege Escalation
Reports:
  MITRE ATT&CK:
    - TA0003:T1136.003
    - TA0003:T1098.003
    - TA0004:T1098.003
Severity: High
DedupPeriodMinutes: 60
Threshold: 1
Runbook: >
  Ensure that the Workforce Pool creation or modification was expected. Adversaries may use this to persist or allow additional access or escalate their privilege.
Reference: https://medium.com/google-cloud/detection-of-inbound-sso-persistence-techniques-in-gcp-c56f7b2a588b
Tests:
  - Name: DeleteWorkforcePool-False
    ExpectedResult: false
    Log:
      insertId: 1plwiv7e2lay7
      logName: organizations/123456789012/logs/cloudaudit.googleapis.com%2Factivity
      operation:
        first: true
        id: locations/global/workforcePools/test-pool/operations/bigar3hp32vamefaukfkaaq000000000
        producer: iam.googleapis.com
      protoPayload:
        "@type": type.googleapis.com/google.cloud.audit.AuditLog
        authenticationInfo:
          principalEmail: user@example.com
        authorizationInfo:
          - granted: true
            permission: iam.workforcePools.delete
            resource: locations/global/workforcePools/test-pool
            resourceAttributes: {}
        methodName: google.iam.admin.v1.WorkforcePools.DeleteWorkforcePool
        request:
          "@type": type.googleapis.com/google.iam.admin.v1.DeleteWorkforcePoolRequest
          name: locations/global/workforcePools/test-pool
        requestMetadata:
          callerIp: 07da:0994:97fb:8db1:c68f:c109:fcdd:d594
          callerSuppliedUserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/119.0,gzip(gfe),gzip(gfe)
          destinationAttributes: {}
          requestAttributes:
            auth: {}
            reason: 8uSywAYQGg5Db2xpc2V1bSBGbG93cw
            time: "2023-11-17T18:58:52.165673889Z"
        resourceName: locations/global/workforcePools/test-pool
        serviceName: iam.googleapis.com
      receiveTimestamp: "2023-11-17T18:58:52.901258022Z"
      resource:
        labels:
          method: google.iam.admin.v1.WorkforcePools.DeleteWorkforcePool
          service: iam.googleapis.com
        type: audited_resource
      severity: NOTICE
      timestamp: "2023-11-17T18:58:52.158942930Z"
  - Name: UpdateWorkforcePool-True
    ExpectedResult: true
    Log:
      insertId: 1h09dxwe33hgu
      logName: organizations/123456789012/logs/cloudaudit.googleapis.com%2Factivity
      operation:
        first: true
        id: locations/global/workforcePools/test-pool/operations/bigarg7n32vamefy6ximiaq000000000
        producer: iam.googleapis.com
      protoPayload:
        "@type": type.googleapis.com/google.cloud.audit.AuditLog
        authenticationInfo:
          principalEmail: user@example.com
        authorizationInfo:
          - granted: true
            permission: iam.workforcePools.update
            resource: locations/global/workforcePools/test-pool
            resourceAttributes: {}
        methodName: google.iam.admin.v1.WorkforcePools.UpdateWorkforcePool
        request:
          "@type": type.googleapis.com/google.iam.admin.v1.UpdateWorkforcePoolRequest
          updateMask: description,sessionDuration,disabled,displayName
          workforcePool:
            description: Test pool to facilitate detection writing
            displayName: Test Pool
            name: locations/global/workforcePools/test-pool
            sessionDuration: 43200s
        requestMetadata:
          callerIp: 07da:0994:97fb:8db1:c68f:c109:fcdd:d594
          callerSuppliedUserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/119.0,gzip(gfe),gzip(gfe)
          destinationAttributes: {}
          requestAttributes:
            auth: {}
            reason: 8uSywAYQGg5Db2xpc2V1bSBGbG93cw
            time: "2023-11-17T18:53:15.208909504Z"
        resourceName: locations/global/workforcePools/test-pool
        serviceName: iam.googleapis.com
      receiveTimestamp: "2023-11-17T18:53:16.523653141Z"
      resource:
        labels:
          method: google.iam.admin.v1.WorkforcePools.UpdateWorkforcePool
          service: iam.googleapis.com
        type: audited_resource
      severity: NOTICE
      timestamp: "2023-11-17T18:53:15.200613481Z"
  - Name: CreateWorkforcePool-True
    ExpectedResult: true
    Log:
      insertId: 6432zre32u1v
      logName: organizations/123456789012/logs/cloudaudit.googleapis.com%2Factivity
      operation:
        first: true
        id: locations/global/workforcePools/test-pool/operations/bifqrwxk32vamegiyoqaoeab00000000
        producer: iam.googleapis.com
      protoPayload:
        "@type": type.googleapis.com/google.cloud.audit.AuditLog
        authenticationInfo:
          principalEmail: user@example.com
        authorizationInfo:
          - granted: true
            permission: iam.workforcePools.create
            resource: organizations/123456789012
            resourceAttributes: {}
        methodName: google.iam.admin.v1.WorkforcePools.CreateWorkforcePool
        request:
          "@type": type.googleapis.com/google.iam.admin.v1.CreateWorkforcePoolRequest
          location: locations/global
          workforcePool:
            description: Test pool
            displayName: Test Pool
            name: locations/global/workforcePools/test-pool
            parent: organizations/325169835352
            sessionDuration: 3600s
            state: ACTIVE
          workforcePoolId: test-pool
        requestMetadata:
          callerIp: 07da:0994:97fb:8db1:c68f:c109:fcdd:d594
          callerSuppliedUserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/119.0,gzip(gfe),gzip(gfe)
          destinationAttributes: {}
          requestAttributes:
            auth: {}
            reason: 8uSywAYQGg5Db2xpc2V1bSBGbG93cw
            time: "2023-11-17T18:47:53.284817626Z"
        resourceName: organizations/325169835352
        serviceName: iam.googleapis.com
      receiveTimestamp: "2023-11-17T18:47:54.138395349Z"
      resource:
        labels:
          method: google.iam.admin.v1.WorkforcePools.CreateWorkforcePool
          service: iam.googleapis.com
        type: audited_resource
      severity: NOTICE
      timestamp: "2023-11-17T18:47:53.276929945Z"


# ------ paired body: gcp_workforce_pool_created_or_updated.py ------

METHODS = [
    "google.iam.admin.v1.WorkforcePools.CreateWorkforcePool",
    "google.iam.admin.v1.WorkforcePools.UpdateWorkforcePool",
]


def rule(event):
    return event.deep_get("protoPayload", "methodName", default="") in METHODS


def title(event):
    actor = event.deep_get(
        "protoPayload", "authenticationInfo", "principalEmail", default="<ACTOR_NOT_FOUND>"
    )
    workforce_pool = event.deep_get(
        "protoPayload", "request", "workforcePool", "name", default=""
    ).split("/")[-1]

    resource = organization_id = event.get("logName", "<LOG_NAME_NOT_FOUND>").split("/")

    organization_id = resource[resource.index("organizations") + 1]

    return (
        f"GCP: [{actor}] created or updated workforce pool "
        f"[{workforce_pool}] in organization [{organization_id}]"
    )


def alert_context(event):
    return event.deep_get("protoPayload", "request", "workforcePool", default={})

Detection rules belong to the projects that publish them and remain under their own licenses. This site indexes and links to them; it claims no rights in them.