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={})