Detection rules › Kusto
SharePointFileOperation via previously unseen IPs
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
| Tactic | Techniques |
|---|---|
| Exfiltration |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | FileDownloaded |
| Microsoft 365 | FileUploaded |
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
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.
| Field | Kind | Values | Search |
|---|---|---|---|
Deviation | gt |
| field:"Deviation" kind:gt value:"25" |
Operation | in |
| field:"Operation" kind:in |
RecordType | eq |
| field:"RecordType" kind:eq value:"SharePointFileOperation" |
UserAgent | is_not_null | field:"aws::userAgent" kind:is_not_null |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ClientIP | project |
Count | project |
Deviation | project |
EndTimeUtc | project |
OfficeObjectId | project |
OfficeWorkload | project |
Operation | project |
Site_Url | project |
StartTimeUtc | project |
UserAgent | project |
UserId | project |
UserType | project |
AccountName | extend |
AccountUPNSuffix | extend |