Workload Identity - SP Self-Privilege Escalation via App Role Assignment
29 Service Principal Self Privilege Escalation
Query
// High-value Microsoft Graph app roles / Entra ID directory roles that constitute escalation.
let DangerousAppRoles = dynamic([
"RoleManagement.ReadWrite.Directory",
"Application.ReadWrite.All",
"AppRoleAssignment.ReadWrite.All",
"Directory.ReadWrite.All",
"User.ReadWrite.All",
"Group.ReadWrite.All",
"Mail.ReadWrite",
"MailboxSettings.ReadWrite",
"full_access_as_app",
"Exchange.ManageAsApp",
"Global Administrator",
"Application Administrator",
"Cloud Application Administrator",
"Privileged Role Administrator",
"Privileged Authentication Administrator"
]);
AuditLogs
| where TimeGenerated > ago(1h)
// Only app-role / permission-grant operations (directory-role & consent ops removed on purpose).
| where OperationName in (
"Add app role assignment to service principal",
"Add delegated permission grant",
"Add app role assignment grant"
)
| where Result =~ "success"
| extend InitiatorSPId = tostring(InitiatedBy.app.servicePrincipalId)
| extend InitiatorSPName = tostring(InitiatedBy.app.displayName)
| extend InitiatorAppId = tostring(InitiatedBy.app.appId)
| extend InitiatorUserId = tostring(InitiatedBy.user.id)
| extend InitiatorUPN = tostring(InitiatedBy.user.userPrincipalName)
// Must be SP-initiated AND have no human behind it -> excludes PIM / admin / user-driven actions.
| where isnotempty(InitiatorSPId)
| where isempty(InitiatorUserId) and isempty(InitiatorUPN)
// Defense in depth: drop Microsoft first-party governance brokers (MS-PIM "initiates" PIM changes).
| where InitiatorAppId != "01fc33a7-78ba-4d2f-a4b7-768e336e890e"
| where InitiatorSPName !has "MS-PIM" and InitiatorSPName !has "Privileged Identity Management"
| extend TargetObjectId = tostring(TargetResources[0].id)
| extend TargetObjectName = tostring(TargetResources[0].displayName)
| extend TargetType = tostring(TargetResources[0].type)
// Resolve the actual permission / role granted from modifiedProperties.
| mv-expand ModProp = TargetResources[0].modifiedProperties
| where tostring(ModProp.displayName) in (
"AppRole.Value",
"DelegatedPermissionGrant.Scope",
"ServicePrincipal.OAuth2PermissionGrants"
)
| extend GrantedPermission = tostring(ModProp.newValue)
| extend IsSelfGrant = InitiatorSPId == TargetObjectId
| extend IsDangerousPerm = GrantedPermission has_any (DangerousAppRoles)
// Self-grant (any permission) OR a dangerous permission granted to ANOTHER service principal.
| where IsSelfGrant or (IsDangerousPerm and TargetType =~ "ServicePrincipal")
| summarize
EscalationCount = count(),
GrantedPermissions = make_set(GrantedPermission, 20),
TargetObjects = make_set(TargetObjectName, 10),
TargetObjectIds = make_set(TargetObjectId, 10),
Operations = make_set(OperationName, 5),
SelfGrantCount = countif(IsSelfGrant),
DangerousPermCount = countif(IsDangerousPerm),
FirstEscalation = min(TimeGenerated),
LastEscalation = max(TimeGenerated)
by InitiatorSPName, InitiatorSPId
// Corroborate: was this SP recently created or had credentials added in the last 7 days?
| join kind=leftouter (
AuditLogs
| where TimeGenerated > ago(7d)
| where OperationName in (
"Add service principal",
"Add service principal credentials",
"Update service principal"
)
| where Result =~ "success"
| extend SPId = tostring(TargetResources[0].id)
| summarize RecentOps = make_set(OperationName, 5) by SPId
| extend IsNewOrModified = true
) on $left.InitiatorSPId == $right.SPId
| extend IsNewOrModified = coalesce(IsNewOrModified, false)
| extend SeverityLevel = case(
SelfGrantCount > 0 and DangerousPermCount > 0, "Critical - SP granted itself a dangerous permission",
SelfGrantCount > 0, "High - SP self-grant (any permission)",
DangerousPermCount > 0 and IsNewOrModified, "Critical - New/modified SP granting dangerous permissions",
DangerousPermCount > 0, "High - SP granting dangerous permissions to another SP",
"Medium")
| project
InitiatorSPName, InitiatorSPId,
EscalationCount, SelfGrantCount, DangerousPermCount,
GrantedPermissions, TargetObjects, TargetObjectIds,
Operations, RecentOps, IsNewOrModified,
SeverityLevel, FirstEscalation, LastEscalation
| order by LastEscalation descExplanation
This query is designed to detect when a service principal (a type of identity used by applications or services to access resources) in Azure elevates its own privileges or those of another service principal it controls. This is done by assigning high-value app roles or permissions to itself, which can be a stealthy way to gain unauthorized access or control, bypassing normal security checks.
Here's a simplified breakdown of what the query does:
-
Purpose: It identifies service principals that are elevating their privileges by assigning themselves or other service principals high-value roles or permissions.
-
Scope: The query specifically looks for actions initiated by service principals without any human user involvement. It excludes actions driven by users, such as those through Privileged Identity Management (PIM).
-
Detection: It checks for successful operations where app roles or permissions are added, focusing on high-value roles like "Directory.ReadWrite.All" or "Global Administrator".
-
Conditions: The query ensures that the action is initiated by a service principal, not a user, and excludes known governance services like MS-PIM.
-
Analysis: It examines if the service principal has recently been created or modified, which could indicate suspicious activity.
-
Severity Levels: It categorizes the severity of the detected actions, marking them as "Critical" or "High" based on whether the service principal granted itself or another service principal dangerous permissions.
-
Output: The results include details about the service principal, the number of escalations, the permissions granted, and the severity level of the detected activity.
-
Alerting: If such an activity is detected, an alert is created with details about the service principal and the nature of the escalation, helping security teams to verify if the activity is authorized.
Overall, this query helps in identifying potential security risks where service principals might be used to gain unauthorized access or control within an Azure environment.
Details

David Alonso
Released: June 18, 2026
Tables
Keywords
Operators
Severity
HighTactics
Frequency: 1h
Period: 7d