Adversaries may exploit OAuth consent to gain high-risk permissions by deploying new or rarely seen applications to escalate privileges undetected. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential unauthorized access and privilege escalation attempts early.
KQL Query
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let appLookback = starttime - 30d;
let highRiskScopes = dynamic([
"Mail.ReadWrite", "Mail.Send", "Files.ReadWrite.All",
"full_access_as_app", "Directory.ReadWrite.All",
"EWS.AccessAsUser.All", "Exchange.ManageAsApp"
]);
// Applications seen in the tenant during the 30-day baseline period
let KnownApps =
AuditLogs
| where TimeGenerated between (appLookback .. starttime)
| where OperationName =~ "Consent to application"
| extend AppName = tolower(tostring(TargetResources[0].displayName))
| where isnotempty(AppName)
| summarize by AppName;
// Consent events in the current window
AuditLogs
| where TimeGenerated between (starttime .. endtime)
| where OperationName =~ "Consent to application"
| extend ModProps = TargetResources[0].modifiedProperties
| extend AppName = tolower(tostring(TargetResources[0].displayName))
| extend ActorUpn = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend ActorApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend ActorIp = iff(
isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)),
tostring(parse_json(tostring(InitiatedBy.user)).ipAddress),
tostring(parse_json(tostring(InitiatedBy.app)).ipAddress))
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| mv-expand ModProps
| extend PropertyName = tostring(ModProps.displayName)
| extend NewValue = replace_string(tostring(ModProps.newValue), '"', "")
| where PropertyName =~ "ConsentAction.Permissions"
// Extract individual scope tokens from the permission string
| extend ScopeRaw = extract(@'Scope:\s*([^,\]]*)', 1, NewValue)
| extend Scopes = split(trim(" ", ScopeRaw), " ")
| mv-expand Scope = Scopes to typeof(string)
| extend Scope = trim(" ", Scope)
| where Scope in~ (highRiskScopes)
// Keep only applications not seen in the 30-day baseline
| join kind=leftanti KnownApps on AppName
| extend AccountName = iff(ActorUpn has "@", tostring(split(ActorUpn, "@")[0]), Actor)
| extend AccountUPNSuffix = iff(ActorUpn has "@", tostring(split(ActorUpn, "@")[1]), "")
| summarize
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
GrantedScopes = make_set(Scope),
EventCount = count()
by AppName, Actor, AccountName, AccountUPNSuffix, ActorIp, CorrelationId, Result
| sort by FirstSeen desc
id: c449826b-d6d0-4ac8-8dad-19acc3fc75a7
name: OAuth consent to high-risk permission by a new or rarely seen application
description: |
Hunting query that identifies OAuth consent events where the granted permission scope
includes high-risk delegated or application permissions, and where the target application
has not been observed in the tenant during the previous 30 days. The combination of a
newly observed application and a high-privilege scope can indicate a consent phishing
attempt, an illicit OAuth grant, or an insider threat granting excessive permissions
to an external application.
High-risk scopes covered include Mail.ReadWrite, Mail.Send, Files.ReadWrite.All,
full_access_as_app, Directory.ReadWrite.All, EWS.AccessAsUser.All, and
Exchange.ManageAsApp. Analysts must validate every result. Benign matches include
newly deployed enterprise applications with legitimate mail or directory integration,
freshly registered automation accounts, and authorized ISV integrations.
This query is complementary to ConsentToApplicationDiscovery.yaml, which covers all
consent events regardless of scope or application age.
References:
- https://learn.microsoft.com/azure/active-directory/manage-apps/protect-against-consent-phishing
- https://learn.microsoft.com/azure/active-directory/reports-monitoring/reference-audit-activities
- https://attack.mitre.org/techniques/T1528/
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- CredentialAccess
relevantTechniques:
- T1528
query: |
let starttime = todatetime('{{StartTimeISO}}');
let endtime = todatetime('{{EndTimeISO}}');
let appLookback = starttime - 30d;
let highRiskScopes = dynamic([
"Mail.ReadWrite", "Mail.Send", "Files.ReadWrite.All",
"full_access_as_app", "Directory.ReadWrite.All",
"EWS.AccessAsUser.All", "Exchange.ManageAsApp"
]);
// Applications seen in the tenant during the 30-day baseline period
let KnownApps =
AuditLogs
| where TimeGenerated between (appLookback .. starttime)
| where OperationName =~ "Consent to application"
| extend AppName = tolower(tostring(TargetResources[0].displayName))
| where isnotempty(AppName)
| summarize by AppName;
// Consent events in the current window
AuditLogs
| where TimeGenerated between (starttime .. endtime)
| where OperationName =~ "Consent to application"
| extend ModProps = TargetResources[0].modifiedProperties
| extend AppName = tolower(tostring(TargetResources[0].displayName))
| extend ActorUpn = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend ActorApp = tostring(parse_json(tostring(InitiatedBy.app)).displayName)
| extend ActorIp = iff(
isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)),
tostring(parse_json(tostring(InitiatedBy.user)).ipAddress),
tostring(parse_json(tostring(InitiatedBy.app)).ipAddress))
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: Scheduled Backup Job Using OAuth for Cloud Storage Access
Description: A legitimate scheduled backup job uses an OAuth token to access cloud storage (e.g., AWS S3, Google Drive) with high-risk permissions like storage.full_control.
Filter/Exclusion: Exclude events where the application is a known backup tool (e.g., Veeam, Duplicati) or where the user is a system admin performing routine backups.
Scenario: Admin Task to Grant Permissions to a New Monitoring Tool
Description: An admin grants high-risk permissions (e.g., user.read, mail.read) to a new monitoring tool (e.g., Datadog, New Relic) for telemetry and analytics.
Filter/Exclusion: Exclude events where the user is a domain admin or where the application is listed in a pre-approved tool inventory (e.g., allowed_apps.txt).
Scenario: CI/CD Pipeline Authenticating with OAuth to Deploy to Cloud Services
Description: A CI/CD pipeline (e.g., Jenkins, GitHub Actions) authenticates via OAuth to deploy code to a cloud service (e.g., Azure DevOps, AWS EC2) and requests high-risk permissions for deployment.
Filter/Exclusion: Exclude events where the application is a known CI/CD tool or where the request is part of a pre-approved deployment workflow (e.g., ci_cd_allowed_apps).
Scenario: User Accessing a High-Risk API for Internal Reporting
Description: A user accesses a high-risk API (e.g., Microsoft Graph with user.read or mail.read) to generate internal reports using a new application (e.g., Power BI, Tableau).
Filter/Exclusion: Exclude events where the user is part of a reporting team or where the application is used