Azure VM RunCommand allows any identity with Virtual Machine Contributor or higher RBAC permissions to execute arbitrary scripts on a VM through the Azure management plane — bypassing NSGs, firewalls, and RDP/SSH restrictions entirely. Attackers who compromise an Azure subscription or resource group can pivot directly to any VM without touching the network.
RunCommand operations are logged in AzureActivity under the operation Microsoft.Compute/virtualMachines/runCommand/action. Any RunCommand executed by an identity that is not an approved automation account or DevOps pipeline requires immediate investigation.
☰ Table of Contents
01 — BACKGROUND
TECHNIQUE OVERVIEWRunCommand uses the Azure VM Guest Agent (running on every Azure VM) as a side-channel for command execution. Scripts execute as SYSTEM (Windows) or root (Linux) without requiring network connectivity to the VM. This makes it a powerful lateral movement and persistence vector — an attacker with subscription-level Contributor can use it against all VMs in the subscription regardless of network isolation. MITRE ATT&CK T1651 (Cloud Administration Command) specifically covers this technique.
| Field | Value |
|---|---|
| MITRE Technique | T1059 — Command and Scripting Interpreter, T1651 — Cloud Administration Command |
| Log Source | AzureActivity (OperationName contains "runCommand") |
| Operation Names | Microsoft.Compute/virtualMachines/runCommand/action, Microsoft.Compute/virtualMachines/extensions/write |
| Execution Context | SYSTEM (Windows) / root (Linux) via Azure VM Guest Agent |
| Network Bypass | YES — management plane bypasses all NSG/firewall rules |
| Detection Priority | HIGH — any RunCommand from non-approved identity is suspicious |
02 — IDENTIFICATION
2 INDICATORSAzureActivity logs every RunCommand invocation including the caller identity and target VM. The script content itself is passed as a request body and is not logged in AzureActivity by default, but the operation type, caller, and VM name are available for immediate triage. Correlate against a known-good allowlist of automation accounts (Ansible, Azure DevOps, Azure Automation) that legitimately use RunCommand.
- 1Navigate to Monitor → Activity log. Set filter: Operation: Microsoft.Compute/virtualMachines/runCommand/action and time range Last 24 hours.
- 2Review each entry: Initiated by (identity), Resource (VM name), Status (Succeeded/Failed). Failed RunCommands may indicate the attacker probing VMs where the Guest Agent is not running.
- 3Compare each caller against the approved automation account list maintained by your cloud team. Any human user account invoking RunCommand is inherently suspicious unless responding to a declared incident.
- 4Run the KQL below to list all RunCommand and custom extension write operations in the last 24 hours with caller identity and target VM.
- 5Check whether multiple VMs were targeted in sequence — this is a clear lateral movement pattern via management-plane pivoting.
| Indicator | Meaning | Action |
|---|---|---|
| Human UPN invoking RunCommand | Human account performing RunCommand outside an incident ticket | ESCALATE — verify with user |
| Multiple VMs in sequence | Lateral movement via management plane across subscription | ISOLATE all targeted VMs |
| Extension/write + RunCommand combo | Attacker installing persistent backdoor via extension | ESCALATE immediately |
| Unfamiliar service principal | Compromised or rogue service principal executing commands | Revoke SP credentials |
After executing an initial RunCommand, attackers typically drop a persistent backdoor (scheduled task, cron job, new local admin account, or a C2 implant). Defender for Endpoint on the VM will capture process creation events from the VM Guest Agent (GuestAgent.exe on Windows, waagent on Linux). These process trees reveal the script content that was executed, even though the command body is not stored in AzureActivity.
- 1Navigate to Advanced Hunting and run the KQL below targeting the affected VM hostname. Filter by process parent
GuestAgent.exe(Windows) orwaagent(Linux) to see all commands executed via RunCommand. - 2Review the full process tree: did the RunCommand script spawn
cmd.exe,powershell.exe, orbash? What child processes did those spawn? - 3Look for network connections made immediately after RunCommand execution: outbound TCP to non-RFC1918 IPs on unusual ports (4444, 8080, 443 to unknown hosts) indicate C2 beaconing.
- 4Check for new local administrator accounts:
net user /add,net localgroup administrators, or PowerShellNew-LocalUserin the process command lines. - 5Check for scheduled task creation:
schtasks /createorRegister-ScheduledTaskin command lines — these indicate persistence installation.
If the VM does not have Defender for Endpoint installed, the command content is not recoverable from logs alone. In this case, perform a live forensic memory acquisition of the VM immediately while it is still running — memory may contain the script that was executed.
03 — CONTAINMENT
IMMEDIATE ACTION- 1Deny all outbound network access from the VM: add a Deny All Outbound NSG rule at priority 100 to immediately cut C2 connectivity while preserving forensic state.
- 2Revoke the caller's RBAC role at the VM, resource group, or subscription level as appropriate: Azure portal → Access control (IAM) → Role assignments → Remove.
- 3If the caller is a service principal, also revoke its client secrets and certificates in Entra ID to prevent re-authentication.
- 4Do NOT reboot or deallocate the VM until forensic memory acquisition is complete — rebooting destroys volatile evidence including C2 process state.
- 5Snapshot the OS disk for forensic analysis before any remediation actions.
04 — ERADICATION
CLEANUP- 1Remove any persistence mechanisms identified in I2: scheduled tasks, new local admin accounts, added SSH keys, or backdoor services.
- 2Implement an Azure Policy to deny
Microsoft.Compute/virtualMachines/runCommand/actionto all identities except a specific approved-list group. This prevents future management-plane RCE by unauthorized identities. - 3Audit all VM extension installations in the subscription over the past 30 days for unauthorized extensions.
- 4If the VM cannot be trusted post-compromise, rebuild from a known-good image (golden image) rather than attempting in-place cleanup.
- 5Create a Sentinel analytics rule alerting on all future RunCommand operations outside the approved-list service principals.
- All persistence mechanisms removed from affected VMs
- Caller identity (SP or user) credentials revoked and RBAC removed
- Azure Policy denying RunCommand to non-approved identities deployed
- VM rebuilt or forensically verified clean
- Sentinel alert rule for future RunCommand events created
05 — ESCALATION
ESCALATION PATHS| Condition | Severity | Action | Notify |
|---|---|---|---|
| C2 beacon confirmed, multiple VMs compromised | SEV1 | Full IR, isolate subscription, forensic acquisition | CISO → IT Security → Cloud team |
| Single VM, persistence installed, unauthorized caller | SEV1 | Isolate VM, revoke caller, rebuild VM | SOC Lead → CISO |
| Suspicious RunCommand, no post-execution artifacts | SEV2 | Revoke caller RBAC, audit VM, continue investigation | SOC Lead → Cloud team |
| Verified DevOps pipeline, authorized command | INFO | Add to allowlist, document, close | None |
Stay Threat-Ready
Follow CyberHawk Threat Intel for daily SOC analyst playbooks, detection engineering guides, and threat intelligence.