Detection rules › Kusto

Malicious Inbox Rule

Status
available
Severity
medium
Time window
1d
Group by
ClientIPAddress, Keyword, OfficeObjectId, Operation, OriginatingServer, ResultStatus, RuleDetail, UserId
Source
github.com/Azure/Azure-Sentinel

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

Telemetry coverage

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 =
ifClientIP has "."tostring(split(ClientIP, ":")[0])
elifClientIP has "["tostring(trim_start(@'[[]', tostring(split(ClientIP, "]")[0])))
elseClientIP

Stage 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.

FieldKindValuesSearch
BodyContainsWordsmatch
  • alert transforms: term
  • suspicious transforms: term
  • Fatal transforms: term
  • do not click transforms: term
  • do not open transforms: term
  • fake transforms: term
  • helpdesk transforms: term
  • hijacked transforms: term
  • malicious transforms: term
  • phishing transforms: term
  • spam transforms: term
field:"BodyContainsWords" kind:match
OfficeWorkloadeq
  • Exchange
field:"m365::Workload" kind:eq value:"Exchange"
Operationeq
  • New-InboxRule
field:"Operation" kind:eq value:"New-InboxRule"
Parametersmatch
  • DeleteMessage transforms: term
  • Deleted Items transforms: term
  • Junk Email transforms: term
field:"m365::Parameters" kind:match
ResultStatuseq
  • Succeeded
  • True
field:"ResultStatus" kind:eq
SubjectContainsWordsmatch
  • alert transforms: term
  • suspicious transforms: term
  • Fatal transforms: term
  • do not click transforms: term
  • do not open transforms: term
  • fake transforms: term
  • helpdesk transforms: term
  • hijacked transforms: term
  • malicious transforms: term
  • phishing transforms: term
  • spam transforms: term
field:"SubjectContainsWords" kind:match
SubjectOrBodyContainsWordsmatch
  • alert transforms: term
  • suspicious transforms: term
  • Fatal transforms: term
  • do not click transforms: term
  • do not open transforms: term
  • fake transforms: term
  • helpdesk transforms: term
  • hijacked transforms: term
  • malicious transforms: term
  • phishing transforms: term
  • spam transforms: term
field:"SubjectOrBodyContainsWords" kind:match

Output fields

These fields are emitted when the rule matches.

FieldSource
ClientIPAddresssummarize
EndTimeUtcsummarize
Keywordsummarize
OfficeObjectIdsummarize
Operationsummarize
OriginatingServersummarize
ResultStatussummarize
RuleDetailsummarize
StartTimeUtcsummarize
UserIdsummarize
AccountNameextend
AccountUPNSuffixextend
OriginatingServerNameextend