Detection rules › Kusto

Power Automate - Unusual bulk deletion of flow resources

Status
available
Severity
medium
Time window
14d
Group by
ActorName, ActorUserId, UserUpn
Source
github.com/Azure/Azure-Sentinel

Identifies bulk deletion of Power Automate flows that exceed a predefined threshold defined in the query and deviate from activity patterns observed in the last 14 days.

MITRE ATT&CK coverage

Rule body kusto

id: 56cb646e-56a0-4f0e-8866-9bc1dd15da78
kind: Scheduled
name: Power Automate - Unusual bulk deletion of flow resources
description: Identifies bulk deletion of Power Automate flows that exceed a predefined
  threshold defined in the query and deviate from activity patterns observed in the
  last 14 days.
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: PowerAutomate
    dataTypes:
      - PowerAutomateActivity
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
  - DefenseEvasion
relevantTechniques:
  - T1485
  - T0828
  - T1562
query: |
  // minThreshold: Minimum number of apps to be deleted to be considered an anomaly;
  // This is to prevent one-off isolated delete flow to be considered outlier.
  // The Min Threshold can be reduced or increased according to the traffic in the organization.
  let minThreshold=10;
  let interval = 1h;
  let startTime = ago(14d);
  let endTime = now();
  let query_frequency = 1h;
  let flow_deletion_events = PowerAutomateActivity
      | where TimeGenerated >= startTime
      | where EventOriginalType =~ "DeleteFlow"
      | extend IngestionTimeGenerated = TimeGenerated;
  flow_deletion_events
  | make-series DeletedFlowCount=count() on IngestionTimeGenerated from startTime to endTime step interval by ActorName, UserUpn, ActorUserId
  | extend(Anomalies, AnomalyScore, ExpectedUsage) = series_decompose_anomalies(DeletedFlowCount)
  | mv-expand
      DeletedFlowCount to typeof(double),
      IngestionTimeGenerated to typeof(datetime),
      Anomalies to typeof(double),
      AnomalyScore to typeof(double),
      ExpectedUsage to typeof(long)
  | where IngestionTimeGenerated >= ago(query_frequency)
  | where Anomalies != 0 and DeletedFlowCount >= minThreshold
  | lookup (flow_deletion_events
      | where IngestionTimeGenerated >= ago(query_frequency))
      on ActorName, UserUpn, ActorUserId
  | extend
      AccountName = tostring(split(ActorName, "@")[0]),
      UPNSuffix = tostring(split(ActorName, "@")[1]),
      PowerAutomateAppId = 27592
  | project
      TimeGenerated,
      ActorName,
      DeletedFlowCount,
      ExpectedUsage,
      Anomalies,
      AnomalyScore,
      AccountName,
      UPNSuffix,
      PowerAutomateAppId,
      UserUpn,
      ActorUserId
eventGroupingSettings:
  aggregationKind: SingleAlert
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: UPNSuffix
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: PowerAutomateAppId
alertDetailsOverride:
  alertDisplayNameFormat: Power Automate - unusual bulk deletion of {{DeletedFlowCount}}
    flows
  alertDescriptionFormat: User {{ActorName}}  deleted {{DeletedFlowCount}} flows in
    the last hour, surpassing the bulk delete threshold. This is anomalous compared
    to the past 14 days.
customDetails:
  DeletedFlowCount: DeletedFlowCount
version: 3.2.0

Stages and Predicates

Parameters

let minThreshold = 10;
let interval = 1h;
let startTime = ago(14d);
let endTime = now();
let query_frequency = 1h;

The stages below define let flow_deletion_events (the rule's main pipeline source).

Stage 1: source

PowerAutomateActivity

Stage 2: where

| where TimeGenerated >= startTime

Stage 3: where

| where EventOriginalType =~ "DeleteFlow"

Stage 4: extend

| extend IngestionTimeGenerated = TimeGenerated

The stages below score time-series anomalies (make-series, series_decompose_anomalies).

The stages below run on flow_deletion_events (the outer pipeline).

Stage 5: summarize

flow_deletion_events
| make-series DeletedFlowCount=count() on IngestionTimeGenerated from startTime to endTime step interval by ActorName, UserUpn, ActorUserId
Threshold
ge 10

Stage 6: extend

| extend(Anomalies, AnomalyScore, ExpectedUsage) = series_decompose_anomalies(DeletedFlowCount)

Stage 7: mv-expand

| mv-expand
    DeletedFlowCount to typeof(double),
    IngestionTimeGenerated to typeof(datetime),
    Anomalies to typeof(double),
    AnomalyScore to typeof(double),
    ExpectedUsage to typeof(long)

Stage 8: where

| where IngestionTimeGenerated >= ago(query_frequency)

Stage 9: where

| where Anomalies != 0 and DeletedFlowCount >= minThreshold

Stage 10: kusto:lookup

| lookup (flow_deletion_events
    | where IngestionTimeGenerated >= ago(query_frequency))
    on ActorName, UserUpn, ActorUserId

Stage 11: extend

| extend
    AccountName = tostring(split(ActorName, "@")[0]),
    UPNSuffix = tostring(split(ActorName, "@")[1]),
    PowerAutomateAppId = 27592

Stage 12: project

| project
    TimeGenerated,
    ActorName,
    DeletedFlowCount,
    ExpectedUsage,
    Anomalies,
    AnomalyScore,
    AccountName,
    UPNSuffix,
    PowerAutomateAppId,
    UserUpn,
    ActorUserId

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
Anomaliesne
  • 0 transforms: cased
DeletedFlowCountge
  • 10 transforms: cased
EventOriginalTypeeq
  • DeleteFlow
TimeGeneratedge
  • startTime 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
ActorNameproject
ActorUserIdproject
Anomaliesproject
AnomalyScoreproject
DeletedFlowCountproject
ExpectedUsageproject
PowerAutomateAppIdproject
TimeGeneratedproject
UPNSuffixproject
UserUpnproject