Detection rules › Panther
GCP Org or Folder Policy Was Changed Manually
Alert if a GCP Org or Folder Policy Was Changed Manually.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | T1556.009 Modify Authentication Process: Conditional Access Policies |
Rules detecting the same action
Other rules on this platform that filter on the same API call or operation.
Rule body yaml
AnalysisType: rule
Filename: gcp_iam_org_folder_changes.py
RuleID: "GCP.IAM.OrgFolderIAMChanges"
DisplayName: "GCP Org or Folder Policy Was Changed Manually"
Enabled: true
DedupPeriodMinutes: 1440 # 24 hours
LogTypes:
- GCP.AuditLog
Tags:
- GCP
- Identity & Access Management
- Persistence
- Modify Authentication Process - Conditional Access Policies
Reports:
GCP_CIS_1.3:
- 1.8
MITRE ATT&CK:
- TA0003:T1556.009
Severity: High
Description: >
Alert if a GCP Org or Folder Policy Was Changed Manually.
Runbook: |
Contact the party that made the change.
If it was intended to be temporary, ask for a window for rollback (< 24 hours).
If it must be permanent, ask for change-management doc explaining why it was needed.
Direct them to make the change in Terraform to avoid automated rollback.
Grep for google_org and google_folder in terraform repos for places to
put your new policy bindings.
Reference: https://cloud.google.com/iam/docs/granting-changing-revoking-access
SummaryAttributes:
- severity
- p_any_ip_addresses
Tests:
- Name: Terraform User Agent
ExpectedResult: true
Log:
{
"insertId": "-lmjke7dbt7y",
"logName": "organizations/888888888888/logs/cloudaudit.googleapis.com%2Factivity",
"p_log_type": "GCP.AuditLog",
"protoPayload":
{
"at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo":
{
"principalEmail": "terraform@platform.iam.gserviceaccount.com",
"principalSubject": "serviceAccount:terraform@platform.iam.gserviceaccount.com",
"serviceAccountKeyName": "//iam.googleapis.com/projects/platform/serviceAccounts/terraform@platform.iam.gserviceaccount.com/keys/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
},
"authorizationInfo":
[
{
"granted": true,
"permission": "resourcemanager.organizations.setIamPolicy",
"resource": "organizations/888888888888",
"resourceAttributes":
{
"name": "organizations/888888888888",
"service": "cloudresourcemanager.googleapis.com",
"type": "cloudresourcemanager.googleapis.com/Organization",
},
},
],
"methodName": "SetIamPolicy",
"request":
{
"@type": "type.googleapis.com/google.iam.v1.SetIamPolicyRequest",
"policy":
{
"bindings":
[
{
"members":
[
"joey.jojo@example.com",
"serviceAccount:terraform@platform.iam.gserviceaccount.com",
],
"role": "roles/owner",
},
],
"etag": "BwXcRFUAtX4=",
},
"resource": "organizations/888888888888",
"updateMask": "bindings,etag,auditConfigs",
},
"requestMetadata":
{
"callerIP": "100.100.100.100",
"callerSuppliedUserAgent": "Terraform/0.13.2 terraform-provider-google/3.90.1",
"destinationAttributes": {},
"requestAttributes": {},
},
"resourceName": "organizations/888888888888",
"response":
{
"@type": "type.googleapis.com/google.iam.v1.Policy",
"bindings":
[
{
"members":
[
"joey.jojo@example.com",
"serviceAccount:terraform@platform.iam.gserviceaccount.com",
],
"role": "roles/owner",
},
],
"etag": "BwXeRCtKxCw=",
},
"serviceData":
{
"@type": "type.googleapis.com/google.iam.v1.logging.AuditData",
"policyDelta":
{
"bindingDeltas":
[
{
"action": "ADD",
"member": "user:backdoor@example.com",
"role": "roles/owner",
},
],
},
},
"serviceName": "cloudresourcemanager.googleapis.com",
"status": {},
},
"receiveTimestamp": "2022-05-05 14:00:49.450798551",
"resource":
{
"labels": { "organization_id": "888888888888" },
"type": "organization",
},
"severity": "NOTICE",
"timestamp": "2022-05-05 14:00:48.814294000",
}
- Name: Manual Change
ExpectedResult: true
Log:
{
"insertId": "-yoga2udnx8s",
"logName": "organizations/888888888888/logs/cloudaudit.googleapis.com%2Factivity",
"p_log_type": "GCP.AuditLog",
"protoPayload":
{
"@type": "type.googleapis.com/google.cloud.audit.AuditLog",
"authenticationInfo": { "principalEmail": "chris@example.com" },
"authorizationInfo":
[
{
"granted": true,
"permission": "resourcemanager.organizations.setIamPolicy",
"resource": "organizations/888888888888",
"resourceAttributes":
{
"name": "organizations/888888888888",
"service": "cloudresourcemanager.googleapis.com",
"type": "cloudresourcemanager.googleapis.com/Organization",
},
},
],
"methodName": "SetIamPolicy",
"request":
{
"@type": "type.googleapis.com/google.iam.v1.SetIamPolicyRequest",
"etag": "BwXYPRNtbqo=",
"policy":
{
"bindings":
[
{
"members":
[
"user:chris@example.com",
"serviceAccount:diana@platform.iam.gserviceaccount.com",
],
"role": "roles/owner",
},
],
},
},
"receiveTimestamp": "2022-02-17T22:52:03.190032712Z",
"requestMetadata":
{
"callerIp": "38.38.38.38",
"callerSuppliedUserAgent": "Mozilla/5.0 Chrome/98.0.4758.102",
"destinationAttributes": {},
"requestAttributes": {},
},
"resource":
{
"labels": { "organization_id": "888888888888" },
"type": "organization",
},
"resourceName": "organizations/888888888888",
"response":
{
"@type": "type.googleapis.com/google.iam.v1.Policy",
"bindings":
[
{
"members": ["user:chris@example.com"],
"role": "roles/owner",
},
],
"etag": "BwXYPp1Xs/Y=",
},
"serviceData":
{
"@type": "type.googleapis.com/google.iam.v1.logging.AuditData",
"policyDelta":
{
"bindingDeltas":
[
{
"action": "REMOVE",
"member": "serviceAccount:diana@platform.iam.gserviceaccount.com",
"role": "roles/owner",
},
],
},
},
},
"serviceName": "cloudresourcemanager.googleapis.com",
"severity": "NOTICE",
"status": {},
"timestamp": "2022-02-17T22:52:02.692083Z",
}
Detection logic
Condition
protoPayload.methodName eq "SetIamPolicy"
logName starts_with "organizations" or logName starts_with "folder"
logName ends_with "/logs/cloudaudit.googleapis.com%2Factivity"
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
logName | ends_with |
|
logName | starts_with |
|
protoPayload.methodName | eq |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
actor | actor_user |
policy_change | protoPayload.serviceData.policyDelta |
caller_ip | protoPayload.requestMetadata.callerIP |
user_agent | protoPayload.requestMetadata.callerSuppliedUserAgent |
p_log_type |