Adversaries may bypass Privileged Identity Management controls by directly assigning privileged directory roles, circumventing necessary approval and justification processes. SOC teams should proactively hunt for this behavior to detect potential unauthorized elevation of privileges and mitigate insider threats in Azure Sentinel.
KQL Query
let timeframe = 14d;
let PrivilegedRoles = dynamic([
"Global Administrator",
"Privileged Role Administrator",
"Application Administrator",
"Cloud Application Administrator",
"Exchange Administrator",
"SharePoint Administrator",
"User Account Administrator",
"Authentication Administrator",
"Privileged Authentication Administrator",
"Security Administrator",
"Hybrid Identity Administrator"
]);
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where Category =~ "RoleManagement"
| where OperationName =~ "Add member to role."
| where Result =~ "success"
| 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))
| extend TargetUpn = tostring(TargetResources[0].userPrincipalName)
| extend TargetId = tostring(TargetResources[0].id)
| mv-expand ModProp = TargetResources[0].modifiedProperties
| where tostring(ModProp.displayName) =~ "Role.DisplayName"
| extend RoleName = trim('"', tostring(ModProp.newValue))
| where RoleName in~ (PrivilegedRoles)
| extend ActorName = iff(ActorUpn has "@", tostring(split(ActorUpn, "@")[0]), Actor)
| extend ActorUPNSuffix = iff(ActorUpn has "@", tostring(split(ActorUpn, "@")[1]), "")
| extend AccountName = iff(TargetUpn has "@", tostring(split(TargetUpn, "@")[0]), TargetUpn)
| extend AccountUPNSuffix = iff(TargetUpn has "@", tostring(split(TargetUpn, "@")[1]), "")
| project TimeGenerated, Actor, ActorName, ActorUPNSuffix, ActorIp,
TargetUpn, AccountName, AccountUPNSuffix, TargetId, RoleName, CorrelationId
| sort by TimeGenerated desc
id: 2df6ff4f-f90f-4158-ac4a-98c1b23d9e18
name: Privileged directory role assigned outside PIM workflow
description: |
Identifies permanent directory role assignments to privileged roles made outside
the Privileged Identity Management activation workflow. Direct assignments bypass
PIM approval and justification requirements.
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- Persistence
- PrivilegeEscalation
relevantTechniques:
- T1098.003
query: |
let timeframe = 14d;
let PrivilegedRoles = dynamic([
"Global Administrator",
"Privileged Role Administrator",
"Application Administrator",
"Cloud Application Administrator",
"Exchange Administrator",
"SharePoint Administrator",
"User Account Administrator",
"Authentication Administrator",
"Privileged Authentication Administrator",
"Security Administrator",
"Hybrid Identity Administrator"
]);
AuditLogs
| where TimeGenerated >= ago(timeframe)
| where Category =~ "RoleManagement"
| where OperationName =~ "Add member to role."
| where Result =~ "success"
| 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))
| extend TargetUpn = tostring(TargetResources[0].userPrincipalName)
| extend TargetId = tostring(TargetResources[0].id)
| mv-expand ModProp = TargetResources[0].modifiedProperties
| where tostring(ModProp.displayName) =~ "Role.DisplayName"
| extend RoleName = trim('"', tostring(ModProp.newValue))
| where RoleName in~ (PrivilegedRoles)
| extend ActorName = iff(ActorUpn has "@", tostring(split(ActorUpn, "@")[0]), Actor)
| extend ActorUPNSuffix = iff(ActorUpn has "@", tostring(split(ActorUpn, "@")[1]), "")
| extend AccountName = iff(TargetUpn has "@", tostring(split(TargetUpn, "@")[0]), TargetUpn)
| extend AccountUPNSuffix = iff(TargetUpn has "@", tostring(split(TargetUpn, "@")[1]), "")
| project TimeGenerated, Actor, ActorName, ActorUPNSuffix, ActorIp,
TargetUpn, AccountName, AccountUPNSuffix, TargetId, RoleName, CorrelationId
| sort by TimeGenerated desc
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: Actor
- identifier: Name
columnName: ActorName
- identifier: UPNSuffix
columnName: ActorUPNSuffix
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: TargetUpn
- identifier: Name
columnName: AccountName
- identifier: UPNSuffix
columnName: AccountUPNSuffix
- entityType: IP
fieldMappings:
- identifier: Address
columnName: ActorIp
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
Scenario: A system administrator manually assigns a privileged directory role (e.g., Global Administrator) to a user via the Azure Portal for emergency access.
Filter/Exclusion: Exclude assignments made via the Azure Portal where the user has the “User Account Administrator” role or is part of the “Global Administrators” group.
Scenario: A scheduled job (e.g., Microsoft Purview data classification job) temporarily assigns a privileged role to a service account to perform a specific task.
Filter/Exclusion: Exclude assignments made by scheduled jobs running under service accounts with the “Service Principal” or “Managed Identity” tag.
Scenario: An IT support tool (e.g., Microsoft Endpoint Manager) automatically assigns a privileged role to a user during a bulk user provisioning task.
Filter/Exclusion: Exclude assignments made by tools with known automation tags (e.g., “Microsoft Endpoint Manager”, “Azure AD Connect”, or “Azure AD Identity Protection”) during scheduled provisioning.
Scenario: A third-party service (e.g., Okta) synchronizes user roles and inadvertently assigns a privileged directory role to a user during a sync cycle.
Filter/Exclusion: Exclude assignments made by external identity providers (e.g., Okta, Ping Identity) where the source system is flagged as a “third-party SaaS” or “external identity provider”.
Scenario: A security team member uses the Azure AD PowerShell module to assign a privileged role to a user for a temporary investigation.
Filter/Exclusion: Exclude assignments made via PowerShell scripts where the script is flagged as “security team tool” or the user is part of the “Security Operations” group.