Adversaries may be granting high-risk permissions to unknown applications through OAuth consent to escalate privileges or access sensitive data. SOC teams should proactively hunt for this behavior in Azure Sentinel to identify potential unauthorized access attempts and mitigate risks associated with overly permissive application permissions.
KQL Query
let timeframe = 1d;
let lookback = 90d;
let HighRiskScopes = dynamic([
"RoleManagement.ReadWrite.Directory",
"Application.ReadWrite.All",
"AppRoleAssignment.ReadWrite.All",
"Directory.ReadWrite.All",
"User.ReadWrite.All",
"Mail.ReadWrite",
"Mail.Send",
"Files.ReadWrite.All",
"full_access_as_app"
]);
let KnownApps =
AuditLogs
| where TimeGenerated >= ago(timeframe + lookback) and TimeGenerated < ago(timeframe)
| where OperationName =~ "Consent to application"
| extend AppId = tostring(TargetResources[0].id)
| distinct AppId;
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "Consent to application"
| where Result =~ "success"
| extend AppId = tostring(TargetResources[0].id)
| extend AppName = tostring(TargetResources[0].displayName)
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorApp = tostring(InitiatedBy.app.displayName)
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| extend ActorIp = iff(
isnotempty(tostring(InitiatedBy.user.ipAddress)),
tostring(InitiatedBy.user.ipAddress),
tostring(InitiatedBy.app.ipAddress))
| mv-expand ModProp = TargetResources[0].modifiedProperties
| where tostring(ModProp.displayName) =~ "ConsentAction.Permissions"
| extend GrantedPermissions = tostring(ModProp.newValue)
| where GrantedPermissions has_any (HighRiskScopes)
| join kind=leftanti KnownApps on AppId
| extend AccountName = iff(Actor has "@", tostring(split(Actor, "@")[0]), Actor)
| extend AccountUPNSuffix = iff(Actor has "@", tostring(split(Actor, "@")[1]), "")
| project TimeGenerated, AppName, AppId, GrantedPermissions, Actor,
AccountName, AccountUPNSuffix, ActorIp, CorrelationId
| sort by TimeGenerated desc
id: 2a166359-a104-4d72-93ae-643ae69bf801
name: OAuth application consent to high-risk permission scope
description: |
Identifies OAuth application consent events where high-risk permissions such as
Directory.ReadWrite.All or RoleManagement.ReadWrite.Directory were granted to apps
with no prior tenant consent history in the preceding 90 days.
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- Persistence
- CredentialAccess
relevantTechniques:
- T1528
query: |
let timeframe = 1d;
let lookback = 90d;
let HighRiskScopes = dynamic([
"RoleManagement.ReadWrite.Directory",
"Application.ReadWrite.All",
"AppRoleAssignment.ReadWrite.All",
"Directory.ReadWrite.All",
"User.ReadWrite.All",
"Mail.ReadWrite",
"Mail.Send",
"Files.ReadWrite.All",
"full_access_as_app"
]);
let KnownApps =
AuditLogs
| where TimeGenerated >= ago(timeframe + lookback) and TimeGenerated < ago(timeframe)
| where OperationName =~ "Consent to application"
| extend AppId = tostring(TargetResources[0].id)
| distinct AppId;
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where OperationName =~ "Consent to application"
| where Result =~ "success"
| extend AppId = tostring(TargetResources[0].id)
| extend AppName = tostring(TargetResources[0].displayName)
| extend ActorUpn = tostring(InitiatedBy.user.userPrincipalName)
| extend ActorApp = tostring(InitiatedBy.app.displayName)
| extend Actor = iff(isnotempty(ActorUpn), ActorUpn, ActorApp)
| extend ActorIp = iff(
isnotempty(tostring(InitiatedBy.user.ipAddress)),
tostring(InitiatedBy.user.ipAddress),
tostring(InitiatedBy.app.ipAddress))
| mv-expand ModProp = TargetResources[0].modifiedProperties
| where tostring(ModProp.displayName) =~ "ConsentAction.Permissions"
| extend GrantedPermissions = tostring(ModProp.newValue)
| where GrantedPermissions has_any (HighRiskScopes)
| join kind=leftanti KnownApps on AppId
| extend AccountName = iff(Actor has "@", tostring(split(Actor, "@")[0]), Actor)
| extend AccountUPNSuffix = iff(Actor has "@", tostring(split(Actor, "@")[1]), "")
| project TimeGenerated, AppName, AppId, GrantedPermissions, Actor,
AccountName, AccountUPNSuffix, ActorIp, CorrelationId
| sort by TimeGenerated desc
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Actor
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ActorIp
version: 1.0.0
metadata:
source:
kind: Community
author:
name: descambiado
support:
tier: Community
categories:
domains: [ "Security - Threat Protection", "Identity" ]
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: A system administrator grants consent to a new third-party app for high-risk permissions during a tenant setup or migration.
Filter/Exclusion: UserPrincipalName == "[email protected]" || UserPrincipalName == "[email protected]"
Scenario: A scheduled job or automation tool (e.g., Azure DevOps pipeline) provisions an app with high-risk permissions for CI/CD purposes.
Filter/Exclusion: UserAgent contains "Azure DevOps" || UserAgent contains "PowerShell" || UserAgent contains "Azure CLI"
Scenario: A legitimate internal tool (e.g., Microsoft Teams) requests high-risk permissions during an onboarding process for a new team.
Filter/Exclusion: ApplicationDisplayName == "Microsoft Teams" || ApplicationDisplayName == "Microsoft 365 Admin" || ApplicationDisplayName == "Teams Admin"
Scenario: A service account (e.g., Azure Automation Run As account) is granted high-risk permissions to manage Azure resources.
Filter/Exclusion: UserPrincipalName contains "azureautomation" || UserPrincipalName contains "runas" || UserPrincipalName contains "service"
Scenario: A user with elevated privileges (e.g., Global Administrator) grants consent to a trusted internal app for administrative tasks.
Filter/Exclusion: UserPrincipalName == "[email protected]" || UserPrincipalName == "[email protected]" || UserPrincipalName == "[email protected]"