15.8 Detection Engineering

3-5 hours · Module 15

Detection Engineering

Five analytics rules that detect consent phishing patterns. These complement the M11-M13 detection packs.

Required role: Microsoft Sentinel Contributor.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
// Scheduled Rule: OAuth consent granting high-risk permissions
// Schedule: 15 minutes / Lookback: 20 minutes
// Severity: High
// MITRE: T1550.001 (Application Access Token)
AuditLogs
| where OperationName == "Consent to application"
| where Result == "success"
| extend AppName = tostring(TargetResources[0].displayName)
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| extend Permissions = tostring(TargetResources[0].modifiedProperties)
| where Permissions has_any ("Mail.ReadWrite", "Mail.Send",
    "Files.ReadWrite.All", "Directory.ReadWrite.All",
    "MailboxSettings.ReadWrite", "User.ReadWrite.All")
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| project TimeGenerated, ConsentUser, AppName, Permissions, IPAddress

Entity mapping: Account → ConsentUser (FullName). IP → IPAddress (Address).


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
// Scheduled Rule: OAuth consent from external IP  possible phishing
// Schedule: 15 minutes / Lookback: 20 minutes
// Severity: Medium
// MITRE: T1550.001
let KnownCorpIPs = _GetWatchlist('CorporateExternalIPs') | project SearchKey;
AuditLogs
| where OperationName == "Consent to application"
| where Result == "success"
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| where IPAddress !in (KnownCorpIPs)
| where isnotempty(IPAddress)
| extend AppName = tostring(TargetResources[0].displayName)
| project TimeGenerated, ConsentUser, AppName, IPAddress

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Scheduled Rule: Multiple users consenting to the same app in a short window
// Schedule: 1 hour / Lookback: 4 hours
// Severity: High
// MITRE: T1550.001
AuditLogs
| where TimeGenerated > ago(4h)
| where OperationName == "Consent to application"
| where Result == "success"
| extend AppName = tostring(TargetResources[0].displayName)
| extend AppId = tostring(TargetResources[0].id)
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| summarize
    UserCount = dcount(ConsentUser),
    Users = make_set(ConsentUser, 20),
    FirstConsent = min(TimeGenerated),
    LastConsent = max(TimeGenerated)
    by AppName, AppId
| where UserCount > 3
| project AppName, AppId, UserCount, Users, FirstConsent, LastConsent

What this detects: Consent phishing campaigns. More than 3 users consenting to the same application in 4 hours is almost always a phishing campaign — not organic adoption.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Scheduled Rule: Consent to application created in the last 7 days
// Schedule: 1 hour / Lookback: 2 hours
// Severity: Medium
// MITRE: T1098.003 (Additional Cloud Credentials)
let NewApps = AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName == "Add application"
| extend AppId = tostring(TargetResources[0].id)
| project AppCreated = TimeGenerated, AppId;
AuditLogs
| where OperationName == "Consent to application"
| where Result == "success"
| extend AppId = tostring(TargetResources[0].id)
| extend AppName = tostring(TargetResources[0].displayName)
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| join kind=inner NewApps on AppId
| extend DaysSinceCreation = datetime_diff('day', TimeGenerated, AppCreated)
| where DaysSinceCreation < 7
| project TimeGenerated, ConsentUser, AppName, AppId, DaysSinceCreation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
// Scheduled Rule: Application accesses Exchange within 1 hour of consent
// Schedule: 1 hour / Lookback: 2 hours
// Severity: High
// MITRE: T1528 (Steal Application Access Token)
let RecentConsents = AuditLogs
| where TimeGenerated > ago(2h)
| where OperationName == "Consent to application"
| where Result == "success"
| extend AppId = tostring(TargetResources[0].id)
| project ConsentTime = TimeGenerated, AppId;
AADServicePrincipalSignInLogs
| where ResultType == "0"
| where ResourceDisplayName in ("Microsoft Exchange Online", "Microsoft Graph",
    "Office 365 SharePoint Online")
| join kind=inner RecentConsents on AppId
| where TimeGenerated between(ConsentTime .. (ConsentTime + 1h))
| project AppId, ConsentTime, AccessTime = TimeGenerated,
    ResourceDisplayName, IPAddress,
    MinutesAfterConsent = datetime_diff('minute', TimeGenerated, ConsentTime)

What this detects: An application that accesses data within 1 hour of receiving consent. Legitimate applications may be used days or weeks after consent. An application that starts accessing Exchange or Graph within minutes of consent is likely automated exfiltration — the attacker’s script starts harvesting data as soon as the consent is granted.


Deployment checklist

For each rule: create in Sentinel Analytics, configure entity mapping, set MITRE technique, test against historical data, enable, monitor.

Subsection artifact: 5 deployable consent phishing detection rules. Combined with M11 (8), M12 (6), and M13 (5): 24 detection rules total covering the complete M365 attack surface from credential theft through persistence and financial fraud.

Compliance mapping: NIST CSF DE.AE-2. ISO 27001 A.8.16.


Consent phishing detection rules generate more false positives than AiTM or BEC rules because legitimate application consent is common. Tune before deploying:

Rule 1 (High-Risk Permissions) tuning: Maintain an exclusion list of known legitimate applications that request high-risk permissions. Examples: your helpdesk tool (may request Mail.ReadWrite to manage tickets), your backup solution (may request Files.ReadWrite.All), your SSO provider (may request Directory.Read.All). Add these AppIds to the rule’s exclusion filter.

Rule 3 (Bulk Consent) tuning: Adjust the threshold based on your organisation’s application adoption patterns. If IT regularly deploys new applications and instructs 20 users to consent simultaneously: the threshold of 3 users in 4 hours generates false positives. Raise to 5 or 10 users. Alternatively: create an exclusion for consents that occur within 1 hour of an IT announcement email (correlate with EmailEvents from IT distribution lists).

Rule 4 (New Application) tuning: Exclude applications registered by your IT team’s admin accounts. Internal applications are new by definition — the rule should only flag consents to applications registered in external tenants.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Identify your top consented applications for exclusion tuning
AuditLogs
| where TimeGenerated > ago(90d)
| where OperationName == "Consent to application"
| where Result == "success"
| extend AppName = tostring(TargetResources[0].displayName)
| extend AppId = tostring(TargetResources[0].id)
| summarize ConsentCount = count() by AppName, AppId
| order by ConsentCount desc
| take 20

The top 20 most-consented applications are almost certainly legitimate. Add them to your exclusion list. Any new application NOT in this list that receives a consent is worth investigating.

Try it yourself

Run the top-20 consented applications query against your tenant. Review each: do you recognise the application? Is the publisher verified? Are the permissions appropriate? This produces your initial exclusion list for the consent detection rules — deploy the rules with these exclusions from day one to reduce false positive noise.

What you should produce

A list of 10-20 AppIds to exclude from consent detection rules. Each with: application name, publisher, permissions, and your assessment (legitimate / investigate / remove). This is both a detection tuning exercise and the first step of your quarterly consent audit.