Azure role assignment abuse and elevateAccess, investigated
How attackers escalate with Azure role assignments and elevateAccess, what roleAssignments/write events look like in the Activity Log, and how to triage them.
TL;DR. Privilege escalation in Azure is mostly one operation: Microsoft.Authorization/roleAssignments/write. What makes it suspicious is the role (Owner, User Access Administrator, Role Based Access Control Administrator, or Contributor granted to a non-human identity), the caller (a service principal, or a principal assigning a role to itself) and the scope (subscription or root). The nuclear version is Microsoft.Authorization/elevateAccess/action: a Global Administrator gives themselves User Access Administrator at root scope /, over every subscription. That event is written to the tenant-level log, so a subscription export will not show it.
Role assignments are the backbone of Azure authorization, and therefore the first thing an intruder with some access tries to extend. They are also routine administration, which is why a raw list of roleAssignments/write events is useless. This article is about telling the two apart.
How Azure RBAC escalation works
A role assignment binds three things: a principal, a role definition and a scope. Anyone holding Microsoft.Authorization/roleAssignments/write at a scope can create assignments at that scope. In the built-in roles, that means Owner, User Access Administrator and Role Based Access Control Administrator.
Typical escalation paths I see:
| Starting point | Escalation | Why it works |
|---|---|---|
| Leaked secret of a CI/CD service principal that was given User Access Administrator "to deploy role assignments" | Grants itself Contributor or Owner on the subscription | Over-privileged automation |
| Compromised Global Administrator in Entra ID | elevateAccess → User Access Administrator at / → Owner on any subscription | Entra and Azure RBAC are separate, but Global Administrators can bridge them |
| Contributor on a Key Vault using access policies | Adds itself to the vault's access policy | Not RBAC, but the same idea — see the Key Vault article |
| Owner of an Automation account or VM with a privileged managed identity | Runs code as that identity | Covered in managed identity abuse |
MITRE ATT&CK tracks the grant as T1098.003 Additional Cloud Roles and the use of legitimate cloud identities as T1078.004 Cloud Accounts.
What elevateAccess is, and where it is logged
Elevate access is a documented, legitimate feature: a Global Administrator in Microsoft Entra ID can flip "Access management for Azure resources" and receive the User Access Administrator role at root scope /. From there they can assign themselves any role in any subscription and management group of the tenant. Microsoft recommends removing that access once the task is done (Elevate access to manage all Azure subscriptions).
The forensic trap is where it is logged. Per the same page, elevate-access entries appear in the Microsoft Entra directory audit logs (service "Azure RBAC (Elevated Access)", in preview at the time of writing) and in the Directory Activity view of the Activity Log — the tenant-level log. The operation is Microsoft.Authorization/elevateAccess/action with scope /providers/Microsoft.Authorization. A per-subscription az monitor activity-log list does not return it. Export the tenant level with az rest as shown in the export guide.
This is not an exotic path. Microsoft's write-up of Storm-0501 describes the actor using elevateAccess/action to obtain User Access Administrator and then roleAssignments/write to take Owner across subscriptions.
Reading a roleAssignments/write event
In the REST / CLI schema, the details you need are split between the event and its request body:
| Field | Where | Why it matters |
|---|---|---|
caller and claims | Event | Who created the assignment. A GUID caller with an appid claim is a service principal; an xms_mirid claim means a managed identity |
httpRequest.clientIpAddress | Event | Where the call came from |
properties.requestbody → PrincipalId, RoleDefinitionId, Scope | Request body | Who received which role, where |
authorization.evidence.role | Resource-log schema | Which role allowed the caller to do it |
status.value | Event | Started / Succeeded / Failed — failures are interesting too |
RoleDefinitionId is a GUID. The built-in role IDs are fixed and documented in Azure built-in roles; for example Owner is 8e3af657-a8ff-443c-a75c-2fe8c4bcb635, Contributor b24988ac-6180-42a0-ab88-20f7382dd24c and User Access Administrator 18d7d88d-d35e-4fb5-a5c3-7773c20a72d9.
Two cheap but strong signals:
- The caller assigns a role to itself. Compare the caller's object ID with
PrincipalIdin the request body. Legitimate bootstrap scripts do this occasionally; attackers do it constantly. - The assignee is non-human and the role is broad. A service principal or managed identity receiving Owner or Contributor on a whole subscription deserves an owner and a ticket number.
What the analyzer flags
The Azure Forensics analyzer implements four RBAC rules, all on the Activity Log:
| Rule | Detects | Level |
|---|---|---|
AZ-RBAC-001 | Owner, User Access Administrator or Role Based Access Control Administrator assigned | Medium |
AZ-RBAC-002 | Service principal or managed identity assigned Owner, Contributor or an access-management role | High |
AZ-RBAC-003 | Principal granted a role to itself (caller object ID = assignee) | High |
AZ-RBAC-004 | elevateAccess/action — Global Administrator elevated to root scope | High |
Pair them with AZ-ID-001 (service principal used from a new IP) and the chain usually reads itself: new IP → self-assignment → whatever came next.
Triage checklist
For every flagged assignment:
- Is there a change ticket? Ask the platform team before assuming anything; many "suspicious" assignments are Friday-evening fixes.
- Who is the caller, and is this their normal behaviour? Pivot on the caller in the Entities tab. A deployment SP that has never assigned roles before is a strong signal.
- From which IP? Compare with the caller's baseline IPs. CI runners change IP; attackers change IP and timing.
- What happened next with the new rights? Look at the next hour of events by the assignee. A role nobody uses is a mistake; a role used within minutes to run commands or read secrets is an attack.
- Was it removed? Attackers sometimes clean up with
roleAssignments/delete. The deletion is also logged. - For
elevateAccess: which Global Administrator, from which sign-in? That is an Entra ID question for m365forensics.com.
Remediation
- Remove assignments created during the incident (
az role assignment delete), and review who holds Owner and User Access Administrator at every scope. - Remove the User Access Administrator assignment at
/created by elevate access; Microsoft documents both the portal toggle and the CLI / REST removal. - Rotate the credentials of any service principal that made or received a suspicious assignment.
- Reduce standing privilege: no User Access Administrator on CI/CD identities; consider Privileged Identity Management for humans.