In an illicit consent grant attack, the lure is a link to a real Microsoft consent screen for an app the attacker registered. Once a user clicks Accept, the app holds delegated access to mail, files or contacts through a refresh token, with no credential theft and no MFA challenge involved. The grant is logged in Entra audit logs, which makes it detectable if you watch the right operations.
Signals at a Glance
| Signal | Where | ATT&CK |
|---|---|---|
| User consent to an app requesting mail, files or offline_access scopes | Entra AuditLogs | T1528 |
| First-seen app consented by two or more users in a short window | Entra AuditLogs | T1566.002 |
| Admin consent granted to an unverified publisher or new app | Entra AuditLogs | T1528 |
| App sign-in or Graph calls from infrastructure unrelated to the publisher | Service principal sign-in logs | T1550.001 |
| New mailbox rules or bulk mail reads by the app after consent | Unified audit log, Graph activity | T1114.002 |
Catching the Grant
The consent event is the earliest reliable signal. Entra records Consent to application and related grant operations in AuditLogs, with the app and the granted permissions in the target resource. Start by flagging grants that include scopes with high abuse value:
// Microsoft Sentinel (KQL) - consent grants that include high-value delegated scopes
let riskyScopes = dynamic(["Mail.Read","Mail.ReadWrite","Mail.Send","Files.Read.All","Files.ReadWrite.All",
"Contacts.Read","MailboxSettings.ReadWrite","User.Read.All","Directory.ReadWrite.All","offline_access"]);
AuditLogs
| where OperationName in ("Consent to application", "Add delegated permission grant", "Add app role assignment grant to user")
| where Result =~ "success"
| extend App = tostring(TargetResources[0].displayName),
AppId = tostring(TargetResources[0].id),
Actor = tostring(InitiatedBy.user.userPrincipalName),
ActorIp = tostring(InitiatedBy.user.ipAddress),
Perms = tostring(TargetResources[0].modifiedProperties)
| where Perms has_any (riskyScopes)
| project TimeGenerated, OperationName, Actor, ActorIp, App, AppId, Perms
// tune/validate: offline_access alone is common; baseline your sanctioned apps and allowlist by AppId
- Alert on any
IsAdminConsentgrant to an app you did not register or approve, since admin consent applies tenant-wide. - Treat unverified publishers requesting mail or file scopes as high priority; verified publisher status is a useful but not decisive filter.
- Restrict user consent in the tenant (admin consent workflow, limit to verified publishers and low-risk permissions) so the detection fires on exceptions rather than routine traffic.
Spotting the Campaign
Phishing campaigns reuse one app across many victims, so the strongest pattern is a first-seen application consented by several users within hours. This query separates it from your normal app catalogue:
// KQL - new app consented by multiple distinct users in the last day
let known = AuditLogs
| where TimeGenerated between (ago(30d) .. ago(1d))
| where OperationName == "Consent to application"
| extend AppId = tostring(TargetResources[0].id)
| distinct AppId;
AuditLogs
| where TimeGenerated > ago(1d)
| where OperationName == "Consent to application" and Result =~ "success"
| extend AppId = tostring(TargetResources[0].id),
App = tostring(TargetResources[0].displayName),
User = tostring(InitiatedBy.user.userPrincipalName)
| where AppId !in (known)
| summarize Users = dcount(User), FirstSeen = min(TimeGenerated), UserList = make_set(User, 20) by AppId, App
| where Users >= 2
// tune/validate: raise the threshold in large tenants or add a publisher-domain filter
- Pivot on the AppId in the service principal sign-in logs and Graph activity logs to see what the app read after consent.
- Contain by revoking the grant, disabling the service principal and revoking user refresh tokens; resetting the password alone does not end an app grant.
- Search for look-alike display names (for example, security or document-sharing brands) paired with a reply URL on an unrelated domain.
Use Microsoft’s Entra guidance on detecting and remediating illicit consent grants, Microsoft Sentinel and Defender for Cloud Apps app-governance content, and the SigmaHQ Azure audit rules as starting points. Baseline your own environment first: sanctioned SaaS integrations generate legitimate consent events constantly.
Build Detections That Fire
Mjolnir Security helps blue teams turn threat intelligence into high-fidelity detections across endpoint, network, identity and cloud telemetry.
- Detection Engineering We build and tune detections mapped to ATT&CK from your own telemetry.
- Threat Hunting as a Service Hypothesis-driven hunts for the behaviours on this page.
- 24/7 Incident Response Containment when a detection fires. Call +1 833 403 5875.
Stay ahead of emerging threats. Get notified when we publish new intelligence reports and advisories.
Subscribe to Alerts