azure-msgraph-rolemanagement-write
A service principal holding RoleManagement.ReadWrite.Directory can assign any Entra directory role (including Global Administrator) to any principal, achieving tenant takeover - the app-permission equivalent of holding Privileged Role Administrator.
match
A conjunctive graph pattern. Variables (?x) bind node ids; every clause must hold.
where
node_type(?sp) in [
MachineIdentity, ApplicationIdentity] # service principal (enterprise app)
?approle is the Graph app role RoleManagement.ReadWrite.Directory (id: d01b97e9-cbc0-49fe-810a-750afd5527a3) on microsoft.graph resource SP (id: 00000003-0000-0000-c000-000000000000)
the appRoleAssignment is active (not pending revocation, admin consent granted)
emit
| source type | Identity |
|---|---|
| target type | Identity |
| source | ?sp |
| target | <any Identity principal in the Entra tenant> |
| permissions | RoleManagement.ReadWrite.Directory |
| conditions | iam_permission service_state |
| state logic | ACTIVE when the appRoleAssignment for RoleManagement.ReadWrite.Directory exists and is in state Granted (admin consent). CONDITIONAL(service_state) if the SP's credential has expired or been revoked (SP cannot call Graph without a valid credential). BLOCKED if a Conditional Access policy explicitly blocks the SP from calling the Graph roleManagement endpoint (network location, device compliance, or app restriction CA policy targeting the Graph resource). Never BLOCKED by ARM RBAC deny assignments (Graph authorization is Entra-only). Inherit weakest with the HasRole input. |
Narrative
{sp.name} holds the MS Graph application permission RoleManagement.ReadWrite.Directory and can therefore create directory role assignments via POST /roleManagement/directory/roleAssignments, including granting itself or any controlled principal the Global Administrator role - equivalent to full Entra tenant takeover without ARM RBAC or human interaction.
Raw rule rules/derived/azure/msgraph.yaml
id: azure-msgraph-rolemanagement-write
emits: CanGrantPermission
description: "A service principal holding RoleManagement.ReadWrite.Directory can assign any Entra directory\
\ role (including Global Administrator) to any principal, achieving tenant takeover \u2014 the app-permission\
\ equivalent of holding Privileged Role Administrator."
match:
- - sp: null
- HasRole
- approle: null
where:
- 'node_type(?sp) in [MachineIdentity, ApplicationIdentity] # service principal (enterprise app)'
- '?approle is the Graph app role RoleManagement.ReadWrite.Directory (id: d01b97e9-cbc0-49fe-810a-750afd5527a3)
on microsoft.graph resource SP (id: 00000003-0000-0000-c000-000000000000)'
- the appRoleAssignment is active (not pending revocation, admin consent granted)
emit:
source_type: Identity
target_type: Identity
source: ?sp
target: <any Identity principal in the Entra tenant>
permissions:
- RoleManagement.ReadWrite.Directory
conditions:
- iam_permission
- service_state
state_logic: ACTIVE when the appRoleAssignment for RoleManagement.ReadWrite.Directory exists and is
in state Granted (admin consent). CONDITIONAL(service_state) if the SP's credential has expired or
been revoked (SP cannot call Graph without a valid credential). BLOCKED if a Conditional Access policy
explicitly blocks the SP from calling the Graph roleManagement endpoint (network location, device
compliance, or app restriction CA policy targeting the Graph resource). Never BLOCKED by ARM RBAC
deny assignments (Graph authorization is Entra-only). Inherit weakest with the HasRole input.
confidence: '0.97'
derived_from:
- <HasRole edge_id (appRoleAssignment RoleManagement.ReadWrite.Directory on ?sp)>
- azure/identity-escalation.yaml#azure-directory-role-assign (directory-role parallel; this rule covers
the app-permission path)
- "azure/identity-escalation.yaml#azure-graph-app-role-grant (AppRoleAssignment.ReadWrite.All is orthogonal;\
\ do not dedupe \u2014 both are independent paths to CanGrantPermission)"
false_positive_note: "Key only on the appRoleAssignment row \u2014 an app that DECLARED RoleManagement.ReadWrite.Directory\
\ in requiredResourceAccess but has NOT been admin-consented does NOT hold this permission; no appRoleAssignment\
\ row will exist in tenant data. Also requires the SP to have a valid credential (secret/cert/fedcred)\
\ to call Graph \u2014 a credential-less SP cannot invoke the API. Conditional Access \"Workload Identity\"\
\ policies can block the SP from the Graph roleManagement endpoint \u2014 model as BLOCKED(CA policy).\
\ In tenants with Entra ID P2 PIM, *eligible* assignments require PIM activation flows (roleEligibilityScheduleRequests),\
\ but *direct active* assignments via POST /roleManagement/directory/roleAssignments remain possible\
\ for RoleManagement.ReadWrite.Directory holders UNLESS the tenant has explicitly configured the role\
\ to require PIM-only activation via Entra ID Governance role settings. Flag as CONDITIONAL(pim_activation)\
\ only when the specific role definition has PIM-only enforcement confirmed, not merely because P2\
\ licensing is present. The tenant must have Entra ID licensing that enables directory role assignment\
\ via Graph (all M365/Entra paid tiers do). RoleManagement.ReadWrite.Directory and AppRoleAssignment.ReadWrite.All\
\ are NOT supersets of each other \u2014 they control different Graph endpoints (/roleManagement/directory\
\ vs /appRoleAssignments) and both lead to CanGrantPermission independently; do NOT suppress this\
\ rule when AppRoleAssignment.ReadWrite.All is held."
narrative: "{sp.name} holds the MS Graph application permission RoleManagement.ReadWrite.Directory and\
\ can therefore create directory role assignments via POST /roleManagement/directory/roleAssignments,\
\ including granting itself or any controlled principal the Global Administrator role \u2014 equivalent\
\ to full Entra tenant takeover without ARM RBAC or human interaction."