Skip to content

Back to the catalog

Abusing and Detecting Alternative Data Channel Command Execution on Azure Virtual Machines
hausec.comwriteup

Abusing and Detecting Alternative Data Channel Command Execution on Azure Virtual Machines

Hausec details a covert command execution technique on Azure VMs abusing the userData property of the Instance Metadata Service (IMDS): an attacker with VM write privileges uses Invoke-AzVMRunCommand once to deploy a PowerShell "agent" that polls IMDS for base64-encoded commands, executes them, and exfiltrates results back via the Azure REST API using a smuggled access token — enabling repeat command execution without further RunCommand calls. The technique is implemented in PowerZure via Invoke-AzureVMUserDataCommand and Invoke-AzureVMUserDataAgent, and the post includes detection guidance covering artifact locations, Activity log alerts on Invoke-AzVMRunCommand, and PowerShell logging.

TL;DR Abusing Azure VM userData/IMDS as a covert C2 channel via a PowerZure-deployed PowerShell agent, plus detection tips.
Document

What it is

A covert command-execution technique for Azure VMs that avoids repeated Invoke-AzVMRunCommand calls by abusing the userData property of the Instance Metadata Service (IMDS). Implemented in PowerZure as Invoke-AzureVMUserDataCommand and Invoke-AzureVMUserDataAgent. The value below the summary is the concrete detection surface: exact artifact paths, the persistence mechanism, and the token-smuggling design constraint.

How it works

The asymmetry that makes this work: IMDS (http://169.254.169.254/metadata/instance) accepts only GET from the local VM, so the VM can read userData but cannot write metadata back through IMDS. Writes require the Azure REST API (https://management.azure.com/subscriptions/...), which supports PATCH/PUT. The technique bridges this by smuggling an Azure REST access token into the uploaded userData alongside the command — the agent then uses that token to authenticate its result upload.

The VM reads userData locally with:

$userData = Invoke-RestMethod -Headers @{"Metadata"="true"} -Method GET -NoProxy -Uri "http://169.254.169.254/metadata/instance/compute/userData?api-version=2021-01-01&format=text"
[System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($userData))

Persistence is a Scheduled Task triggered on an Azure-specific Event ID that fires several times per minute, giving near-continuous polling without a long-running process.

Preconditions

All three must hold, or the technique fails:

  • The account uploading data has VM write privileges (Microsoft.Compute/virtualMachines/write — included in VM Contributor, Contributor, etc.).
  • Invoke-AzVMRunCommand can be executed by users without approval.
  • The VM is powered on.

What to do

Detection artifacts the summary only gestures at, given here in full:

  • PowerZure agent script: C:\WindowsAzure\SecAgent\AzureInstanceMetadataService.ps1
  • RunCommand payload residue: C:\Packages\Plugins\Microsoft.CPlat.Core.RunCommandWindows\1.1.9\Downloads
  • Activity log: Invoke-AzVMRunCommand always logs — alert on and review it.
  • PowerShell logging captures all agent activity since commands execute in-script.
  • Manual check: inspect the userData field in the portal. The author reports never having seen it legitimately populated; treat any content as suspect and verify its purpose.

Caveats

The * in "without RunCommand" is real — Invoke-AzVMRunCommand is still required once to plant the agent, so that initial call is your most reliable detection anchor. Paths and event-driven scheduling are PowerZure-implementation specifics; a bespoke agent could relocate or rename them.

Organisations named here

  • Invoke-AzureVMUserDataAgent
  • Invoke-AzureVMUserDataCommand

Technologies named here

  • Azure Virtual Machines
  • PowerZure
  • Invoke-AzVMRunCommand
  • Azure Instance Metadata Service
  • Azure REST API
  • Azure
  • PowerShell