15.3 Identifying Malicious Consent Grants

3-5 hours · Module 15

Required role: Microsoft Sentinel Reader for KQL. Application Administrator or Cloud Application Administrator for application metadata review.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// All consent grants for the malicious application
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName == "Consent to application"
| where Result == "success"
| extend AppName = tostring(TargetResources[0].displayName)
| where AppName == "Microsoft Security Update Tool"
| extend ConsentUser = tostring(InitiatedBy.user.userPrincipalName)
| extend AppId = tostring(TargetResources[0].id)
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| extend Permissions = tostring(TargetResources[0].modifiedProperties)
| project TimeGenerated, ConsentUser, AppName, AppId, IPAddress, Permissions
| order by TimeGenerated asc

What to examine: The list of users who consented, the timestamp pattern (clustered = phishing), the IPs (corporate = users clicked from work, external = users clicked from home/mobile — both are valid for phishing), and the permissions granted.


Step 2: Assess the application metadata

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
// Application details  is this a legitimate or malicious app?
AuditLogs
| where OperationName == "Consent to application"
| where tostring(TargetResources[0].displayName) == "Microsoft Security Update Tool"
| take 1
| extend AppProperties = parse_json(tostring(TargetResources[0].modifiedProperties))
| mv-expand AppProperties
| project PropertyName = tostring(AppProperties.displayName),
    OldValue = tostring(AppProperties.oldValue),
    NewValue = tostring(AppProperties.newValue)

Red flags indicating a malicious application:

Publisher is unverified (no blue verified badge in Entra ID → Enterprise Applications). The application is registered in an external tenant. The application name mimics a Microsoft product (“Microsoft Security Update Tool,” “Office 365 Document Viewer,” “Azure AD Compliance Scanner”). The requested permissions are high-risk (Mail.ReadWrite, Files.ReadWrite.All, Directory.ReadWrite.All). The application was registered recently (check the registration date — if it was created the same week as the phishing campaign, it is almost certainly malicious).


Step 3: Extract the exact permissions granted

1
2
3
4
5
6
7
8
9
// Parse the specific permissions granted to the malicious application
AuditLogs
| where OperationName == "Consent to application"
| where tostring(TargetResources[0].displayName) == "Microsoft Security Update Tool"
| take 1
| extend Props = parse_json(tostring(TargetResources[0].modifiedProperties))
| mv-expand Props
| where tostring(Props.displayName) == "ConsentAction.Permissions"
| project Permissions = tostring(Props.newValue)

Cross-reference the permissions against the risk table from subsection 15.1. If Mail.ReadWrite, Mail.Send, or Files.ReadWrite.All are present: the application can read/modify/send email and access all files. This is a full data compromise for every consenting user.


Step 4: Check the application’s sign-in activity

1
2
3
4
5
6
7
8
// Has the malicious application actually authenticated and accessed resources?
AADServicePrincipalSignInLogs
| where TimeGenerated > ago(7d)
| where ServicePrincipalName == "Microsoft Security Update Tool"
    or AppId == "MALICIOUS-APP-ID"
| project TimeGenerated, IPAddress, ResourceDisplayName,
    ResultType, ServicePrincipalName
| order by TimeGenerated asc

If this returns results: the application has already authenticated and is actively accessing data. The IPAddress shows the attacker’s infrastructure. The ResourceDisplayName shows which M365 services were accessed (Exchange Online = email, SharePoint = files, Graph = directory).

If zero results: the application was consented but has not yet been used. Revoke immediately — the attacker may not have started exfiltration yet.

Subsection artifact: The 4-step consent identification queries. These form the identification section of your OAuth investigation playbook.


Knowledge check


Not every consent is malicious. Develop a scoring system to prioritise investigation:

IndicatorScoreRationale
Unverified publisher+3Legitimate enterprise apps are verified
App registered in external tenant+2Internal apps are in your tenant
App registered < 30 days ago+3Phishing apps are freshly registered
Mail.ReadWrite or Mail.Send permission+3Highest-risk email permissions
Files.ReadWrite.All permission+3Full file access
Directory.ReadWrite.All permission+4Directory modification — critical
Multiple users consented within 4 hours+4Phishing campaign pattern
Consent from non-corporate IP+1May be remote worker — context needed
App name mimics Microsoft product+3Social engineering technique

Score interpretation: 0-3: likely legitimate. 4-7: investigate further. 8+: high probability malicious — investigate immediately.

Apply this scoring to every consent event in the tenant-wide audit (subsection 15.6). It transforms the audit from a manual review of 47 applications into a prioritised investigation queue.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Automated red flag scoring for consent events
AuditLogs
| where TimeGenerated > ago(365d)
| 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)
| extend Permissions = tostring(TargetResources[0].modifiedProperties)
| extend IPAddress = tostring(InitiatedBy.user.ipAddress)
| summarize
    ConsentCount = dcount(ConsentUser),
    Users = make_set(ConsentUser, 20),
    FirstConsent = min(TimeGenerated),
    LastConsent = max(TimeGenerated)
    by AppName, AppId, Permissions
| extend Score = 0
    + iff(ConsentCount > 3 and datetime_diff('hour', LastConsent, FirstConsent) < 4, 4, 0)
    + iff(Permissions has_any ("Mail.ReadWrite", "Mail.Send"), 3, 0)
    + iff(Permissions has "Files.ReadWrite.All", 3, 0)
    + iff(Permissions has "Directory.ReadWrite.All", 4, 0)
| where Score > 3
| project AppName, AppId, ConsentCount, Score, Users, Permissions
| order by Score desc

This automated scoring surfaces the highest-risk consents first — the applications most likely to be malicious. Review the top-scoring applications manually. Applications scoring 8+ warrant immediate investigation; 4-7 warrant review within 24 hours.

Check your understanding

1. The application's AADServicePrincipalSignInLogs show zero sign-in events. Does this mean no data was compromised?

The consent was granted but the application has not yet been used. This is good — revoke immediately before the attacker starts exfiltration. However, verify: some application API calls may not appear in service principal sign-in logs (depending on the API and the logging configuration). Check CloudAppEvents for any Graph API or Exchange API calls from the application's AppId as a secondary verification.
Correct — no sign-ins means no data accessed
The logs are delayed — wait 24 hours
Application sign-ins are not logged