Detection rules › Kusto

Unusual Volume of file deletion by users

Status
available
Severity
high
Time window
5m
Group by
AccountDisplayName, ActionType, ApplicationId, SessionId, Time, UserId
Source
github.com/Azure/Azure-Sentinel

This query looks for users performing file deletion activities. Spikes in file deletion observed from risky sign-in sessions are flagged here. This applies to SharePoint and OneDrive users. Audit event and Cloud application identifier references. Reference - https://learn.microsoft.com/microsoft-365/compliance/audit-log-activities?view=o365-worldwide Reference - https://learn.microsoft.com/azure/sentinel/entities-reference#cloud-application-identifiers

MITRE ATT&CK coverage

TacticTechniques
Impact

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

id: e5f8e196-3544-4a8b-96a9-17c1b6a49710
name: Unusual Volume of file deletion by users
description: |
  This query looks for users performing file deletion activities. Spikes in file deletion observed from risky sign-in sessions are flagged here.
  This applies to SharePoint and OneDrive users.
  Audit event and Cloud application identifier references.
  Reference - https://learn.microsoft.com/microsoft-365/compliance/audit-log-activities?view=o365-worldwide
  Reference - https://learn.microsoft.com/azure/sentinel/entities-reference#cloud-application-identifiers
severity: High
status: Available
requiredDataConnectors:
  - connectorId: MicrosoftThreatProtection
    dataTypes:
      - CloudAppEvents
      - AADSignInEventsBeta
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Impact
relevantTechniques:
  - T1485
query: |
  let relevantOperations = pack_array("FileDeleted", "FileRecycled", "FileDeletedFirstStageRecycleBin", "FileDeletedSecondStageRecycleBin", "FileVersionsAllMinorsRecycled", "FileVersionRecycled", "FileVersionsAllRecycled");
  let relevantAppIds = pack_array(int(20892), int(15600)); // App Ids for SharePoint and OneDrive
  let timeWindow = 7d;
  let timeNow = now();
  //
  let riskyUsers= // Look for users with risky sign-ins
    SigninLogs    
    | where CreatedDateTime between ((timeNow - timeWindow) .. (timeNow))
    | where isnotempty(UserId) and isnotempty(OriginalRequestId)
    | where ResultType == '0'
    | where RiskLevelDuringSignIn == 'high'
    | project UserId, RiskLevelDuringSignIn, CreatedDateTime, SessionId=OriginalRequestId
    ;
  let hasUsers = isnotempty(toscalar(riskyUsers));
  //
  let deleteEvents = // look for file deletion activity and scope it to risky users
    CloudAppEvents
    | where hasUsers
    | where TimeGenerated between ((timeNow - timeWindow) .. (timeNow))
    | where ApplicationId in (relevantAppIds)
    | where isnotempty(AccountObjectId)
    | where AccountObjectId in (riskyUsers)
    | where ActionType in (relevantOperations)
    | extend SessionId= tostring(RawEventData.AppAccessContext.AADSessionId)
    | where isnotempty(SessionId)
    | project UserId=AccountObjectId, AccountDisplayName, ApplicationId, SessionId, ActionType, TimeGenerated, ReportId
    ;   
   //
  deleteEvents  
  | join kind=leftsemi riskyUsers on UserId, SessionId
  | summarize Count=count() , (Timestamp, ReportId)=arg_min(TimeGenerated, ReportId) by UserId, AccountDisplayName, ApplicationId, ActionType, Time=bin(TimeGenerated, 5m)
  // look for only those scoped users who have generated an increase in file deletion activity.
  | summarize TotalCount= countif(Count > 50), (Timestamp, ReportId)=arg_min(Timestamp, ReportId) by UserId, AccountDisplayName, ApplicationId 
  | where TotalCount >= 3
  | project UserId, AccountDisplayName, ApplicationId, TotalCount, ReportId, Timestamp
  | extend NTDomain = tostring(split(AccountDisplayName,'\\',0)[0]), Name = tostring(split(AccountDisplayName,'\\',1)[0])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: AadUserId
        columnName: UserId
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: Name
      - identifier: NTDomain
        columnName: NTDomain
  - entityType: CloudApplication
    fieldMappings:
      - identifier: AppId
        columnName: ApplicationId
customDetails:
  Count: TotalCount
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let relevantAppIds = pack_array(int(20892), int(15600));
let timeWindow = 7d;
let timeNow = now();
let hasUsers = isnotempty(toscalar(riskyUsers));

let deleteEvents is inlined into the numbered stages below.

Let binding: relevantOperations used in Stage 8

let relevantOperations = pack_array("FileDeleted", "FileRecycled", "FileDeletedFirstStageRecycleBin", "FileDeletedSecondStageRecycleBin", "FileVersionsAllMinorsRecycled", "FileVersionRecycled", "FileVersionsAllRecycled");

Let binding: riskyUsers used in Stages 7, 12

let riskyUsers = SigninLogs    
  | where CreatedDateTime between ((timeNow - timeWindow) .. (timeNow))
  | where isnotempty(UserId) and isnotempty(OriginalRequestId)
  | where ResultType == '0'
  | where RiskLevelDuringSignIn == 'high'
  | project UserId, RiskLevelDuringSignIn, CreatedDateTime, SessionId=OriginalRequestId;

Stage 1: source

let deleteEvents

Stage 2: source

CloudAppEvents

Stage 3: where

where /* macro: hasUsers */

Stage 4: where

where /* macro: (TimeGenerated between ((timeNow - 7d) .. timeNow)) */

Stage 5: where

where ApplicationId =~ "relevantAppIds"

Stage 6: where

where isnotempty(AccountObjectId)

Stage 7: where

where AccountObjectId =~ "riskyUsers"

Stage 8: where

where ActionType =~ "relevantOperations"

Stage 9: extend

extend SessionId

Stage 10: where

where isnotempty(SessionId)

Stage 11: project

project AccountDisplayName, ActionType, ApplicationId, ReportId, SessionId, TimeGenerated, UserId

Stage 12: join

join kind=leftsemi (riskyUsers) on UserId, SessionId

Stage 13: summarize

summarize Count, ReportId, Time, Timestamp by UserId, AccountDisplayName, ApplicationId, ActionType, Time

Stage 14: summarize

summarize ReportId, Timestamp, TotalCount by UserId, AccountDisplayName, ApplicationId

Stage 15: where

where TotalCount >= 3

Stage 16: project

project AccountDisplayName, ApplicationId, ReportId, Timestamp, TotalCount, UserId

Stage 17: extend

extend NTDomain, Name

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
AccountDisplayNameproject
ApplicationIdproject
ReportIdproject
Timestampproject
TotalCountproject
UserIdproject
NTDomainextend
Nameextend