SOP-45: Azure VM RunCommand Abuse Response

·

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 02 Identification 03 Containment 04 Eradication 05 Escalation
🛠

01 — BACKGROUND

TECHNIQUE OVERVIEW
01
Azure VM RunCommand Architecture & Abuse Surface
T1059 / T1651

RunCommand 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.

FieldValue
MITRE TechniqueT1059 — Command and Scripting Interpreter, T1651 — Cloud Administration Command
Log SourceAzureActivity (OperationName contains "runCommand")
Operation NamesMicrosoft.Compute/virtualMachines/runCommand/action, Microsoft.Compute/virtualMachines/extensions/write
Execution ContextSYSTEM (Windows) / root (Linux) via Azure VM Guest Agent
Network BypassYES — management plane bypasses all NSG/firewall rules
Detection PriorityHIGH — any RunCommand from non-approved identity is suspicious
🔍

02 — IDENTIFICATION

2 INDICATORS
▶ Investigation Workflow
I1
RunCommand Execution Detection & Caller Triage
T1651
WHY CHECK

AzureActivity 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.

📍 Portal Navigation — Azure Activity Log
portal.azure.com Monitor Activity log → filter: Operation = Run Command
  • 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.
IndicatorMeaningAction
Human UPN invoking RunCommandHuman account performing RunCommand outside an incident ticketESCALATE — verify with user
Multiple VMs in sequenceLateral movement via management plane across subscriptionISOLATE all targeted VMs
Extension/write + RunCommand comboAttacker installing persistent backdoor via extensionESCALATE immediately
Unfamiliar service principalCompromised or rogue service principal executing commandsRevoke SP credentials
DETECTS: All VM RunCommand operations and VM extension installations in AzureActivity over the last 24 hours — identifies unauthorized management-plane RCE and persistence via extension write.
KQL — Microsoft Sentinel (AzureActivity — VM RunCommand)
AzureActivity | where TimeGenerated > ago(24h) | where OperationNameValue in~ ( "MICROSOFT.COMPUTE/VIRTUALMACHINES/RUNCOMMAND/ACTION", "MICROSOFT.COMPUTE/VIRTUALMACHINES/EXTENSIONS/WRITE") | where ActivityStatusValue == "Success" | extend CallerIdentity = Caller | extend TargetVM = tostring(split(ResourceId, "/")[8]) | summarize VMsTargeted = make_set(TargetVM), VMCount = dcount(TargetVM), Operations = make_set(OperationNameValue), EarliestAction = min(TimeGenerated) by CallerIdentity | order by VMCount desc
DETECTS: VM RunCommand executions in Splunk from Azure Activity logs — surfaces caller identity and targeted VMs for lateral movement triage.
SPL — Splunk
index=* sourcetype=azure:activity operationName="Microsoft.Compute/virtualMachines/runCommand/action" status=Succeeded | stats count as run_count, values(resourceId) as vms, dc(resourceId) as vm_count by caller | sort -vm_count
I2
Post-Execution Persistence & C2 Beacon Check
T1059
WHY CHECK

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.

📍 Portal Navigation — Defender for Endpoint (Process Tree)
security.microsoft.com Incidents & alerts Hunting → Advanced Hunting
  • 1Navigate to Advanced Hunting and run the KQL below targeting the affected VM hostname. Filter by process parent GuestAgent.exe (Windows) or waagent (Linux) to see all commands executed via RunCommand.
  • 2Review the full process tree: did the RunCommand script spawn cmd.exe, powershell.exe, or bash? 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 PowerShell New-LocalUser in the process command lines.
  • 5Check for scheduled task creation: schtasks /create or Register-ScheduledTask in command lines — these indicate persistence installation.
DETECTS: All processes spawned by the Azure VM Guest Agent on the targeted host — reveals the actual commands executed via RunCommand including persistence mechanisms and C2 setup scripts.
KQL — Microsoft Defender XDR (DeviceProcessEvents — RunCommand Children)
let TargetHost = "TARGET_VM_HOSTNAME"; let RunCommandTime = datetime("RUNCOMMAND_TIMESTAMP"); DeviceProcessEvents | where TimeGenerated between ((RunCommandTime - 5m) .. (RunCommandTime + 30m)) | where DeviceName == TargetHost | where InitiatingProcessFileName in~ ("GuestAgent.exe","WindowsAzureGuestAgent.exe","waagent") or InitiatingProcessParentFileName in~ ("GuestAgent.exe","WindowsAzureGuestAgent.exe","waagent") | project TimeGenerated, DeviceName, InitiatingProcessFileName, FileName, ProcessCommandLine, AccountName | order by TimeGenerated asc

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
C1
Verdict & Escalation Decision
DECISION GATE
▶ Triage Verdict — Select One
CONFIRMED Persistence mechanism or C2 beacon found in process tree → Isolate VM from network, revoke caller identity, full IR, forensic acquisition
PARTIAL Unknown caller, no persistence found yet, investigation ongoing → Revoke caller RBAC on VM resource, block further RunCommand, continue investigation
FALSE POSITIVE Caller is DevOps pipeline (verified), command matches deployment script → Document, add pipeline SP to allowlist, tune alert
C2
VM Isolation & Caller Access Revocation
CONTAINMENT
  • 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
E1
VM Remediation & RunCommand Governance
ERADICATION
  • 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/action to 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.
Eradication Complete When
  • 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
ES1
Escalation Matrix
ESCALATION
ConditionSeverityActionNotify
C2 beacon confirmed, multiple VMs compromisedSEV1Full IR, isolate subscription, forensic acquisitionCISO → IT Security → Cloud team
Single VM, persistence installed, unauthorized callerSEV1Isolate VM, revoke caller, rebuild VMSOC Lead → CISO
Suspicious RunCommand, no post-execution artifactsSEV2Revoke caller RBAC, audit VM, continue investigationSOC Lead → Cloud team
Verified DevOps pipeline, authorized commandINFOAdd to allowlist, document, closeNone

Stay Threat-Ready

Follow CyberHawk Threat Intel for daily SOC analyst playbooks, detection engineering guides, and threat intelligence.

📺 YouTube 🎤 TikTok 🐦 X / Twitter 📡 Telegram
All SOPs Blog Web App (Free)
They can't exploit you if you are the Exploit.