Adversaries may modify the RequiredResourceAccess property of an OAuth application to gain unauthorized access to sensitive resources. SOC teams should proactively hunt for this behavior in Azure Sentinel to detect potential lateral movement or privilege escalation attempts.
KQL Query
AuditLogs
| where Category =~ "ApplicationManagement"
| where ActivityDisplayName has_any ("Update application")
| where Result =~ "success"
| where tostring(InitiatedBy.user.userPrincipalName) has "@" or tostring(InitiatedBy.app.displayName) has "@"
| extend UserAgent = tostring(AdditionalDetails[0].value)
| extend InitiatingUser = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend InitiatingIpAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| extend ModifiedApplication = tostring(TargetResources[0].displayName)
| extend ModifiedApplicationObjectId = tostring(TargetResources[0].id)
| extend ModifiedProperties = parse_json(tostring(TargetResources[0].modifiedProperties))
| extend ModifiedPropertyName = tostring(ModifiedProperties[0].displayName)
| extend ResourceAppId = parse_json(tostring(ModifiedProperties[0].newValue))[0].ResourceAppId
| where ModifiedPropertyName =~ "RequiredResourceAccess"
| extend Type = tostring(TargetResources[0].type)
| project-away ModifiedProperties
| project-reorder TimeGenerated, OperationName, InitiatingUser, InitiatingIpAddress, UserAgent, ModifiedApplication, ModifiedApplicationObjectId, CorrelationId, TenantId
| extend timestamp = TimeGenerated, AccountCustomEntity = InitiatingUser, IPCustomEntity = InitiatingIpAddress
id: 665e6075-3a3f-42c0-a6c7-7e83dc44f281
name: OAuth Application Required Resource Access Update
description: |
'This hunting query identifies updates to the RequiredResourceAccess property of an OAuth application.
This property specifies resources that an application requires access to and the set of OAuth permission scopes and application roles that it needs under each of those resources.
This pre-configuration of required resource access drives the consent experience. The resourceAccess property of the requiredResourceAccess type is a collection of ResourceAccess.
A threat actor might update this property before granting permissions to an application.
'
severity: Medium
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- Persistence
relevantTechniques:
- T1098
tags:
- SimuLand
query: |
AuditLogs
| where Category =~ "ApplicationManagement"
| where ActivityDisplayName has_any ("Update application")
| where Result =~ "success"
| where tostring(InitiatedBy.user.userPrincipalName) has "@" or tostring(InitiatedBy.app.displayName) has "@"
| extend UserAgent = tostring(AdditionalDetails[0].value)
| extend InitiatingUser = tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)
| extend InitiatingIpAddress = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| extend ModifiedApplication = tostring(TargetResources[0].displayName)
| extend ModifiedApplicationObjectId = tostring(TargetResources[0].id)
| extend ModifiedProperties = parse_json(tostring(TargetResources[0].modifiedProperties))
| extend ModifiedPropertyName = tostring(ModifiedProperties[0].displayName)
| extend ResourceAppId = parse_json(tostring(ModifiedProperties[0].newValue))[0].ResourceAppId
| where ModifiedPropertyName =~ "RequiredResourceAccess"
| extend Type = tostring(TargetResources[0].type)
| project-away ModifiedProperties
| project-reorder TimeGenerated, OperationName, InitiatingUser, InitiatingIpAddress, UserAgent, ModifiedApplication, ModifiedApplicationObjectId, CorrelationId, TenantId
| extend timestamp = TimeGenerated, AccountCustomEntity = InitiatingUser, IPCustomEntity = InitiatingIpAddress
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
version: 1.0.0
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: Scheduled Job Updates RequiredResourceAccess
Description: A scheduled job or automation tool (e.g., Azure DevOps, Jenkins, or Power Automate) updates the RequiredResourceAccess property of an OAuth application as part of a CI/CD pipeline or configuration management.
Filter/Exclusion: Check for the presence of known automation tools or scheduled job names in the caller or caller_id field. Example: caller:"Azure DevOps" OR caller:"Jenkins" OR caller:"Power Automate"
Scenario: Admin Task to Update Application Permissions
Description: A system administrator manually updates the OAuth application’s required resource access through the Azure portal or via Azure CLI as part of routine permission management.
Filter/Exclusion: Filter by user accounts with administrative privileges (e.g., user_principal_name:"[email protected]") or check for known admin tasks in the operation_name or activity field.
Scenario: Third-Party SaaS Integration Configuration
Description: A third-party SaaS application (e.g., ServiceNow, Salesforce, or Microsoft Teams) updates the OAuth application’s required resource access during integration setup or configuration.
Filter/Exclusion: Use the client_app_id or resource fields to identify known third-party SaaS integrations. Example: resource:"salesforce.com" OR resource:"servicenow.com"
Scenario: Resource Access Update via Azure PowerShell
Description: An administrator uses Azure PowerShell to update the RequiredResourceAccess property of an OAuth application as part of a script or automation process.
Filter/Exclusion: Filter by the presence of Azure PowerShell commands or script names in the caller or operation_name field. Example: operation_name:"Update-AzADApplication" OR caller:"Azure PowerShell"
**Scenario: