When Microsoft revamped the privileged access model in the late fall of 2020, it was received with mixed results. To some, it felt as if it was overcomplicating the simple three-tier model that had been the gold standard for protecting Active Directory and other critical business assets for about a decade.
However, the shift was necessary, as it was acknowledging that business critical assets are not just the identity provider. The revamp changed how we look at things, with the proliferation of virtualization and cloud providers, and pointing out the management and control plane as critical privileged points of access.

It’s quite a common pattern to extend Active Directory to Azure as the initial standup of infrastructure out there, to support all the other “things” dependent on it.
But as it goes with the cloud, there are new vectors to be on the watch for, and in this case, it’s ensuring that your RBAC permissions over Tier 0 assets in Azure are properly defined.
Even with a lot of strong guidance from Microsoft on the Cloud Adoption Framework (CAF) and Landing Zones, as well as adhereing to the Well Architected Framework, it’s not always clearly defined as to how the Enterprise access model ties into things.

In the model depicted above, we see that Active Directory is living in its own subscription, but even when we drill into the details of that piece of the puzzle, there isn’t direct guidance as far as what the RBAC model should look like over that subscription, beyond a bunch of circular references to products or how to achieve certain compliance levels. And while all this stuff is good, this is an area where sometimes it needs to be clearly spelled out.
When permissions go wrong
When we examine the roles assigned to that identity subscription, it’s important to realize that if you have certain levels of unassuming access over Domain Controller VMs it effectively provides means to gaining privileged access in Active Directory.
If this isn’t news to you, that’s good. If you are still unsure what this means, or want a refresher, read on.
Many organizations, especially those that have been in Azure a good long while before all the WAF and CAF, can range anywhere from 30 Global Administrators that are also Owners on all subscriptions (witnessed firsthand) to a NOC that has Virtual Machine Contributor to accounts separated and dedicated to the management of the Domain Controller VM’s… and everything in between.
If you’re somewhere between too many Global Admins and to delegating out the Virtual Machine Contributor role, that is where you are leaving yourself exposed.
Let’s look at an example here with Isaiah Langer and the rights he has on the Domain Controller nwind-dc1:

Virtual Machine Contributor currently has 327 actions that compose the role, and a description
Lets you manage the virtual machines, but not access to them, and not the virtual network or storage account they’re connected to
On the surface that sounds fine – we can’t change the network configuration of the VM, and if we attempt to export the disk we are met with an error:

However, Isaiah does have the ability to use the Run command operation.
Run Command leverages the VM agent installed on the machine when it’s created; this requires no additional extension deployed. The VM agent runs in the SYSTEM context. And this all makes sense, needing certain privileges on the VM to be able to manage it.
Before looking at what we can do with this Run command, we’ll just look at a Domain Admin in AD. Note PasswordLastSet:

Back to Isaiah in the run command portal, let’s try something simple:
net user da-eric P0wned1234! /domain

And we hit Run.
Twenty seconds or so later:

Back on the Domain Controller:

And we now successfully have changed the password for the Domain Admin da-eric. Considering how easy it is to glean this sort of knowledge from Active Directory, it’s trivial to figure out who we want to target.
Of course, if we would rather not disturb an existing Domain Admin, we can just create a new one:
net user da-isaiah P0wned1234! /add /logonpasswordchg:no /domain
And then add the user to the Administrator group:
net localgroup Administrators da-isaiah /add
Back on the Domain Controller:

These are just some examples. While a threat actor may not be so obvious, it’s also important to point out that once a VM contributor account is breached, the attack itself does not need to be overengineered or complicated. Whatever you can do within the SYSTEM context, you can pull off through Azure Run command with these rights.
Tracking run command
If we look at the Azure activity logs, the Run command logs do not provide any insight into the command that ran. Look at the log below, from the last command Isaiah ran:
{
"authorization": {
"action": "Microsoft.Compute/virtualMachines/runCommand/action",
"scope": "/subscriptions/6010f303-6259-4f47-bb99-9d461d36c1e4/resourceGroups/rg-lab/providers/Microsoft.Compute/virtualMachines/nwind-dc1"
},
"caller": "isaiah.langer@fabrikam.cloud",
"channels": "Operation",
"claims": {
"aud": "https://management.core.windows.net/",
"iss": "https://sts.windows.net/11ae06df-10e8-4b9e-bf66-2a91f4955339/",
"iat": "1665576850",
"nbf": "1665576850",
"exp": "1665581298",
"http://schemas.microsoft.com/claims/authnclassreference": "1",
"aio": "ATQAy/8TAAAA2Vzb2bVgvWc1B1/qZsCPq9nblD/9RdvCXf0Y87EX8swm9HGyVQXobsMvpWY7uuBp",
"http://schemas.microsoft.com/claims/authnmethodsreferences": "pwd",
"appid": "c44b4083-3bb0-49c1-b47d-974e53cbdf3c",
"appidacr": "2",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname": "Langer",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname": "Isaiah",
"groups": "310c96cf-5606-4d8d-ae59-cb698bc9fa5b,00242a6d-86d2-4351-9303-9bcf1bfb077a,c336cee6-a292-46a8-abb2-5faeb16d66d0,b04f480e-9056-45f4-be59-0ef1e2136fac,b492badd-d952-40fc-b1e8-32279fc8635e,dc61c347-aca1-4f92-bbf4-1389af8ea099,3e56b395-18bc-4305-9f6a-ac67612c912b",
"ipaddr": "",
"name": "Isaiah Langer",
"http://schemas.microsoft.com/identity/claims/objectidentifier": "5d2a4414-5fac-46ec-85d0-4ad537d75c77",
"onprem_sid": "S-1-5-21-131657314-4052732218-1597742569-2110",
"puid": "10032001710A0360",
"rh": "0.AVAA3wauEegQnku_ZiqR9JVTOUZIf3kAutdPukPawfj2MBNQAA0.",
"http://schemas.microsoft.com/identity/claims/scope": "user_impersonation",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier": "hf0X9izWHDY50kvXgOssXWjwzaMxLB9hLuSHj1aXPsA",
"http://schemas.microsoft.com/identity/claims/tenantid": "11ae06df-10e8-4b9e-bf66-2a91f4955339",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name": "isaiah.langer@fabrikam.cloud",
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/upn": "isaiah.langer@fabrikam.cloud",
"uti": "gE43gRPnTU2aC5p7UGEwAA",
"ver": "1.0",
"xms_tcdt": "1629656520"
},
"correlationId": "cdd385b0-5df8-44f1-8d63-2036772306ba",
"description": "",
"eventDataId": "42f9771e-342b-4278-b78a-ca77184504ee",
"eventName": {
"value": "EndRequest",
"localizedValue": "End request"
},
"category": {
"value": "Administrative",
"localizedValue": "Administrative"
},
"eventTimestamp": "2022-10-12T13:00:11.66499Z",
"id": "/subscriptions/6010f303-6259-4f47-bb99-9d461d36c1e4/resourceGroups/rg-lab/providers/Microsoft.Compute/virtualMachines/nwind-dc1/events/42f9771e-342b-4278-b78a-ca77184504ee/ticks/638011764116649900",
"level": "Informational",
"operationId": "23192952-b8ef-4671-813e-36262ee112c3",
"operationName": {
"value": "Microsoft.Compute/virtualMachines/runCommand/action",
"localizedValue": "Run Command on Virtual Machine"
},
"resourceGroupName": "rg-lab",
"resourceProviderName": {
"value": "Microsoft.Compute",
"localizedValue": "Microsoft.Compute"
},
"resourceType": {
"value": "Microsoft.Compute/virtualMachines",
"localizedValue": "Microsoft.Compute/virtualMachines"
},
"resourceId": "/subscriptions/6010f303-6259-4f47-bb99-9d461d36c1e4/resourceGroups/rg-lab/providers/Microsoft.Compute/virtualMachines/nwind-dc1",
"status": {
"value": "Succeeded",
"localizedValue": "Succeeded"
},
"subStatus": {
"value": "",
"localizedValue": ""
},
"submissionTimestamp": "2022-10-12T13:01:24.1576475Z",
"subscriptionId": "6010f303-6259-4f47-bb99-9d461d36c1e4",
"tenantId": "11ae06df-10e8-4b9e-bf66-2a91f4955339",
"properties": {
"eventCategory": "Administrative",
"entity": "/subscriptions/6010f303-6259-4f47-bb99-9d461d36c1e4/resourceGroups/rg-lab/providers/Microsoft.Compute/virtualMachines/nwind-dc1",
"message": "Microsoft.Compute/virtualMachines/runCommand/action",
"hierarchy": "11ae06df-10e8-4b9e-bf66-2a91f4955339/6010f303-6259-4f47-bb99-9d461d36c1e4"
},
"relatedEvents": []
}
Note the absence of the command itself. Likewise, the VM agent has local logging, C:\WindowsAzure\Logs\Plugins\Microsoft.CPlat.Core.RunCommandWindows, but the logs themselves will not contain a copy of the command run.
It’s not just the portal
While the examples shown here are using the Azure portal, you can perform the same commands using Azure CLI, the Azure PowerShell module Invoke-AzVMRunCommand, and the Azure Management API https://management.azure.com
An example from a run using Azure CLI:
isaiah@Azure:~$ az vm run-command invoke --command-id RuNPowerShellScript --name nwind-dc1 -g "rg-lab" --scripts "net user da-eric P0wned1234! /domain"
{
"value": [
{
"code": "ComponentStatus/StdOut/succeeded",
"displayStatus": "Provisioning succeeded",
"level": "Info",
"message": "The command completed successfully.\n",
"time": null
},
{
"code": "ComponentStatus/StdErr/succeeded",
"displayStatus": "Provisioning succeeded",
"level": "Info",
"message": "",
"time": null
}
]
}
isaiah@Azure:~$
Mitigation, monitoring and recommendations
The easiest course of action is to review who has ownership and of the subscription(s) containing Domain Controllers, as well as Virtual Machine Contributor. If those assigned are different from the staff that manages identity in your organization, investigate if the permissions can be removed/reduced. And if your Domain Controllers do not reside in their own subscription, now is the time to start planning to move them.
Some organizations may still have standard processes to allow users who do not manage Active Directory the ability to do things like start/stop Domain Controllers, create a custom RBAC role. You can copy the Virtual Machine Contributor Role, and then use the Exclude Permissions feature to restrict the ability to use Run commands:

Exclude permissions is a great way to restrict certain permissions within a wildcard permission, so you don’t need to manually define all the VirtualMachine permissions permitted.
After building the custom role, assign it to whomever currently has Virtual Machine Contributor over Domain Controllers. Once you do, what you’ll find is that the user will still have the visibility of Run command in the portal, but Azure will blackhole the process if a user attempts to use it.
For management of critical Tier 0 VM’s like Domain Controllers, PIM should be used for role elevation, even if it’s to a role that has the Run command permissions removed. Remember, you can mix and match licenses – go buy some AAD P2 licenses for your organization to cover management of Tier 0 assets if P2 across the organization is not something on the financial forecast.
Organizationally some may accept the risk here, which you shouldn’t, as Mandiant indicates they have seen this actively exploited in incident response cases.
Last thing, whether you mitigate or don’t, a takeaway here is why it’s critical to have robust monitoring of critical groups and users in Active Directory, and even better have an Identity Threat Detection and Response (ITDR) platform in place.
About This Posts Featured Image
The chosen photo is the work of Dima Pechurin, used under the Unsplash license.