Detection rules › Kusto
Malicious Inbox Rule
Often times after the initial compromise the attackers create inbox rules to delete emails that contain certain keywords. This is done so as to limit ability to warn compromised users that they've been compromised. Below is a sample query that tries to detect this. Reference: https://www.reddit.com/r/sysadmin/comments/7kyp0a/recent_phishing_attempts_my_experience_and_what/
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Persistence | |
| Stealth |
Telemetry coverage
| Platform | Record / event type |
|---|---|
| Microsoft 365 | Exchange audit operation New-InboxRule: Federated domain added |
Rules detecting the same action
These rules filter on the same operation.
Rule body
id: 7b907bf7-77d4-41d0-a208-5643ff75bf9a
name: Malicious Inbox Rule
description: |
'Often times after the initial compromise the attackers create inbox rules to delete emails that contain certain keywords.
This is done so as to limit ability to warn compromised users that they've been compromised. Below is a sample query that tries to detect this.
Reference: https://www.reddit.com/r/sysadmin/comments/7kyp0a/recent_phishing_attempts_my_experience_and_what/'
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: Office365
dataTypes:
- OfficeActivity (Exchange)
queryFrequency: 1d
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Persistence
- DefenseEvasion
relevantTechniques:
- T1098
- T1078
query: |
let Keywords = dynamic(["helpdesk", " alert", " suspicious", "fake", "malicious", "phishing", "spam", "do not click", "do not open", "hijacked", "Fatal"]);
OfficeActivity
| where OfficeWorkload =~ "Exchange"
| where Operation =~ "New-InboxRule" and (ResultStatus =~ "True" or ResultStatus =~ "Succeeded")
| where Parameters has "Deleted Items" or Parameters has "Junk Email" or Parameters has "DeleteMessage"
| extend Events=todynamic(Parameters)
| parse Events with * "SubjectContainsWords" SubjectContainsWords '}'*
| parse Events with * "BodyContainsWords" BodyContainsWords '}'*
| parse Events with * "SubjectOrBodyContainsWords" SubjectOrBodyContainsWords '}'*
| where SubjectContainsWords has_any (Keywords)
or BodyContainsWords has_any (Keywords)
or SubjectOrBodyContainsWords has_any (Keywords)
| extend ClientIPAddress = case( ClientIP has ".", tostring(split(ClientIP,":")[0]), ClientIP has "[", tostring(trim_start(@'[[]',tostring(split(ClientIP,"]")[0]))), ClientIP )
| extend Keyword = iff(isnotempty(SubjectContainsWords), SubjectContainsWords, (iff(isnotempty(BodyContainsWords),BodyContainsWords,SubjectOrBodyContainsWords )))
| extend RuleDetail = case(OfficeObjectId contains '/' , tostring(split(OfficeObjectId, '/')[-1]) , tostring(split(OfficeObjectId, '\\')[-1]))
| summarize count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by Operation, UserId, ClientIPAddress, ResultStatus, Keyword, OriginatingServer, OfficeObjectId, RuleDetail
| extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1])
| extend OriginatingServerName = tostring(split(OriginatingServer, " ")[0])
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: UserId
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: Host
fieldMappings:
- identifier: FullName
columnName: OriginatingServerName
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ClientIPAddress
version: 2.0.4
kind: Scheduled
Stages and Predicates
Parameters
let Keywords = dynamic(["helpdesk", " alert", " suspicious", "fake", "malicious", "phishing", "spam", "do not click", "do not open", "hijacked", "Fatal"]);
Stage 1: source
OfficeActivity
Stage 2: where
| where OfficeWorkload =~ "Exchange"
Stage 3: where
| where Operation =~ "New-InboxRule" and (ResultStatus =~ "True" or ResultStatus =~ "Succeeded")
Stage 4: where
| where Parameters has "Deleted Items" or Parameters has "Junk Email" or Parameters has "DeleteMessage"
Stage 5: extend
| extend Events=todynamic(Parameters)
Stage 6: parse
| parse Events with * "SubjectContainsWords" SubjectContainsWords '}'*
Stage 7: parse
| parse Events with * "BodyContainsWords" BodyContainsWords '}'*
Stage 8: parse
| parse Events with * "SubjectOrBodyContainsWords" SubjectOrBodyContainsWords '}'*
Stage 9: where
| where SubjectContainsWords has_any (Keywords)
or BodyContainsWords has_any (Keywords)
or SubjectOrBodyContainsWords has_any (Keywords)
Stage 10: extend (3 consecutive steps)
| extend ClientIPAddress = case( ClientIP has ".", tostring(split(ClientIP,":")[0]), ClientIP has "[", tostring(trim_start(@'[[]',tostring(split(ClientIP,"]")[0]))), ClientIP )
| extend Keyword = iff(isnotempty(SubjectContainsWords), SubjectContainsWords, (iff(isnotempty(BodyContainsWords),BodyContainsWords,SubjectOrBodyContainsWords )))
| extend RuleDetail = case(OfficeObjectId contains '/' , tostring(split(OfficeObjectId, '/')[-1]) , tostring(split(OfficeObjectId, '\\')[-1]))
ClientIPAddress =if
ClientIP has "."tostring(split(ClientIP, ":")[0])elif
ClientIP has "["tostring(trim_start(@'[[]', tostring(split(ClientIP, "]")[0])))else
ClientIPStage 11: summarize
| summarize count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by Operation, UserId, ClientIPAddress, ResultStatus, Keyword, OriginatingServer, OfficeObjectId, RuleDetail
Stage 12: extend
| extend AccountName = tostring(split(UserId, "@")[0]), AccountUPNSuffix = tostring(split(UserId, "@")[1])
Stage 13: extend
| extend OriginatingServerName = tostring(split(OriginatingServer, " ")[0])
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
BodyContainsWords | match |
| field:"BodyContainsWords" kind:match |
OfficeWorkload | eq |
| field:"m365::Workload" kind:eq value:"Exchange" |
Operation | eq |
| field:"Operation" kind:eq value:"New-InboxRule" |
Parameters | match |
| field:"m365::Parameters" kind:match |
ResultStatus | eq |
| field:"ResultStatus" kind:eq |
SubjectContainsWords | match |
| field:"SubjectContainsWords" kind:match |
SubjectOrBodyContainsWords | match |
| field:"SubjectOrBodyContainsWords" kind:match |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
ClientIPAddress | summarize |
EndTimeUtc | summarize |
Keyword | summarize |
OfficeObjectId | summarize |
Operation | summarize |
OriginatingServer | summarize |
ResultStatus | summarize |
RuleDetail | summarize |
StartTimeUtc | summarize |
UserId | summarize |
AccountName | extend |
AccountUPNSuffix | extend |
OriginatingServerName | extend |