Detection rules › Kusto

SharePointFileOperation via previously unseen IPs

Status
available
Severity
medium
Time window
14d
Group by
ClientIP, Operation, Site_Url, UserId
Source
github.com/Azure/Azure-Sentinel

Identifies anomalies using user behavior by setting a threshold for significant changes in file upload/download activities from new IP addresses. It establishes a baseline of typical behavior, compares it to recent activity, and flags deviations exceeding a default threshold of 25.

MITRE ATT&CK coverage

TacticTechniques
Exfiltration

Telemetry coverage

Rules detecting the same action

These rules filter on the same operation.

Rule body

id: 4b11568b-3f5f-4ba1-80c8-7f1dc8390eb7
name: SharePointFileOperation via previously unseen IPs
description: |
  'Identifies anomalies using user behavior by setting a threshold for significant changes in file upload/download activities from new IP addresses. It establishes a baseline of typical behavior, compares it to recent activity, and flags deviations exceeding a default threshold of 25.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: Office365
    dataTypes:
      - OfficeActivity
queryFrequency: 1d
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Exfiltration
relevantTechniques:
  - T1030
query: |
  // Define a threshold for significant deviations
  let threshold = 25;
  // Define the name for the SharePoint File Operation record type
  let szSharePointFileOperation = "SharePointFileOperation";
  // Define an array of SharePoint operations of interest
  let szOperations = dynamic(["FileDownloaded", "FileUploaded"]);
  // Define the start and end time for the analysis period
  let starttime = 14d;
  let endtime = 1d;
  // Define a baseline of normal user behavior
  let userBaseline = OfficeActivity
  | where TimeGenerated between(ago(starttime)..ago(endtime))
  | where RecordType =~ szSharePointFileOperation
  | where Operation in~ (szOperations)
  | where isnotempty(UserAgent)
  | summarize Count = count() by UserId, Operation, Site_Url, ClientIP
  | summarize AvgCount = avg(Count) by UserId, Operation, Site_Url, ClientIP;
  // Get recent user activity
  let recentUserActivity = OfficeActivity
  | where TimeGenerated > ago(endtime)
  | where RecordType =~ szSharePointFileOperation
  | where Operation in~ (szOperations)
  | where isnotempty(UserAgent)
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), RecentCount = count() by UserId, UserType, Operation, Site_Url, ClientIP, OfficeObjectId, OfficeWorkload, UserAgent;
  // Join the baseline and recent activity, and calculate the deviation
  let UserBehaviorAnalysis = userBaseline | join kind=inner (recentUserActivity) on UserId, Operation, Site_Url, ClientIP
  | extend Deviation = abs(RecentCount - AvgCount) / AvgCount;
  // Filter for significant deviations
  UserBehaviorAnalysis
  | where Deviation > threshold
  | project StartTimeUtc, EndTimeUtc, UserId, UserType, Operation, ClientIP, Site_Url, OfficeObjectId, OfficeWorkload, UserAgent, Deviation, Count=RecentCount
  | order by Count desc, ClientIP asc, Operation asc, UserId asc
  | extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1])
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: UserId
      - identifier: Name
        columnName: AccountName
      - identifier: UPNSuffix
        columnName: AccountUPNSuffix
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIP
  - entityType: URL
    fieldMappings:
      - identifier: Url
        columnName: Site_Url
version: 2.0.4
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 25;
let szSharePointFileOperation = "SharePointFileOperation";
let szOperations = dynamic(["FileDownloaded", "FileUploaded"]);
let starttime = 14d;
let endtime = 1d;

let userBaseline and let UserBehaviorAnalysis are inlined into the numbered stages below.

Let binding: recentUserActivity used in Stage 8

let recentUserActivity = OfficeActivity
| where TimeGenerated > ago(endtime)
| where RecordType =~ szSharePointFileOperation
| where Operation in~ (szOperations)
| where isnotempty(UserAgent)
| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), RecentCount = count() by UserId, UserType, Operation, Site_Url, ClientIP, OfficeObjectId, OfficeWorkload, UserAgent;

Stages 1 to 9 define let UserBehaviorAnalysis (the rule's main pipeline source); stages 10 to 13 run on it.

Stage 1: source

OfficeActivity

Stage 2: where

| where TimeGenerated between(ago(starttime)..ago(endtime))

Stage 3: where

| where RecordType =~ szSharePointFileOperation

Stage 4: where

| where Operation in~ (szOperations)

Stage 5: where

| where isnotempty(UserAgent)

Stage 6: summarize

| summarize Count = count() by UserId, Operation, Site_Url, ClientIP

Stage 7: summarize

| summarize AvgCount = avg(Count) by UserId, Operation, Site_Url, ClientIP

Stage 8: join

| join kind=inner (recentUserActivity) on UserId, Operation, Site_Url, ClientIP

Stage 9: extend

| extend Deviation = abs(RecentCount - AvgCount) / AvgCount

Stage 10: where

UserBehaviorAnalysis
| where Deviation > threshold

Stage 11: project

| project StartTimeUtc, EndTimeUtc, UserId, UserType, Operation, ClientIP, Site_Url, OfficeObjectId, OfficeWorkload, UserAgent, Deviation, Count=RecentCount

Stage 12: sort

| order by Count desc, ClientIP asc, Operation asc, UserId asc

Stage 13: extend

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

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
ClientIPproject
Countproject
Deviationproject
EndTimeUtcproject
OfficeObjectIdproject
OfficeWorkloadproject
Operationproject
Site_Urlproject
StartTimeUtcproject
UserAgentproject
UserIdproject
UserTypeproject
AccountNameextend
AccountUPNSuffixextend