Detection rules › Panther

GCP Log Bucket or Sink Deleted

Severity
medium
Log types
GCP.AuditLog
Tags
GCP, Logging, Bucket, Sink, Infrastructure
Reference
https://cloud.google.com/logging/docs
Source
github.com/panther-labs/panther-analysis

This rule detects deletions of GCP Log Buckets or Sinks.

Rule body yaml

---
AnalysisType: rule
DedupPeriodMinutes: 60
DisplayName: GCP Log Bucket or Sink Deleted
Enabled: true
Filename: gcp_log_bucket_or_sink_deleted.py
RuleID: "GCP.Log.Bucket.Or.Sink.Deleted"
Severity: Medium
LogTypes:
  - GCP.AuditLog
Tags:
  - GCP
  - Logging
  - Bucket
  - Sink
  - Infrastructure
Description: >
  This rule detects deletions of GCP Log Buckets or Sinks.
Runbook: >
  Ensure that the bucket or sink deletion was expected. Adversaries may do this to cover their tracks.
Reference: https://cloud.google.com/logging/docs
Tests:
  - Name: logging-bucket.deleted-should-alert
    LogType: GCP.AuditLog
    ExpectedResult: true
    Log:
      {
        "insertid": "xxxxxxxxxx",
        "logname": "projects/test-project-123456/logs/cloudaudit.googleapis.com%2Factivity",
        "protoPayload":
          {
            "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "authenticationInfo": { "principalEmail": "user@domain.com" },
            "authorizationInfo":
              [
                {
                  "granted": true,
                  "permission": "logging.buckets.delete",
                  "resource": "projects/test-project-123456/locations/global/buckets/testloggingbucket",
                  "resourceAttributes":
                    {
                      "name": "projects/test-project-123456/locations/global/buckets/testloggingbucket",
                      "service": "logging.googleapis.com",
                    },
                },
              ],
            "methodName": "google.logging.v2.ConfigServiceV2.DeleteBucket",
            "request":
              {
                "@type": "type.googleapis.com/google.logging.v2.DeleteBucketRequest",
                "name": "projects/test-project-123456/locations/global/buckets/testloggingbucket",
              },
            "requestMetadata":
              {
                "callerIP": "12.12.12.12",
                "callerSuppliedUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36,gzip(gfe),gzip(gfe)",
                "destinationAttributes": {},
                "requestAttributes":
                  { "auth": {}, "time": "2023-05-23T19:38:36.846070601Z" },
              },
            "resourceName": "projects/test-project-123456/locations/global/buckets/testloggingbucket",
            "serviceName": "logging.googleapis.com",
            "status": {},
          },
        "receivetimestamp": "2023-05-23 19:38:37.59",
        "resource":
          {
            "labels":
              {
                "method": "google.logging.v2.ConfigServiceV2.DeleteBucket",
                "project_id": "test-project-123456",
                "service": "logging.googleapis.com",
              },
            "type": "audited_resource",
          },
        "severity": "NOTICE",
        "timestamp": "2023-05-23 19:38:36.838",
      }
  - Name: logging-sink.deleted-should-alert
    LogType: GCP.AuditLog
    ExpectedResult: true
    Log:
      {
        "insertid": "xxxxxxxxxx",
        "logname": "projects/test-project-123456/logs/cloudaudit.googleapis.com%2Factivity",
        "protoPayload":
          {
            "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "authenticationInfo": { "principalEmail": "user@domain.com" },
            "authorizationInfo":
              [
                {
                  "granted": true,
                  "permission": "logging.sinks.delete",
                  "resource": "projects/test-project-123456/sinks/test-1",
                  "resourceAttributes":
                    {
                      "name": "projects/test-project-123456/sinks/test-1",
                      "service": "logging.googleapis.com",
                    },
                },
              ],
            "methodName": "google.logging.v2.ConfigServiceV2.DeleteSink",
            "request":
              {
                "@type": "type.googleapis.com/google.logging.v2.DeleteSinkRequest",
                "sinkName": "projects/test-project-123456/sinks/test-1",
              },
            "requestMetadata":
              {
                "callerIP": "12.12.12.12",
                "callerSuppliedUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36,gzip(gfe),gzip(gfe)",
                "destinationAttributes": {},
                "requestAttributes":
                  { "auth": {}, "time": "2023-05-23T19:39:15.230304077Z" },
              },
            "resourceName": "projects/test-project-123456/sinks/test-1",
            "serviceName": "logging.googleapis.com",
            "status": {},
          },
        "receivetimestamp": "2023-05-23 19:39:15.565",
        "resource":
          {
            "labels":
              {
                "destination": "",
                "name": "test-1",
                "project_id": "test-project-123456",
              },
            "type": "logging_sink",
          },
        "severity": "NOTICE",
        "timestamp": "2023-05-23 19:39:15.213",
      }
  - Name: logging-bucket.non-deletion-should-not-alert
    LogType: GCP.AuditLog
    ExpectedResult: false
    Log:
      {
        "insertid": "xxxxxxxxxx",
        "logname": "projects/test-project-123456/logs/cloudaudit.googleapis.com%2Factivity",
        "protoPayload":
          {
            "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "authenticationInfo": { "principalEmail": "user@domain.com" },
            "authorizationInfo":
              [
                {
                  "granted": true,
                  "permission": "logging.buckets.get",
                  "resource": "projects/test-project-123456/locations/global/buckets/testloggingbucket",
                  "resourceAttributes":
                    {
                      "name": "projects/test-project-123456/locations/global/buckets/testloggingbucket",
                      "service": "logging.googleapis.com",
                    },
                },
              ],
            "methodName": "google.logging.v2.ConfigServiceV2.GetBucket",
            "request":
              {
                "@type": "type.googleapis.com/google.logging.v2.GetBucketRequest",
                "name": "projects/test-project-123456/locations/global/buckets/testloggingbucket",
              },
            "requestMetadata":
              {
                "callerIP": "12.12.12.12",
                "callerSuppliedUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36,gzip(gfe),gzip(gfe)",
                "destinationAttributes": {},
                "requestAttributes":
                  { "auth": {}, "time": "2023-05-23T19:38:36.846070601Z" },
              },
            "resourceName": "projects/test-project-123456/locations/global/buckets/testloggingbucket",
            "serviceName": "logging.googleapis.com",
            "status": {},
          },
        "receivetimestamp": "2023-05-23 19:38:37.59",
        "resource":
          {
            "labels":
              {
                "method": "google.logging.v2.ConfigServiceV2.GetBucket",
                "project_id": "test-project-123456",
                "service": "logging.googleapis.com",
              },
            "type": "audited_resource",
          },
        "severity": "NOTICE",
        "timestamp": "2023-05-23 19:38:36.838",
      }
  - Name: logging-sink.non-deletion-should-not-alert
    LogType: GCP.AuditLog
    ExpectedResult: false
    Log:
      {
        "insertid": "xxxxxxxxxx",
        "logname": "projects/test-project-123456/logs/cloudaudit.googleapis.com%2Factivity",
        "protoPayload":
          {
            "at_sign_type": "type.googleapis.com/google.cloud.audit.AuditLog",
            "authenticationInfo": { "principalEmail": "user@domain.com" },
            "authorizationInfo":
              [
                {
                  "granted": true,
                  "permission": "logging.sinks.get",
                  "resource": "projects/test-project-123456/sinks/test-1",
                  "resourceAttributes":
                    {
                      "name": "projects/test-project-123456/sinks/test-1",
                      "service": "logging.googleapis.com",
                    },
                },
              ],
            "methodName": "google.logging.v2.ConfigServiceV2.GetSink",
            "request":
              {
                "@type": "type.googleapis.com/google.logging.v2.GetSinkRequest",
                "sinkName": "projects/test-project-123456/sinks/test-1",
              },
            "requestMetadata":
              {
                "callerIP": "12.12.12.12",
                "callerSuppliedUserAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36,gzip(gfe),gzip(gfe)",
                "destinationAttributes": {},
                "requestAttributes":
                  { "auth": {}, "time": "2023-05-23T19:39:15.230304077Z" },
              },
            "resourceName": "projects/test-project-123456/sinks/test-1",
            "serviceName": "logging.googleapis.com",
            "status": {},
          },
        "receivetimestamp": "2023-05-23 19:39:15.565",
        "resource":
          {
            "labels":
              {
                "destination": "",
                "name": "test-1",
                "project_id": "test-project-123456",
              },
            "type": "logging_sink",
          },
        "severity": "NOTICE",
        "timestamp": "2023-05-23 19:39:15.213",
      }

Detection logic

Rule logic imperative Python

import re

from panther_gcp_helpers import gcp_alert_context


def rule(event):
    granted_list = event.deep_walk("protoPayload", "authorizationInfo", "granted", default=[])
    authenticated = any(granted_list) if isinstance(granted_list, list) else bool(granted_list)
    method_pattern = r"(?:\w+\.)*v\d\.(?:ConfigServiceV\d\.(?:Delete(Bucket|Sink)))"
    match = re.search(method_pattern, event.deep_get("protoPayload", "methodName", default=""))
    return authenticated and match is not None


def title(event):
    actor = event.deep_get(
        "protoPayload", "authenticationInfo", "principalEmail", default="<ACTOR_NOT_FOUND>"
    )
    resource = event.deep_get(
        "protoPayload",
        "resourceName",
        default="<RESOURCE_NOT_FOUND>",
    )
    return f"[GCP]: [{actor}] deleted logging bucket or sink [{resource}]"


def alert_context(event):
    return gcp_alert_context(event)

The parser cannot express this rule's logic as a field filter; the imperative Python above is the detection.

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
projectresource.labels.project_id
principalprotoPayload.authenticationInfo.principalEmail
caller_ipprotoPayload.requestMetadata.callerIP
methodNameprotoPayload.methodName
resourceNameprotoPayload.resourceName
serviceNameprotoPayload.serviceName