Detection rules › Kusto

GCP Audit Logs - Detect Organization Policy Deletion or Updation

Status
available
Severity
high
Time window
1h
Source
github.com/Azure/Azure-Sentinel

'Detects when a Google Cloud Platform organization policy is deleted or updated. Organization policies provide centralized control over your organization's cloud resources and help ensure security and compliance. Deletion or modification of org policies may indicate an attempt to bypass security controls or weaken the security posture of GCP projects. Adversaries may delete or update organization policies to disable security constraints before performing malicious activities.'

MITRE ATT&CK coverage

Rule body kusto

id: 205e1c9f-faee-43f1-b3b8-1952ffbbeea4
name: GCP Audit Logs - Detect Organization Policy Deletion or Updation
description: |
  'Detects when a Google Cloud Platform organization policy is deleted or updated. 
  Organization policies provide centralized control over your organization's cloud resources and help ensure security and compliance.
  Deletion or modification of org policies may indicate an attempt to bypass security controls or weaken the security posture of GCP projects.
  Adversaries may delete or update organization policies to disable security constraints before performing malicious activities.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: GCPAuditLogsDefinition
    dataTypes:
      - GCPAuditLogs
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - DefenseEvasion
relevantTechniques:
  - T1562.001
tags:
  - GCP
  - IAM Organization Policy
  - Compliance
query: |
  GCPAuditLogs
  | where ServiceName == "orgpolicy.googleapis.com"
  | where MethodName has_any ("OrgPolicy.DeletePolicy", "OrgPolicy.UpdatePolicy")
  | extend 
      RequestMetadataJson = parse_json(RequestMetadata),
      AuthInfoJson = parse_json(AuthenticationInfo),
      AuthzInfoJson = parse_json(AuthorizationInfo)
  | extend 
      PolicyName = split(GCPResourceName, "/")[-1],
      CallerIpAddress = tostring(RequestMetadataJson.callerIp),
      UserAgent = tostring(RequestMetadataJson.callerSuppliedUserAgent),
      AuthEmail = tostring(AuthInfoJson.principalEmail),
      Permission = tostring(AuthzInfoJson[0].permission),
      PermissionGranted = tostring(AuthzInfoJson[0].granted)
  | extend 
      AccountName = tostring(split(PrincipalEmail, "@")[0]), 
      AccountUPNSuffix = tostring(split(PrincipalEmail, "@")[1])
  | project TimeGenerated,
            PrincipalEmail,
            AuthEmail,
            ProjectId,
            ResourceName = GCPResourceName,
            PolicyName,
            CallerIpAddress,
            UserAgent,
            MethodName,
            ServiceName,
            Severity,
            Permission,
            PermissionGranted,
            LogName,
            InsertId,
            AccountName,
            AccountUPNSuffix
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: PrincipalEmail
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: CallerIpAddress
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ProjectId
      - identifier: InstanceName
        columnName: ResourceName
customDetails:
  ProjectId: ProjectId
  PolicyName: PolicyName
  ResourceName: ResourceName
  UserAgent: UserAgent
  Permission: Permission
  MethodName: MethodName
alertDetailsOverride:
  alertDisplayNameFormat: "GCP Organization Policy {{PolicyName}} Deleted by {{PrincipalEmail}}"
  alertDescriptionFormat: |-
    Orgnization policy {{PolicyName}} was deleted. This action may weaken security controls and compliance posture.
    
    Resource: {{ResourceName}}
    Source IP: {{CallerIpAddress}}
    
    Investigate whether this deletion was authorized and assess the impact on security controls.
version: 1.0.0
kind: Scheduled

Stages and Predicates

Stage 1: source

GCPAuditLogs

Stage 2: where

| where ServiceName == "orgpolicy.googleapis.com"

Stage 3: where

| where MethodName has_any ("OrgPolicy.DeletePolicy", "OrgPolicy.UpdatePolicy")

Stage 4: extend (3 consecutive steps)

| extend 
    RequestMetadataJson = parse_json(RequestMetadata),
    AuthInfoJson = parse_json(AuthenticationInfo),
    AuthzInfoJson = parse_json(AuthorizationInfo)
| extend 
    PolicyName = split(GCPResourceName, "/")[-1],
    CallerIpAddress = tostring(RequestMetadataJson.callerIp),
    UserAgent = tostring(RequestMetadataJson.callerSuppliedUserAgent),
    AuthEmail = tostring(AuthInfoJson.principalEmail),
    Permission = tostring(AuthzInfoJson[0].permission),
    PermissionGranted = tostring(AuthzInfoJson[0].granted)
| extend 
    AccountName = tostring(split(PrincipalEmail, "@")[0]), 
    AccountUPNSuffix = tostring(split(PrincipalEmail, "@")[1])

Stage 5: project

| project TimeGenerated,
          PrincipalEmail,
          AuthEmail,
          ProjectId,
          ResourceName = GCPResourceName,
          PolicyName,
          CallerIpAddress,
          UserAgent,
          MethodName,
          ServiceName,
          Severity,
          Permission,
          PermissionGranted,
          LogName,
          InsertId,
          AccountName,
          AccountUPNSuffix

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.

FieldKindValues
MethodNamematch
  • OrgPolicy.DeletePolicy
  • OrgPolicy.UpdatePolicy
ServiceNameeq
  • orgpolicy.googleapis.com transforms: cased

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.

FieldSource
AccountNameproject
AccountUPNSuffixproject
AuthEmailproject
CallerIpAddressproject
InsertIdproject
LogNameproject
MethodNameproject
Permissionproject
PermissionGrantedproject
PolicyNameproject
PrincipalEmailproject
ProjectIdproject
ResourceNameproject
ServiceNameproject
Severityproject
TimeGeneratedproject
UserAgentproject