4 min read

Sentinel Basics - Analytic Rules

Thought I may as well begin to share some Microsoft Sentinel Basics I have learnt over the last few months amongst other studies that I am currently completing.

This article is about Analytic Rules and creating a basic rule to alert on the creation of Malicious mailbox rules within an environment. Attackers use compromised accounts to create mailbox rules, a simple process that enables the attackers to maintain a quiet persistent access to the mailbox - they can use this for a whole variety of malicious purposes.

Building these labs have been a good way to begin to understand KQL without relying on prebuilt queries.

Why would we want this rule in place?

If attackers have managed to compromise an account, they can use inbox rules to quietly move information out of the network via that account's mailbox, they may even delete emails from senior executives that they are pretending to be in an attempt to extract money. Automated email rules can be a simple process that enables the attackers to maintain stealthy, persistent access to the mailbox.

Detecting the activity

To start we need to find what operation will need to be included in the detection. By searching in the ingested logs for the operations that have already been captured.

Values and log forwarding rules.

Amongst the attacker creating inbox rules manually via the Outlook desktop client and OWA they may also use the Exchange PowerShell module.

Luckily auditing logs will capture the use of the Exchange PowerShell module.


Red Canary's detection engineering team determined the following "operation" values within the auditing logs to be the note worthy.
New-InboxRule, Set-InboxRule, Remove-InboxRule, Disable-InboxRule Operation values typically show up when someone is using the PowerShell cmdlet or OWA.
UpdateInboxRules is typically seen when rules are created or modified via an Outlook Desktop client.

There are other values, but I will be leaving those out, more information can be found from the Red Canary Blog around email forwarding rules.

Setting up the detection

Navigating to the Analytics tab under the configuration menu, create a new Scheduled query rule

Filling out the below details, starting by naming the rule, giving the rule a descriptive description, setting the severity of the rule and assigning the relevant MITRE ATT&CK Tactic - Persistence and Defense Evasion.

Next setting the rule logic.

The below KQL code was grabbed from the default sentinel rule, now I'm not claiming to be considered a pro at KQL, but so far I am aware that of the below code is that:

let = is used to set a value to a variable.
So the below values "helpdesk", "alert", etc... are being added to the variable 'Keywords'.

OfficeActivity_CL is the log source, all Office Activity is ingested into the log source.

'where' allows you filter from the above log source

let Keywords = dynamic(["helpdesk", " alert", " suspicious", "fake", "malicious", "phishing", "spam", "do not click", "do not open", "hijacked", "Fatal"]);
OfficeActivity_CL
| where Operation_s =~ "New-InboxRule"
| where Parameters_s has "Deleted Items" or Parameters_s has "Junk Email" 
| extend Events=todynamic(Parameters_s)
| 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_s has ".", tostring(split(ClientIP_s,":")[0]), ClientIP_s has "[", tostring(trim_start(@'[[]',tostring(split(ClientIP_s,"]")[0]))), ClientIP_s )
| extend Keyword = iff(isnotempty(SubjectContainsWords), SubjectContainsWords, (iff(isnotempty(BodyContainsWords),BodyContainsWords,SubjectOrBodyContainsWords )))
| extend RuleDetail = case(OfficeObjectId_s contains '/' , tostring(split(OfficeObjectId_s, '/')[-1]) , tostring(split(OfficeObjectId_s, '\\')[-1]))
| summarize count(), StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated) by  Operation_s, UserId__s, ClientIPAddress, ResultStatus_s, Keyword, OriginatingServer_s, OfficeObjectId_s, RuleDetail

The corresponding log would now cause the rule to trigger.

{"CreationTime":"2022-01-01T01:01:01","Id":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","Operation":"New-InboxRule","OrganizationId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx","RecordType":1,"ResultStatus":"True","UserKey":"xxxxxxxxxxxxxxxx","UserType":2,"Version":1,"Workload":"Exchange","ClientIP":"x.x.x.x:x","ObjectId":"Some Username\\Forward All","UserId":"Some_Username@contoso.com","AppId":"","ClientAppId":"","ExternalAccess":false,"OrganizationName":"contoso.onmicrosoft.com","OriginatingServer":"server (15.20.5000.013)","Parameters":[{"Name":"Mailbox","Value":"Some_Username@contoso.com"},{"Name":"Name","Value":"Forward All"},{"Name":"RedirectTo","Value":"attacker@c0nt0s0.net"}],"SessionId":"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"}

Testing the rule against the ingested logs in sentinel helps me see what the query will find, also gives me a chance to validate the data that I'm wanting it to find to prevent creating a dud rule that could subsequently spam alerts if incorrectly configured.

Scrolling down I have entities that I can map to their appropriate fields.

Entities I want to map to this rule are Account, the Host Machine and the IP.

you can read as well as I can, this will enable sentinel to recognise and classify the data in these fields for further analysis.

Next we want to be alerted for when this rule is triggered so I have set the alert format and threshold as per the below screenshot.

Now with the rule created navigating back to Sentinel and searching under Incidents, we can see the newly created rule is working with 3 alerts triggered.

Nice now we can begin to fine tune custom rules.

Onward and upward :)