Skip to content

Entra Directory Roles, PIM Abuse & Privilege Escalation

In enterprise cloud security, obtaining initial access to a standard employee identity is merely the preliminary phase of an intrusion. To achieve complete tenant dominion, access unrestricted eDiscovery portals, disable security controls, or execute widespread data exfiltration, adversaries actively seek Privilege Escalation to the highest administrative tiers of Microsoft Entra ID.

Privilege escalation in Microsoft Entra ID differs fundamentally from Active Directory Domain Services (AD DS). In Entra ID, there are no Kerberos delegation flaws or SAM-Account-Name spoofing vectors. Instead, cloud privilege escalation exploits indirect role-to-role relationships (such as Application Administrators resetting credentials on service principals assigned to directory roles), misconfigured role-assignable groups, and the abuse of Privileged Identity Management (PIM) activation workflows.

This guide details the Tier 0 directory role hierarchy, analyzes practical cloud privilege escalation vectors, decodes PIM activation forensics in Entra audit logs, and provides production-grade KQL hunting queries and PowerShell audit scripts.


Entra ID defines over 80 built-in directory roles. For incident response and security architecture, these roles are partitioned into a three-tiered control plane model:

graph TD
subgraph "Tier 0: Tenant Control Plane"
T0_GA[Global Administrator<br/>Unrestricted Access to all Tenant Planes]
T0_PRA[Privileged Role Administrator<br/>Manage & Assign all Directory Roles]
T0_PAA[Privileged Authentication Administrator<br/>Reset Passwords/MFA for all Admins]
T0_HIA[Hybrid Identity Administrator<br/>Control Entra Connect / Sync Pipeline]
end
subgraph "Tier 1: Workload Control Plane"
T1_EX[Exchange Administrator<br/>Tenant Mailboxes & Mail Flow]
T1_SP[SharePoint Administrator<br/>All SharePoint Sites & OneDrive]
T1_SEC[Security Administrator<br/>Defender, Purview, CA Policies]
T1_INT[Intune Administrator<br/>Endpoint Management & Wipe]
end
subgraph "Tier 2: Identity & App Operators"
T2_APP[Application Administrator<br/>Manage App Registrations & Secrets]
T2_CAPP[Cloud Application Administrator<br/>Manage Cloud Apps & Secrets]
T2_USR[User Administrator<br/>Manage Non-Admin Accounts]
T2_HELP[Helpdesk Administrator<br/>Password Resets for Standard Users]
end
T2_APP -.->|Indirect Privilege Escalation Vector| T0_GA
T2_USR -.->|Role-Assignable Group Vector| T0_PRA
T1_SEC -.->|Conditional Access Bypass| T0_GA
  • Global Administrator: Ultimate tenant authority. Bypasses all RBAC boundaries, can grant root management permissions in Azure subscriptions, and access all workloads.
  • Privileged Role Administrator: Can assign, modify, and delete any directory role assignment (both active and eligible) across all users and service principals.
  • Privileged Authentication Administrator: Can reset passwords, delete MFA credentials, and generate Temporary Access Passes (TAPs) for any identity in the tenant, including Global Administrators.

The most prevalent indirect privilege escalation path in Entra ID occurs when an attacker compromises a user or service principal possessing the Application Administrator or Cloud Application Administrator role:

sequenceDiagram
autonumber
participant Attacker as Adversary (App Admin)
participant Entra as Entra ID Directory
participant TargetSP as Pre-Existing Service Principal (Assigned Global Admin)
participant Graph as Microsoft Graph API
Note over Attacker: Attacker compromises an identity with 'Application Administrator'
Attacker->>Entra: Enumerates Enterprise Apps assigned to Directory Roles
Note over Entra: Discovers SP 'Automation-Pipeline' has active 'Global Administrator' role!
Attacker->>Entra: POST /applications/{id}/addPassword (Injects new Client Secret)
Entra-->>Attacker: Returns valid client_secret for 'Automation-Pipeline'
Attacker->>Entra: Authenticates via OAuth Client Credentials Grant
Entra-->>Attacker: Issues App Token with Global Administrator privileges!
Attacker->>Graph: Fully controls entire tenant plane via Microsoft Graph API!

Application Administrators have the authority to manage credentials (passwords, certificates) on all enterprise applications and service principals in the directory. If an organization has previously assigned a directory role (such as Global Admin, Exchange Admin, or User Admin) to a service principal, any Application Administrator can add a secret to that service principal and immediately assume its elevated privileges.

Entra ID allows security groups to be assigned directory roles (isRoleAssignable: true). To prevent privilege escalation, Entra ID enforces that only Global Administrators and Privileged Role Administrators can manage the membership of role-assignable groups.

However, escalation occurs when:

  1. Group Ownership Abuse: An administrator assigns ownership of a role-assignable group to a standard user or an unmonitored service principal. The owner can add arbitrary members, thereby granting the assigned directory role to any account.
  2. PIM for Groups: Threat actors abuse eligible group memberships within PIM to elevate privileges into role-assignable groups.

Microsoft Entra Privileged Identity Management (PIM) enforces Just-In-Time (JIT) access: users do not hold permanent administrative roles; instead, they are assigned Eligible status and must actively request role activation when needed.

graph TD
ATTACKER[Adversary Compromises User Account] --> PIM_CHECK{Is Account PIM-Eligible?}
PIM_CHECK -->|No| RECON[Normal User Access]
PIM_CHECK -->|Yes: Eligible for Global Admin| ACT_REQ[Submit Role Activation Request<br/>POST /roleAssignmentScheduleRequests]
ACT_REQ --> POL_CHECK{PIM Policy Settings?}
POL_CHECK -->|Setting: Approval Required| WAIT_APP[Awaits Admin Approval<br/>Attacker submits forged ticket INC-94812]
POL_CHECK -->|Setting: No Approval / Justification Only| INSTANT[Instant Role Activation!<br/>Status: Active for 8 Hours]
INSTANT --> FULL_TENANT[Complete Control of Microsoft 365 Tenant]
  1. Unprotected Eligible Roles: Many organizations configure PIM eligibility without requiring independent approvers, relying solely on user-provided justification strings. An attacker simply submits a bogus change ticket ID ("Emergency Hotfix - INC-88412") and gains instant Global Admin rights.
  2. Forged Approval & Co-Opted Approvers: If an approval workflow is required, attackers target the designated approver identities first, approve their own pending activation requests, and complete the escalation.
  3. PIM Policy Tampering: An adversary with Privileged Role Administrator modifies the PIM policy rules (Update role setting in PIM), extending max activation duration to 24 hours, disabling MFA requirements, or removing approval requirements.

All role assignments, PIM activations, and group role manipulations produce explicit audit events under the RoleManagement and GroupManagement categories:

Operation NameCategoryForensic Implication
Add member to roleRoleManagementPermanent Active Role Assignment. Immediate escalation to the designated directory role.
Add eligible member to roleRoleManagementPermanent PIM eligibility granted. User can activate the role at will.
Add role assignment schedule requestRoleManagementPIM Role Activation Event. A user activated their eligible role for a scheduled window.
Add member to groupGroupManagementCheck if target group has isRoleAssignable: true.
Update role setting in PIMRoleManagementTampering with PIM policies (disabling approval, extending duration).

When extracted from the Entra ID AuditLogs table, a PIM activation event contains:

{
"activityDateTime": "2026-03-24T16:45:00Z",
"activityDisplayName": "Add role assignment schedule request",
"category": "RoleManagement",
"result": "success",
"initiatedBy": {
"user": {
"userPrincipalName": "victim-admin@target.com",
"ipAddress": "198.51.100.55"
}
},
"targetResources": [
{
"displayName": "Global Administrator",
"type": "RoleDefinition",
"modifiedProperties": [
{
"displayName": "Role.DisplayName",
"newValue": "\"Global Administrator\""
},
{
"displayName": "Assignment.Type",
"newValue": "\"Active\""
},
{
"displayName": "Assignment.Duration",
"newValue": "\"PT8H\""
},
{
"displayName": "Assignment.Justification",
"newValue": "\"Emergency incident investigation - Ticket INC-89124\""
}
]
}
]
}

Key fields for investigators:

  • initiatedBy.user.ipAddress: Correlate against known corporate egress IPs to confirm if activation originated from attacker infrastructure.
  • modifiedProperties[Assignment.Justification]: Evaluate if the justification matches authentic enterprise ticketing systems.
  • modifiedProperties[Role.DisplayName]: The elevated directory role.

5.1 Detecting Permanent Directory Role Assignments (Tier 0 Escalation)

Section titled โ€œ5.1 Detecting Permanent Directory Role Assignments (Tier 0 Escalation)โ€

Flag any direct assignment of critical administrative roles bypassing PIM:

let CriticalRoles = dynamic([
"Global Administrator",
"Privileged Role Administrator",
"Privileged Authentication Administrator",
"Security Administrator",
"Exchange Administrator"
]);
AuditLogs
| where TimeGenerated >= ago(30d)
| where Category == "RoleManagement"
| where OperationName in ("Add member to role", "Add member to role completed (PIM activation)")
| extend RoleName = tostring(TargetResources[0].displayName)
| extend AssignedTo = tostring(TargetResources[2].displayName)
| extend AssignedToUPN = tostring(TargetResources[2].userPrincipalName)
| extend Initiator = tostring(InitiatedBy.user.userPrincipalName)
| extend InitiatorIP = tostring(InitiatedBy.user.ipAddress)
| where RoleName in (CriticalRoles)
| project TimeGenerated, OperationName, RoleName, AssignedTo, AssignedToUPN, Initiator, InitiatorIP
| sort by TimeGenerated desc

Identify PIM activations occurring outside business hours or from unfamiliar foreign IP addresses:

AuditLogs
| where TimeGenerated >= ago(14d)
| where Category == "RoleManagement"
| where OperationName == "Add role assignment schedule request"
| extend InitiatorUPN = tostring(InitiatedBy.user.userPrincipalName),
InitiatorIP = tostring(InitiatedBy.user.ipAddress)
| extend TargetRole = tostring(TargetResources[0].displayName)
| extend ModifiedProps = TargetResources[0].modifiedProperties
| mv-expand ModifiedProps
| where ModifiedProps.displayName == "Assignment.Justification"
| extend Justification = tostring(ModifiedProps.newValue)
| project TimeGenerated, OperationName, TargetRole, InitiatorUPN, InitiatorIP, Justification
| sort by TimeGenerated desc

5.3 Detecting Secret Injection on Service Principals Assigned to Directory Roles

Section titled โ€œ5.3 Detecting Secret Injection on Service Principals Assigned to Directory Rolesโ€

Correlate credential additions on service principals that hold directory roles:

let RoleAssignedApps = AuditLogs
| where TimeGenerated >= ago(90d)
| where OperationName == "Add member to role"
| extend TargetAppId = tostring(TargetResources[1].id)
| where isnotempty(TargetAppId)
| distinct TargetAppId;
AuditLogs
| where TimeGenerated >= ago(30d)
| where OperationName in ("Update application - Certificates and secrets management", "Add service principal credentials")
| extend AppId = tostring(TargetResources[0].id)
| where AppId in (RoleAssignedApps)
| project TimeGenerated, OperationName, TargetApp=tostring(TargetResources[0].displayName),
AppId, InitiatedBy=tostring(InitiatedBy.user.userPrincipalName)
| sort by TimeGenerated desc

Run this script to enumerate all active and eligible members of Tier 0 roles:

Terminal window
# Prerequisites: Microsoft.Graph.Identity.Governance module
# Connect-MgGraph -Scopes "RoleManagement.Read.Directory","Directory.Read.All"
Write-Host "=====================================================" -ForegroundColor Cyan
Write-Host " ENTRA ID DIRECTORY ROLES & PIM ASSIGNMENT AUDIT " -ForegroundColor Cyan
Write-Host "=====================================================" -ForegroundColor Cyan
# 1. Enumerate Active Directory Role Assignments
Write-Host "`n[*] Auditing Active Directory Role Assignments..." -ForegroundColor Yellow
$activeAssignments = Get-MgRoleManagementDirectoryRoleAssignmentScheduleInstance -ExpandProperty "RoleDefinition,Principal"
$tier0Roles = @("Global Administrator", "Privileged Role Administrator", "Privileged Authentication Administrator")
$activeReport = @()
foreach ($assign in $activeAssignments) {
if ($tier0Roles -contains $assign.RoleDefinition.DisplayName) {
$activeReport += [PSCustomObject]@{
RoleName = $assign.RoleDefinition.DisplayName
PrincipalName = $assign.Principal.AdditionalProperties.userPrincipalName
PrincipalType = $assign.Principal.AdditionalProperties["@odata.type"]
AssignmentType= $assign.AssignmentType
StartDateTime = $assign.StartDateTime
EndDateTime = $assign.EndDateTime
}
}
}
$activeReport | Format-Table -AutoSize
# 2. Enumerate Eligible PIM Assignments
Write-Host "`n[*] Auditing Eligible (PIM) Role Assignments..." -ForegroundColor Yellow
$eligibleAssignments = Get-MgRoleManagementDirectoryRoleEligibilityScheduleInstance -ExpandProperty "RoleDefinition,Principal"
$eligibleReport = @()
foreach ($elig in $eligibleAssignments) {
if ($tier0Roles -contains $elig.RoleDefinition.DisplayName) {
$eligibleReport += [PSCustomObject]@{
RoleName = $elig.RoleDefinition.DisplayName
PrincipalName = $elig.Principal.AdditionalProperties.userPrincipalName
MemberType = $elig.MemberType
StartDateTime = $elig.StartDateTime
EndDateTime = $elig.EndDateTime
}
}
}
$eligibleReport | Format-Table -AutoSize
Terminal window
# Remove an active role assignment from a compromised user
$compromisedUPN = "compromised-admin@target.com"
$roleName = "Global Administrator"
# Retrieve role and user IDs
$user = Get-MgUser -Filter "userPrincipalName eq '$compromisedUPN'"
$role = Get-MgRoleManagementDirectoryRoleDefinition -Filter "displayName eq '$roleName'"
# Find active assignment instance
$assignment = Get-MgRoleManagementDirectoryRoleAssignment -Filter "principalId eq '$($user.Id)' and roleDefinitionId eq '$($role.Id)'"
if ($assignment) {
Remove-MgRoleManagementDirectoryRoleAssignment -UnifiedRoleAssignmentId $assignment.Id
Write-Host "[+] Revoked '$roleName' assignment from $compromisedUPN" -ForegroundColor Green
}