Entra ID End-User Consent to Application with High-Risk Delegated Scopes
Identifies an end-user (non-admin) consent grant in Microsoft Entra ID to an application requesting delegated scopes commonly abused in illicit consent grant (OAuth phishing) attacks, such as mail, mailbox settings, contacts, files, SharePoint, OneNote, Teams chat and Exchange Web Services. Graph scopes are evaluated together with offline_access, which returns a long-lived refresh token and gives an attacker-controlled application durable, silent access to the victim's data without stealing a password or re-prompting for MFA. The alert classifies the application owner as Microsoft first-party, in-tenant or external to help prioritize review.
Elastic rule (View on GitHub)
1[metadata]
2creation_date = "2026/10/02"
3integration = ["azure"]
4maturity = "production"
5min_stack_comments = "MV_CONTAINS was added to ES|QL in 9.2.0 and is not available on 8.19; 9.3.0 is the lowest supported 9.x release."
6min_stack_version = "9.3.0"
7updated_date = "2026/10/02"
8
9[rule]
10author = ["Elastic", "Eduard Arbona"]
11description = """
12Identifies an end-user (non-admin) consent grant in Microsoft Entra ID to an application requesting delegated scopes
13commonly abused in illicit consent grant (OAuth phishing) attacks, such as mail, mailbox settings, contacts, files,
14SharePoint, OneNote, Teams chat and Exchange Web Services. Graph scopes are evaluated together with offline_access, which
15returns a long-lived refresh token and gives an attacker-controlled application durable, silent access to the victim's
16data without stealing a password or re-prompting for MFA. The alert classifies the application owner as Microsoft
17first-party, in-tenant or external to help prioritize review.
18"""
19false_positives = [
20 """
21 Onboarding of a sanctioned mail client, add-in, backup, archiving, CRM, calendar or SharePoint application that
22 legitimately requires delegated access with a refresh token. Validate publisher verification, application ownership
23 (Esql.app_owner_type) and whether the scopes align with an approved business use case, then add an exception for the
24 reviewed application. Microsoft first-party developer tooling such as Graph Explorer or Microsoft Graph Command Line
25 Tools may legitimately request mailbox or file scopes when used by IT staff. Restricting end-user consent to verified
26 publishers and low-risk permissions reduces this residual set to unsanctioned applications only.
27 """,
28]
29from = "now-9m"
30interval = "8m"
31language = "esql"
32license = "Elastic License v2"
33name = "Entra ID End-User Consent to Application with High-Risk Delegated Scopes"
34note = """## Triage and analysis
35
36### Investigating Entra ID End-User Consent to Application with High-Risk Delegated Scopes
37
38Adversaries trick a user into granting OAuth consent to an application that requests delegated scopes giving access to mail, files, SharePoint, OneNote, contacts or Teams chats, typically together with `offline_access`. Because `offline_access` yields a long-lived refresh token, the application keeps access silently, without re-authentication or MFA. This is a common precursor to business email compromise, mailbox and file exfiltration, and internal phishing.
39
40This rule inspects Entra ID audit logs for successful `Consent to application` events where `ConsentContext.IsAdminConsent` is false and the granted permissions (`ConsentAction.Permissions`) contain a high-risk delegated scope family. Entra logs one consent event per resource API, so Microsoft Graph scopes and `offline_access` share an event, while SharePoint Online (`AllSites.*`, `MyFiles.*`) and Exchange Web Services (`EWS.AccessAsUser.All`, `full_access_as_user`) scopes arrive in a separate event and are alerted on independently.
41
42The alert includes the following derived fields:
43
44- `Esql.high_risk_scopes` and `Esql.high_risk_scope_count`: matched high-risk scope families (`mail`, `mailbox_settings`, `contacts`, `files`, `sites`, `notes`, `teams_chat`, `sharepoint_spo`, `exchange_ews`).
45- `Esql.low_risk_scopes` and `Esql.low_risk_scope_count`: matched reconnaissance scope families (`people`, `calendars`, `online_meetings`, `tasks`, `user_directory`, `impersonation`). `impersonation` (`user_impersonation`, `Directory.AccessAsUser.All`) is ignored for Microsoft first-party applications such as Azure CLI and Azure PowerShell.
46- `Esql.app_owner_type`: `microsoft` (Microsoft-owned or well-known first-party public client), `tenant` (application registered in this tenant) or `external` (multi-tenant application owned by another tenant, or owner not recorded on this event).
47- `Esql.sp_provisioned_by_consent`: `true` when the service principal was created by this consent, meaning the application had never been seen in the tenant before.
48
49#### Possible investigation steps
50
51- Review `azure.auditlogs.properties.target_resources.0.display_name` and `azure.auditlogs.properties.target_resources.0.id` (the service principal) to determine which application was granted access. Pivot on the AppId in `azure.auditlogs.properties.additional_details.value` to the Enterprise Applications blade in the Entra portal and check publisher verification and the home tenant.
52- Treat `Esql.app_owner_type: external` together with `Esql.sp_provisioned_by_consent: true` as the highest priority combination; this is the classic pattern of a newly introduced third-party application obtaining mailbox or file access through a single user.
53- Review `azure.auditlogs.properties.initiated_by.user.userPrincipalName` and `azure.auditlogs.properties.initiated_by.user.ipAddress` to identify the consenting user and the source of the consent. Investigate their recent activity for signs of phishing or account compromise.
54- Inspect `azure.auditlogs.properties.target_resources.0.modified_properties.4.new_value` to confirm the exact delegated scopes granted and whether write or send capability (`Mail.ReadWrite`, `Mail.Send`, `Files.ReadWrite.All`, `AllSites.FullControl`) was included.
55- Pivot on `azure.auditlogs.properties.correlation_id` to find sibling consent events for other resource APIs granted in the same flow, and to related sign-in and token-usage activity for the same application.
56- Search for follow-on activity by the application such as inbox rule creation, mail forwarding, bulk mail or file downloads, and Teams chat reads.
57
58### False positive analysis
59
60- Sanctioned mail clients, add-ins, backup, archiving, CRM, calendar or SharePoint tools may legitimately request delegated access with a refresh token. Validate publisher verification, ownership and scope alignment, then add an exception for the reviewed application.
61- Microsoft first-party developer tooling (Graph Explorer, Microsoft Graph Command Line Tools) classified as `Esql.app_owner_type: microsoft` can legitimately request mailbox or file scopes when used by IT or development staff. Confirm the consenting user has a reason to use the tool; unexpected users consenting to these clients can also indicate device code phishing.
62- Line-of-business applications registered in the tenant (`Esql.app_owner_type: tenant`) are expected in development-heavy organizations, but attackers with a compromised account can also register applications in the victim tenant.
63
64### Response and remediation
65
66- Revoke the application's OAuth grant (for example `Remove-MgOauth2PermissionGrant` / `Remove-AzureADOAuth2PermissionGrant`) and disable or delete the associated service principal.
67- Revoke the affected user's refresh tokens and require re-authentication.
68- Block the application via Conditional Access or Defender for Cloud Apps.
69- Enable the admin consent workflow and restrict end-user consent to applications from verified publishers.
70"""
71references = [
72 "https://learn.microsoft.com/en-us/entra/architecture/security-operations-applications#end-user-consent",
73 "https://learn.microsoft.com/en-us/security/operations/incident-response-playbook-app-consent",
74 "https://learn.microsoft.com/en-us/entra/identity/enterprise-apps/manage-app-consent-policies",
75 "https://learn.microsoft.com/en-us/defender-cloud-apps/investigate-risky-oauth",
76 "https://www.microsoft.com/en-us/security/blog/2024/01/25/midnight-blizzard-guidance-for-responders-on-nation-state-attack/",
77 "https://github.com/AlteredSecurity/365-Stealer"
78]
79risk_score = 47
80rule_id = "e907ca08-45f2-4f8b-9854-c66be0e985dd"
81severity = "medium"
82tags = [
83 "Domain: Cloud",
84 "Domain: Identity",
85 "Data Source: Azure",
86 "Data Source: Microsoft Entra ID",
87 "Data Source: Microsoft Entra ID Audit Logs",
88 "Platform: Entra ID",
89 "Profile: Beta",
90 "Use Case: Identity and Access Audit",
91 "Threat: OAuth App Consent",
92 "Resources: Investigation Guide",
93 "Tactic: Initial Access",
94 "Tactic: Credential Access",
95 "Rule Type: ES|QL",
96]
97type = "esql"
98
99query = '''
100FROM logs-azure.auditlogs-* metadata _id, _version, _index
101| WHERE (azure.auditlogs.operation_name == "Consent to application"
102 OR event.action == "Consent to application")
103 AND event.outcome == "success"
104 // End-user (non-admin) consent only
105 AND TO_LOWER(`azure.auditlogs.properties.target_resources.0.modified_properties.0.new_value`) LIKE "*false*"
106
107// Normalize ConsentAction.Permissions into a lower-cased, space-padded token string so " token " is an exact scope match.
108// Raw: "[] => [[Id: .., ResourceId: .., ConsentType: Principal, Scope: Mail.Read offline_access .., ..]]"
109| EVAL Esql.scopes = CONCAT(" ", REPLACE(TO_LOWER(`azure.auditlogs.properties.target_resources.0.modified_properties.4.new_value`), """[\[\],;"]""", " "), " ")
110
111// additional_details.key/value are parallel arrays but ES|QL returns multivalues sorted, so pair via MV_CONTAINS, not position.
112// Owner = AppOwnerOrganizationId (Microsoft tenants) OR well-known first-party public-client AppIds (not always stamped with owner org).
113| EVAL
114 Esql.app_owner_type = CASE(
115 MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "f8cdef31-a31e-4b4a-93e4-5f571e91255a") // Microsoft Services
116 OR MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "72f988bf-86f1-41af-91ab-2d7cd011db47") // Microsoft corp
117 OR MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "14d82eec-204b-4c2f-b7e8-296a70dab67e") // Microsoft Graph Command Line Tools
118 OR MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "de8bc8b5-d9f9-48b1-a8ad-b748da725064") // Graph Explorer
119 OR MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "04b07795-8ddb-461a-bbee-02f9e1bf7b46") // Azure CLI
120 OR MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "1950a258-227b-4e31-a9cf-717495945fc2") // Azure PowerShell
121 OR MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "c44b4083-3bb0-49c1-b47d-974e53cbdf3c") // Azure Portal
122 OR MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "aebc6443-996d-45c2-90f0-388ff96faa56"), "microsoft", // VS Code
123 MV_CONTAINS(azure.auditlogs.properties.additional_details.value, azure.tenant_id), "tenant",
124 "external"),
125 // "Consent" = service principal was created by this consent, i.e. first time the app appears in the tenant
126 Esql.sp_provisioned_by_consent = MV_CONTAINS(azure.auditlogs.properties.additional_details.value, "Consent")
127
128// High-risk scope families: in-the-wild consent phishing (MS IR playbook), 365-Stealer/GraphRunner defaults, Midnight Blizzard EWS.
129// Positive enumeration excludes low-risk variants (Mail.ReadBasic, Chat.ReadBasic, Files.*.Selected) without lookarounds.
130| EVAL
131 Esql.high_risk_scopes = TRIM(CONCAT(
132 CASE(Esql.scopes RLIKE """.* mail\.(read|readwrite|send)(\.shared)? .*""", "mail ", ""),
133 CASE(Esql.scopes RLIKE """.* mailbox(settings|item|folder)\.[a-z]+ .*""", "mailbox_settings ", ""),
134 CASE(Esql.scopes RLIKE """.* contacts\.[a-z.]+ .*""", "contacts ", ""),
135 CASE(Esql.scopes RLIKE """.* files\.(read|readwrite)(\.all|\.appfolder)? .*""", "files ", ""),
136 CASE(Esql.scopes RLIKE """.* sites\.[a-z]+\.all .*""", "sites ", ""),
137 CASE(Esql.scopes RLIKE """.* notes\.[a-z.]+ .*""", "notes ", ""),
138 CASE(Esql.scopes RLIKE """.* (chat\.(read|readwrite|create)(\.all)?|chatmessage\.[a-z]+) .*""", "teams_chat ", ""),
139 CASE(Esql.scopes RLIKE """.* (allsites\.[a-z]+|myfiles\.[a-z]+) .*""", "sharepoint_spo ", ""),
140 CASE(Esql.scopes RLIKE """.* (ews\.accessasuser\.all|full_access_as_user) .*""", "exchange_ews ", ""))),
141 // Low-risk scope families: recon / lower impact. ARM user_impersonation and Directory.AccessAsUser.All only count for
142 // non-Microsoft apps (Azure CLI / PowerShell / Portal / VS Code consent noise).
143 Esql.low_risk_scopes = TRIM(CONCAT(
144 CASE(Esql.scopes RLIKE """.* people\.read(\.all)? .*""", "people ", ""),
145 CASE(Esql.scopes RLIKE """.* calendars\.[a-z.]+ .*""", "calendars ", ""),
146 CASE(Esql.scopes RLIKE """.* onlinemeetings\.[a-z]+ .*""", "online_meetings ", ""),
147 CASE(Esql.scopes RLIKE """.* tasks\.[a-z.]+ .*""", "tasks ", ""),
148 CASE(Esql.scopes RLIKE """.* user\.(read\.all|readbasic\.all) .*""", "user_directory ", ""),
149 CASE(Esql.app_owner_type != "microsoft" AND Esql.scopes RLIKE """.* (user_impersonation|directory\.accessasuser\.all) .*""", "impersonation ", "")))
150| EVAL
151 Esql.high_risk_scope_count = CASE(Esql.high_risk_scopes == "", 0, MV_COUNT(SPLIT(Esql.high_risk_scopes, " "))),
152 Esql.low_risk_scope_count = CASE(Esql.low_risk_scopes == "", 0, MV_COUNT(SPLIT(Esql.low_risk_scopes, " ")))
153
154// Graph families require offline_access in the same event (durable access). SharePoint/EWS resource scopes are logged in a
155// separate event without offline_access, so they alert on their own. External apps stacking 2+ low-risk scopes also alert.
156| WHERE (Esql.scopes LIKE "* offline_access *" AND Esql.high_risk_scope_count >= 1)
157 OR Esql.high_risk_scopes LIKE "*sharepoint_spo*"
158 OR Esql.high_risk_scopes LIKE "*exchange_ews*"
159 OR (Esql.scopes LIKE "* offline_access *" AND Esql.low_risk_scope_count >= 2 AND Esql.app_owner_type == "external")
160| EVAL Esql.target_service_principal_id = `azure.auditlogs.properties.target_resources.0.id`
161| DROP Esql.scopes
162| KEEP
163 _id,
164 _version,
165 _index,
166 @timestamp,
167 event.action,
168 event.outcome,
169 azure.tenant_id,
170 azure.auditlogs.operation_name,
171 azure.auditlogs.properties.correlation_id,
172 azure.auditlogs.properties.initiated_by.user.userPrincipalName,
173 azure.auditlogs.properties.initiated_by.user.id,
174 azure.auditlogs.properties.initiated_by.user.ipAddress,
175 `azure.auditlogs.properties.target_resources.0.display_name`,
176 `azure.auditlogs.properties.target_resources.0.id`,
177 `azure.auditlogs.properties.target_resources.0.modified_properties.4.new_value`,
178 azure.auditlogs.properties.additional_details.key,
179 azure.auditlogs.properties.additional_details.value,
180 Esql.*
181'''
182
183[rule.alert_suppression]
184group_by = ["azure.auditlogs.properties.initiated_by.user.userPrincipalName", "Esql.target_service_principal_id"]
185missing_fields_strategy = "doNotSuppress"
186
187[rule.alert_suppression.duration]
188unit = "h"
189value = 6
190
191
192
193[[rule.threat]]
194framework = "MITRE ATT&CK"
195
196[[rule.threat.technique]]
197id = "T1566"
198name = "Phishing"
199reference = "https://attack.mitre.org/techniques/T1566/"
200
201[[rule.threat.technique.subtechnique]]
202id = "T1566.002"
203name = "Spearphishing Link"
204reference = "https://attack.mitre.org/techniques/T1566/002/"
205
206[rule.threat.tactic]
207id = "TA0001"
208name = "Initial Access"
209reference = "https://attack.mitre.org/tactics/TA0001/"
210
211[[rule.threat]]
212framework = "MITRE ATT&CK"
213
214[[rule.threat.technique]]
215id = "T1528"
216name = "Steal Application Access Token"
217reference = "https://attack.mitre.org/techniques/T1528/"
218
219[rule.threat.tactic]
220id = "TA0006"
221name = "Credential Access"
222reference = "https://attack.mitre.org/tactics/TA0006/"
Triage and analysis
Investigating Entra ID End-User Consent to Application with High-Risk Delegated Scopes
Adversaries trick a user into granting OAuth consent to an application that requests delegated scopes giving access to mail, files, SharePoint, OneNote, contacts or Teams chats, typically together with offline_access. Because offline_access yields a long-lived refresh token, the application keeps access silently, without re-authentication or MFA. This is a common precursor to business email compromise, mailbox and file exfiltration, and internal phishing.
This rule inspects Entra ID audit logs for successful Consent to application events where ConsentContext.IsAdminConsent is false and the granted permissions (ConsentAction.Permissions) contain a high-risk delegated scope family. Entra logs one consent event per resource API, so Microsoft Graph scopes and offline_access share an event, while SharePoint Online (AllSites.*, MyFiles.*) and Exchange Web Services (EWS.AccessAsUser.All, full_access_as_user) scopes arrive in a separate event and are alerted on independently.
The alert includes the following derived fields:
Esql.high_risk_scopesandEsql.high_risk_scope_count: matched high-risk scope families (mail,mailbox_settings,contacts,files,sites,notes,teams_chat,sharepoint_spo,exchange_ews).Esql.low_risk_scopesandEsql.low_risk_scope_count: matched reconnaissance scope families (people,calendars,online_meetings,tasks,user_directory,impersonation).impersonation(user_impersonation,Directory.AccessAsUser.All) is ignored for Microsoft first-party applications such as Azure CLI and Azure PowerShell.Esql.app_owner_type:microsoft(Microsoft-owned or well-known first-party public client),tenant(application registered in this tenant) orexternal(multi-tenant application owned by another tenant, or owner not recorded on this event).Esql.sp_provisioned_by_consent:truewhen the service principal was created by this consent, meaning the application had never been seen in the tenant before.
Possible investigation steps
- Review
azure.auditlogs.properties.target_resources.0.display_nameandazure.auditlogs.properties.target_resources.0.id(the service principal) to determine which application was granted access. Pivot on the AppId inazure.auditlogs.properties.additional_details.valueto the Enterprise Applications blade in the Entra portal and check publisher verification and the home tenant. - Treat
Esql.app_owner_type: externaltogether withEsql.sp_provisioned_by_consent: trueas the highest priority combination; this is the classic pattern of a newly introduced third-party application obtaining mailbox or file access through a single user. - Review
azure.auditlogs.properties.initiated_by.user.userPrincipalNameandazure.auditlogs.properties.initiated_by.user.ipAddressto identify the consenting user and the source of the consent. Investigate their recent activity for signs of phishing or account compromise. - Inspect
azure.auditlogs.properties.target_resources.0.modified_properties.4.new_valueto confirm the exact delegated scopes granted and whether write or send capability (Mail.ReadWrite,Mail.Send,Files.ReadWrite.All,AllSites.FullControl) was included. - Pivot on
azure.auditlogs.properties.correlation_idto find sibling consent events for other resource APIs granted in the same flow, and to related sign-in and token-usage activity for the same application. - Search for follow-on activity by the application such as inbox rule creation, mail forwarding, bulk mail or file downloads, and Teams chat reads.
False positive analysis
- Sanctioned mail clients, add-ins, backup, archiving, CRM, calendar or SharePoint tools may legitimately request delegated access with a refresh token. Validate publisher verification, ownership and scope alignment, then add an exception for the reviewed application.
- Microsoft first-party developer tooling (Graph Explorer, Microsoft Graph Command Line Tools) classified as
Esql.app_owner_type: microsoftcan legitimately request mailbox or file scopes when used by IT or development staff. Confirm the consenting user has a reason to use the tool; unexpected users consenting to these clients can also indicate device code phishing. - Line-of-business applications registered in the tenant (
Esql.app_owner_type: tenant) are expected in development-heavy organizations, but attackers with a compromised account can also register applications in the victim tenant.
Response and remediation
- Revoke the application's OAuth grant (for example
Remove-MgOauth2PermissionGrant/Remove-AzureADOAuth2PermissionGrant) and disable or delete the associated service principal. - Revoke the affected user's refresh tokens and require re-authentication.
- Block the application via Conditional Access or Defender for Cloud Apps.
- Enable the admin consent workflow and restrict end-user consent to applications from verified publishers.
References
Related rules
- Entra ID Illicit Consent Grant via Registered Application
- Entra ID Multiple Device Registrations by a Single User
- Entra ID Device-Bound PRT Replay via First-Party App from Unusual IP
- Entra ID Kali365 Default User-Agent Detected
- Microsoft Entra ID Impossible Travel Sign-in