Detection rules › Kusto

GCP Audit Logs - Detect Bulk VM Snapshot Deletion

Status
available
Severity
high
Time window
15m
Group by
PrincipalEmail, ProjectId, UserAgent
Source
github.com/Azure/Azure-Sentinel

'Detects bulk deletion of Google Cloud VM snapshots within a short time period, which may indicate data destruction or defense evasion activities. VM snapshots are critical for backup and disaster recovery. Bulk deletion of snapshots can prevent recovery from incidents and may indicate malicious activity such as ransomware, data destruction, or an attempt to cover tracks after a security breach. Adversaries may delete snapshots to maximize damage, prevent forensic investigation, or hinder recovery efforts. This rule triggers when multiple snapshots are deleted by the same user within a 1-minute window.'

MITRE ATT&CK coverage

Rules detecting the same action

Other rules on this platform that filter on the same API call or operation.

Rule body kusto

id: dfdffdc7-929f-4c7e-8f48-30e5ffddb067
name: GCP Audit Logs - Detect Bulk VM Snapshot Deletion
description: |
  'Detects bulk deletion of Google Cloud VM snapshots within a short time period, which may indicate data destruction or defense evasion activities.
  VM snapshots are critical for backup and disaster recovery. Bulk deletion of snapshots can prevent recovery from incidents and may indicate
  malicious activity such as ransomware, data destruction, or an attempt to cover tracks after a security breach.
  Adversaries may delete snapshots to maximize damage, prevent forensic investigation, or hinder recovery efforts.
  This rule triggers when multiple snapshots are deleted by the same user within a 1-minute window.'
severity: High
status: Available
requiredDataConnectors:
  - connectorId: GCPAuditLogsDefinition
    dataTypes:
      - GCPAuditLogs
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
  - DefenseEvasion
relevantTechniques:
  - T1485
  - T1490
  - T1562.001
tags:
  - GCP 
  - Compute
  - Data Destruction
  - Cloud Security
query: |
  // Update these thresholds if noisy in your environment
  let SnapshotDeletionThreshold = 10;
  let TimeWindow = 1m;
  GCPAuditLogs
  | where ServiceName == "compute.googleapis.com"
  | where MethodName has "compute.snapshots.delete"
  | where GCPResourceType == "gce_snapshot" and Severity == "NOTICE"
  | extend 
      AuthzInfoJson = parse_json(AuthorizationInfo),
      RequestMetadataJson = parse_json(RequestMetadata),
      ResponseJson = parse_json(Response)
  | extend PermissionType = tostring(AuthzInfoJson[0].permissionType)
  | where PermissionType == "ADMIN_WRITE"
  | extend 
      CallerIpAddress = tostring(RequestMetadataJson.callerIp),
      UserAgent = tostring(RequestMetadataJson.callerSuppliedUserAgent),
      SnapshotName = extract(@"snapshots/([^/]+)", 1, GCPResourceName),
      OperationType = tostring(ResponseJson.operationType),
      OperationId = tostring(ResponseJson.id)
  | summarize 
      SnapshotCount = count(),
      SnapshotList = make_set(SnapshotName, 100),
      FirstDeletion = min(TimeGenerated),
      LastDeletion = max(TimeGenerated),
      OperationIds = make_set(OperationId, 100),
      CallerIPs = make_set(CallerIpAddress, 10)
      by PrincipalEmail, ProjectId, UserAgent
  | where SnapshotCount >= SnapshotDeletionThreshold
  | extend DeletionTimeSpan = LastDeletion - FirstDeletion
  | where DeletionTimeSpan <= TimeWindow
  | extend 
      AccountName = tostring(split(PrincipalEmail, "@")[0]), 
      AccountUPNSuffix = tostring(split(PrincipalEmail, "@")[1])
  | project 
      TimeGenerated = FirstDeletion,
      PrincipalEmail,
      ProjectId,
      SnapshotCount,
      SnapshotList,
      FirstDeletion,
      LastDeletion,
      DeletionTimeSpan,
      CallerIPs,
      UserAgent,
      OperationIds,
      AccountName,
      AccountUPNSuffix
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: PrincipalEmail
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: CloudApplication
    fieldMappings:
      - identifier: Name
        columnName: ProjectId
customDetails:
  ProjectId: ProjectId
  SnapshotCount: SnapshotCount
  SnapshotList: SnapshotList
  FirstDeletion: FirstDeletion
  LastDeletion: LastDeletion
  DeletionTimeSpan: DeletionTimeSpan
  CallerIPs: CallerIPs
  UserAgent: UserAgent
alertDetailsOverride:
  alertDisplayNameFormat: "Bulk VM Snapshot Deletion: {{SnapshotCount}} snapshots deleted by {{PrincipalEmail}} in {{ProjectId}}"
  alertDescriptionFormat: |-
    User {{PrincipalEmail}} deleted {{SnapshotCount}} VM snapshots in project {{ProjectId}} within a short time period. 
    This may indicate ransomware, data destruction, or defense evasion activity. Verify authorization, check remaining snapshots, review IP addresses, and investigate for compromise.
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let SnapshotDeletionThreshold = 10;
let TimeWindow = 1m;

Stage 1: source

GCPAuditLogs

Stage 2: where

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

Stage 3: where

| where MethodName has "compute.snapshots.delete"

Stage 4: where

| where GCPResourceType == "gce_snapshot" and Severity == "NOTICE"

Stage 5: extend

| extend 
    AuthzInfoJson = parse_json(AuthorizationInfo),
    RequestMetadataJson = parse_json(RequestMetadata),
    ResponseJson = parse_json(Response)

Stage 6: extend

| extend PermissionType = tostring(AuthzInfoJson[0].permissionType)

Stage 7: where

| where PermissionType == "ADMIN_WRITE"

Stage 8: extend

| extend 
    CallerIpAddress = tostring(RequestMetadataJson.callerIp),
    UserAgent = tostring(RequestMetadataJson.callerSuppliedUserAgent),
    SnapshotName = extract(@"snapshots/([^/]+)", 1, GCPResourceName),
    OperationType = tostring(ResponseJson.operationType),
    OperationId = tostring(ResponseJson.id)

Stage 9: summarize

| summarize 
    SnapshotCount = count(),
    SnapshotList = make_set(SnapshotName, 100),
    FirstDeletion = min(TimeGenerated),
    LastDeletion = max(TimeGenerated),
    OperationIds = make_set(OperationId, 100),
    CallerIPs = make_set(CallerIpAddress, 10)
    by PrincipalEmail, ProjectId, UserAgent
Threshold
ge 10

Stage 10: where

| where SnapshotCount >= SnapshotDeletionThreshold

Stage 11: extend

| extend DeletionTimeSpan = LastDeletion - FirstDeletion

Stage 12: where

| where DeletionTimeSpan <= TimeWindow

Stage 13: extend

| extend 
    AccountName = tostring(split(PrincipalEmail, "@")[0]), 
    AccountUPNSuffix = tostring(split(PrincipalEmail, "@")[1])

Stage 14: project

| project 
    TimeGenerated = FirstDeletion,
    PrincipalEmail,
    ProjectId,
    SnapshotCount,
    SnapshotList,
    FirstDeletion,
    LastDeletion,
    DeletionTimeSpan,
    CallerIPs,
    UserAgent,
    OperationIds,
    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
DeletionTimeSpanle
  • 1m transforms: cased
GCPResourceTypeeq
  • gce_snapshot transforms: cased
MethodNamematch
  • compute.snapshots.delete transforms: term
PermissionTypeeq
  • ADMIN_WRITE transforms: cased
ServiceNameeq
  • compute.googleapis.com transforms: cased
Severityeq
  • NOTICE transforms: cased
SnapshotCountge
  • 10 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
CallerIPsproject
DeletionTimeSpanproject
FirstDeletionproject
LastDeletionproject
OperationIdsproject
PrincipalEmailproject
ProjectIdproject
SnapshotCountproject
SnapshotListproject
TimeGeneratedproject
UserAgentproject