OAuth Illicit Consent Grant & Application Phishing
In enterprise cloud security, OAuth Illicit Consent Grant attacks (often termed Application Phishing) represent one of the most stealthy and resilient persistence mechanisms available to threat actors. Unlike credential theft or session cookie hijacking, an illicit consent grant does not require stealing the user’s password, does not trigger interactive MFA challenges during operational use, survives user password resets, and remains unaffected by device compliance policies.
By abusing the OAuth 2.0 authorization framework and Microsoft Entra ID’s multi-tenant application model, an adversary tricks a user into granting high-privilege delegated API permissions (Mail.ReadWrite, Files.ReadWrite.All, offline_access) to a malicious cloud application. Once approved, the adversary interacts directly with Microsoft Graph APIs using programmatic bearer tokens minted legitimately by Microsoft’s Security Token Service.
This guide provides an exhaustive forensic examination of illicit consent grants, details the role of high-risk scopes, analyzes audit telemetry across Entra ID and Purview, and outlines production hunting queries and emergency revocation playbooks.
1. OAuth 2.0 Consent Architecture in Microsoft Entra ID
Section titled “1. OAuth 2.0 Consent Architecture in Microsoft Entra ID”Microsoft Entra ID implements the OAuth 2.0 authorization code grant flow (RFC 6749) to allow third-party applications to access cloud resources on behalf of users:
sequenceDiagram autonumber participant Attacker as Adversary Infrastructure participant Victim as Target User Browser participant Entra as Microsoft Entra ID (STS) participant Graph as Microsoft Graph API / M365
Attacker->>Victim: Phishing Lure with OAuth Authorization URL Note over Victim: Victim clicks link: https://login.microsoftonline.com/common/oauth2/v2.0/authorize... Victim->>Entra: GET /authorize?client_id=ATTACKER_APP&scope=Mail.ReadWrite+offline_access Entra-->>Victim: Renders Authentic Microsoft Consent Dialog Screen Note over Victim: Dialog: "App wants permission to Read/Write your mail"<br/>Victim clicks "Accept" Entra->>Entra: 1. Instantiates Service Principal in Victim Tenant<br/>2. Writes OAuth2PermissionGrant Record<br/>3. Emits Entra Audit Log: "Consent to application" Entra-->>Victim: HTTP 302 Redirect to Attacker Redirect URI with ?code=AUTH_CODE Victim->>Attacker: Forwards Authorization Code Attacker->>Entra: POST /token (Exchanges code + client_secret) Entra-->>Attacker: Returns Access Token (JWT) + Refresh Token Attacker->>Graph: Programmatic API Queries (GET /v1.0/me/messages) Note over Attacker: Direct exfiltration without victim involvement or MFA!1.1 The Multi-Tenant App Relationship
Section titled “1.1 The Multi-Tenant App Relationship”- Attacker Tenant (Home): The adversary registers an application in their own Microsoft 365 tenant, configuring it as “Accounts in any organizational directory (Any Microsoft Entra directory - Multitenant)”.
- Victim Tenant (Resource): When the victim clicks “Accept” on the consent prompt, Entra ID creates a local Service Principal object representing the external application within the victim’s tenant directory.
- Delegated Grant Object: Entra ID creates an
OAuth2PermissionGrantentity binding the victim’sObjectIdto the application’sObjectIdwith the approved permission string.
2. High-Risk OAuth Scopes & The Power of offline_access
Section titled “2. High-Risk OAuth Scopes & The Power of offline_access”When inspecting consent events, investigators must scrutinize the requested scopes. Adversaries specifically target permissions that grant autonomous, unmonitored data access:
| OAuth Scope | Permission Category | Threat Actor Exploitation Capability |
|---|---|---|
offline_access | OpenID / Core | Indefinite Autonomous Persistence. Instructs Entra ID to issue an OAuth 2.0 Refresh Token. The attacker can refresh access tokens continuously for months without requiring the victim to be online or re-authenticate. |
Mail.Read / Mail.ReadWrite | Exchange / Graph | Grants programmatic access to read, search, modify, and delete all emails, folders, and attachments in the user’s mailbox. |
Mail.Send | Exchange / Graph | Dispatches emails directly from the victim’s address via Microsoft Graph API, bypassing Outlook client telemetry and local mail logs. |
Files.Read.All / Files.ReadWrite.All | SharePoint / OneDrive | Grants programmatic access to all files and document libraries accessible to the victim across SharePoint and OneDrive. |
Contacts.Read / People.Read | Directory / Graph | Scrapes the internal organizational hierarchy, identifying executive targets for secondary BEC fraud. |
Directory.Read.All | Directory (Admin) | Full directory reconnaissance; enumerates all users, groups, devices, and roles. |
3. Forensic Artifacts Across Entra Audit & Purview UAL
Section titled “3. Forensic Artifacts Across Entra Audit & Purview UAL”An illicit consent grant generates conspicuous audit events in both Microsoft Entra ID and the Purview Unified Audit Log.
graph TD CONSENT[User Accepts Illicit Consent Prompt] --> AUDIT_ENTRA[Entra ID AuditLogs Table] CONSENT --> AUDIT_UAL[Purview Unified Audit Log (UAL)]
AUDIT_ENTRA --> EVT1[Activity: Consent to application<br/>InitiatedBy: victim@target.com<br/>Target: ServicePrincipal Name & AppId] AUDIT_ENTRA --> EVT2[Activity: Add service principal<br/>TargetResources: AppId, ServicePrincipalId] AUDIT_ENTRA --> EVT3[Activity: Add OAuth2PermissionGrant<br/>Scope: Mail.ReadWrite offline_access]
AUDIT_UAL --> UAL_REC[RecordType: AzureActiveDirectory (15)<br/>Operation: Consent to application]
ATT_USE[Attacker Uses Token via Graph API] --> UAL_GRAPH[Workload: Exchange / SharePoint<br/>AuditData.AppId = Malicious AppId<br/>AuditData.UserId = victim@target.com]3.1 Entra ID Audit Log Payload (Consent to application)
Section titled “3.1 Entra ID Audit Log Payload (Consent to application)”When parsed from the Microsoft Graph Audit API or Sentinel AuditLogs, the payload contains:
{ "activityDateTime": "2026-03-22T10:15:30Z", "activityDisplayName": "Consent to application", "category": "ApplicationManagement", "result": "success", "initiatedBy": { "user": { "id": "11111111-2222-3333-4444-555555555555", "userPrincipalName": "victim@target.com", "ipAddress": "198.51.100.45" } }, "targetResources": [ { "id": "77777777-8888-9999-aaaa-bbbbbbbbbbbb", "displayName": "eSignature Cloud Validator", "type": "ServicePrincipal", "modifiedProperties": [ { "displayName": "ConsentAction.Permissions", "oldValue": "[]", "newValue": "[\"Mail.ReadWrite\",\"Files.ReadWrite.All\",\"offline_access\"]" }, { "displayName": "TargetId.ServicePrincipalNames", "oldValue": "[]", "newValue": "[\"a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d\"]" } ] } ]}Key fields for analysis:
initiatedBy.user.userPrincipalName: The identity that granted the permissions.targetResources[0].displayName: The deceptive display name chosen by the attacker (e.g.,"eSignature Cloud Validator","Zoom Meeting Integration").modifiedProperties[ConsentAction.Permissions]: The exact array of API scopes surrendered to the threat actor.modifiedProperties[TargetId.ServicePrincipalNames]: The multi-tenantAppId(GUID).
4. Production KQL Hunting Queries
Section titled “4. Production KQL Hunting Queries”4.1 Detecting Illicit Consent Grants with High-Risk Scopes
Section titled “4.1 Detecting Illicit Consent Grants with High-Risk Scopes”Identify any consent event where an end-user granted sensitive mailbox, file, or offline access scopes:
let SensitiveScopes = dynamic(["mail.read", "mail.readwrite", "mail.send", "files.read.all", "files.readwrite.all", "offline_access"]);AuditLogs| where TimeGenerated >= ago(30d)| where OperationName == "Consent to application"| extend InitiatedByUser = tostring(InitiatedBy.user.userPrincipalName), InitiatedByIP = tostring(InitiatedBy.user.ipAddress)| extend TargetApp = tostring(TargetResources[0].displayName), AppId = tostring(TargetResources[0].id)| extend ModifiedProps = TargetResources[0].modifiedProperties| mv-expand ModifiedProps| where ModifiedProps.displayName == "ConsentAction.Permissions"| extend GrantedPermissions = tostring(ModifiedProps.newValue)| where GrantedPermissions has_any (SensitiveScopes)| project TimeGenerated, InitiatedByUser, InitiatedByIP, TargetApp, AppId, GrantedPermissions| sort by TimeGenerated desc4.2 Correlating Consented Apps with Graph API Activity
Section titled “4.2 Correlating Consented Apps with Graph API Activity”Identify instances where a recently consented application executes automated data queries across mail or SharePoint:
let ConsentedApps = AuditLogs| where TimeGenerated >= ago(14d)| where OperationName in ("Consent to application", "Add service principal")| extend AppId = tostring(TargetResources[0].id)| distinct AppId;CloudAppEvents| where TimeGenerated >= ago(14d)| where ActionType in ("MailItemsAccessed", "FileDownloaded", "SearchQueryInitiatedExchange")| extend Raw = parse_json(RawEventData)| extend UsedAppId = tostring(Raw.AppId)| where UsedAppId in (ConsentedApps)| project TimeGenerated, AccountDisplayName, ActionType, UsedAppId, IPAddress, Raw| sort by TimeGenerated desc5. Emergency Incident Remediation & Hardening Playbook
Section titled “5. Emergency Incident Remediation & Hardening Playbook”When an illicit consent grant is identified, the response team must completely eliminate the application’s access and prevent similar attacks across the tenant.
graph TD DISCOVERY[Illicit Consent Identified] --> STEP1[1. Locate & Revoke OAuth2PermissionGrant<br/>Remove-MgOauth2PermissionGrant] STEP1 --> STEP2[2. Delete Local Service Principal<br/>Remove-MgServicePrincipal] STEP2 --> STEP3[3. Invalidate User Refresh Tokens<br/>Revoke-MgUserSignSession] STEP3 --> STEP4[4. Restrict Tenant Consent Settings<br/>Disable user consent via Enterprise Apps policy] STEP4 --> STEP5[5. Enable Admin Consent Workflow<br/>Require admin approval for all third-party apps]5.1 PowerShell Remediation Script
Section titled “5.1 PowerShell Remediation Script”# Prerequisites: Microsoft.Graph.Applications, Microsoft.Graph.Identity.SignIns# Connect-MgGraph -Scopes "Application.ReadWrite.All","DelegatedPermissionGrant.ReadWrite.All","User.ReadWrite.All"
$targetAppId = "a1b2c3d4-e5f6-7a8b-9c0d-1e2f3a4b5c6d"$victimUPN = "victim@target.com"
Write-Host "[*] INITIATING REMEDIATION FOR ILLICIT OAUTH APP: $targetAppId" -ForegroundColor Red
# 1. Locate the Service Principal in the tenant$sp = Get-MgServicePrincipal -Filter "appId eq '$targetAppId'"if (-not $sp) { Write-Error "Service Principal not found for AppId: $targetAppId" return}Write-Host "[+] Found Service Principal: $($sp.DisplayName) (Id: $($sp.Id))" -ForegroundColor Yellow
# 2. Identify and delete all delegated OAuth permission grants for this app$grants = Get-MgOauth2PermissionGrant -Filter "clientId eq '$($sp.Id)'"foreach ($grant in $grants) { Write-Host "[!] Revoking OAuth2 Permission Grant: $($grant.Id) (Scopes: $($grant.Scope))" -ForegroundColor Yellow Remove-MgOauth2PermissionGrant -OAuth2PermissionGrantId $grant.Id}
# 3. Delete the Service Principal from the tenantRemove-MgServicePrincipal -ServicePrincipalId $sp.IdWrite-Host "[+] Service Principal successfully deleted from tenant." -ForegroundColor Green
# 4. Revoke user session tokens as an added security measureRevoke-MgUserSignSession -UserId $victimUPNWrite-Host "[+] Revoked active sessions for victim: $victimUPN" -ForegroundColor Green5.2 Tenant-Wide Hardening: Restricting User Consent
Section titled “5.2 Tenant-Wide Hardening: Restricting User Consent”To prevent end-users from granting permissions to unverified multi-tenant apps, configure the tenant consent policy:
# Update Authorization Policy to disable user self-consentUpdate-MgPolicyAuthorizationPolicy -DefaultUserRolePermissions @{ AllowedToCreateApps = $false PermissionGrantPoliciesAssigned = @("ManagePermissionGrantsForSelf.microsoft-user-default-low")}Write-Host "[+] Tenant configured: End users restricted from granting unverified high-privilege app permissions." -ForegroundColor Green6. Cross-Reference & Investigation Navigation
Section titled “6. Cross-Reference & Investigation Navigation”- Previous Fiche: 22. Forensic Detection & Investigation of AiTM Attacks
- Next Fiche: 24. Password Spraying & Brute-Force Telemetry Analysis
- Related Guides: