Az - Automation Accounts

Reading time: 10 minutes

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

Basic Information

Azure Automation Accounts are cloud-based services in Microsoft Azure that help automate tasks like resource management, configuration, and updates across Azure and on-premises environments. They provide Runbooks (scripts for automation that are executed), schedules, and hybrid worker groups to run automation jobs, enabling infrastructure as code (IaC) and process automation for improved efficiency and consistency in managing cloud resources.

Settings

  • Credentials: The password is only accessible within a runbook inside the automation account, they are used to store usernames and passwords securely.
  • Variables: Used to store configuration data that can be used in runbooks. This could also be sensitive information like API keys. If the variable is stored encrypted, it's only available within a runbook inside the automation account.
  • Certificates: Used to store certificates that can be used in runbooks.
  • Connections: Used to store connection information to external services. This could contain sensitive information.
  • Network Access: It can be set to public or private.

Runbooks & Jobs

A Runbook in Azure Automation is a script that performs tasks automatically within your cloud environment. Runbooks can be written in PowerShell, Python, or Graphical editors. They help automate administrative tasks like VM management, patching, or compliance checks.

In the code located inside Runbooks could contains sensitive info (such as creds).

A Job is an instance of a Runbook execution. When you run a Runbook, a Job is created to track that execution. Each job includes:

  • Status: Queued, Running, Completed, Failed, Suspended.
  • Output: The result of the Runbook execution.
  • Start and End Time: When the job started and completed.

A job contains the output of the Runbook execution. If you can read the jobs, do it as they contain the output of the run (potential sensitive info).

Schedules & Webhooks

There are 3 main ways to execute a Runbook:

  • Schedules: These are used to trigger Runbooks at a specific time or interval.
  • Webhooks: These are HTTP endpoints that can be used to trigger Runbooks from external services. Note that the webhook URL is not visible after creation.
  • Manual Trigger: You can manually trigger a Runbook from the Azure Portal and from the cli.

Source Control

It allows to import Runbooks from Github, Azure Devops (Git) and Azure Devops (TFVC). It's possible to indicate it to publish the Runbooks of the repo to Azure Automation account and it's also possible to indicate to sync the changes from the repo to the Azure Automation account.

When the sync is enabled, in the Github repository a webhook is created to trigger the sync every time a push event occurs. Example of a webhook URL: https://f931b47b-18c8-45a2-9d6d-0211545d8c02.webhook.eus.azure-automation.net/webhooks?token=DRjQyFiOrUtz%2fw7o23XbDpOlTe1%2bUqPQm4pQH2WBfJg%3d

Note that these webhooks won't be visible when listing webhooks in the associated runbooks to the Github repo. Also note that it's not possible to change the repo URL of a source control once it's created.

In order for the configured source control to work, the Azure Automation Account needs to have a managed identity (system or user) with the Contributor role. Moreover, to assign a user managed identity to the Automation Account, it's needed to indicate the client ID of the user MI in the variable AUTOMATION_SC_USER_ASSIGNED_IDENTITY_ID.

Runtime Environments

When creating a Runbook it's possible to select the runtime environment. By default, the following runtime environments are available:

  • Powershell 5.1
  • Powershell 7.1
  • PowerShell 7.2
  • Python 3.10
  • Python 3.8
  • Python 2.7

However, it's also possible to create your own environments, using one of these as a base. In the case of python, it's possible to upload .whl packages to the environment that will be used. In the case of PowerShell, it's possible to upload .zip packages with the modules to have in the runtime.

Hybrid Worker Groups

In Azure Automation, the default execution environment for runbooks is the Azure Sandbox, a cloud-based platform managed by Azure, suitable for tasks involving Azure resources. However, this sandbox has limitations, such as restricted access to on-premises resources and constraints on execution time and resource usage. To overcome these limitations, Hybrid Worker Groups are employed. A Hybrid Worker Group consists of one or more Hybrid Runbook Workers installed on your own machines, whether on-premises, in other cloud environments or Azure VMs. This setup allows runbooks to execute directly on these machines, providing direct access to local resources, the ability to run longer and more resource-intensive tasks, and the flexibility to interact with environments beyond Azure's immediate reach.

When a hybrid worker group is created it's needed to indicate the credentials to use. There are 2 options:

  • Default credentials: You don't need to provide the credentials and the runbooks will be executed inside the VMs as System.
  • Specific credentials: You need to provide the name of the credentials object inside the automation account, which will be used to execute the runbooks inside the VMs. Therefore, in this case, it could be possible to steal valid credentials for the VMs.

Therefore, if you can choose to run a Runbook in a Hybrid Worker, you will execute arbitrary commands inside an external machine as System (nice pivot technique).

Moreover, if the hybrid worker is running in Azure with other Managed Identities attached, the runbook will be able to access the managed identity of the runbook and all the managed identities of the VM from the metadata service.

tip

Remember that the metadata service has a different URL (http://169.254.169.254) than the service from where get the managed identities token of the automation account (IDENTITY_ENDPOINT).

State Configuration (SC)

[!WARNING] As indicated in the docs, Azure Automation State Configuration will be retired on September 30, 2027 and replaced by Azure Machine Configuration.

Automation Accounts also support State Configuration (SC), which is a feature that helps configure and maintain the state of your VMs. It's possible to create and apply DSC configurations to Windows and Linux machines.

From an attackers perspective this was interesting because it allowed to execute arbitrary PS code in all the configured VMs allowing to escalate privileges to the managed identities of these VMs, potentially pivoting to new networks... Also, the configurations could contain sensitive info.

Enumeration

bash
# List Automation Accounts
az automation account list --output table

# Get Automation Account details
# Check the network access in `privateEndpointConnections` and `publicNetworkAccess`
# Check the managed identities in `identity`
az automation account show --name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME>

# Get keys of automation account
## These are used for the DSC
az automation account list-keys --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME>

# Get schedules of automation account
az automation schedule list --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME>

# Get connections of automation account
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/connections?api-version=2023-11-01"

# Get connection details
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/connections/<connection-name>?api-version=2023-11-01"

# Get credentials of automation account
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/credentials?api-version=2023-11-01"

# Get credential details
## Note that you will only be able to access the password from inside a Runbook
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/credentials/<credential-name>?api-version=2023-11-01"

# Get certificates of automation account
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/certificates?api-version=2023-11-01"

# Get certificate details
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/certificates/<certificate-name>?api-version=2023-11-01"

# Get variables of automation account
## It's possible to get the value of unencrypted variables but not the encrypted ones
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/variables?api-version=2023-11-01"

# Get variable details
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/variables/<variable-name>?api-version=2023-11-01"

# Get runbooks of an automation account
az automation runbook list --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME>

# Get runbook details
az automation runbook show --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME> --name <RUNBOOK-NAME>

# Get runbook content
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/runbooks/<runbook-name>/content?api-version=2023-11-01"

# Get jobs of an automation account
az automation job list --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME>

# Get job details
az automation job show --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME> --name <JOB-NAME>

# Get job output
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"

# Get the Runbook content when the job was executed
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>/runbookContent?api-version=2023-11-01"

# Get webhooks inside an automation account
## It's possible to see to which runbook it belongs in the given data
## For security reasons it's not possible to see the URL of the webhook after creating it, here is a URL example: https://f931b47b-18c8-45a2-9d6d-0211545d8c02.webhook.eus.azure-automation.net/webhooks?token=dOdnxk6z7ugAxiuyUMKgPuDMav2Jw5EJediMdiN4jLo%3d
## Generating a webhook can be useful from a persistence perspective
az rest --method GET \
  --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/webhooks?api-version=2018-06-30"

# Get the source control setting of an automation account (if any)
## inside the output it's possible to see if the autoSync is enabled, if the publishRunbook is enabled and the repo URL
az automation source-control list --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME>

# Get custom runtime environments
## Check in defaultPackages for custom ones, by default Python envs won't have anything here and PS1 envs will have "az" and "azure cli"
az automation runtime-environment list \
    --resource-group <res-group>> \
    --automation-account-name <account-name> \
    --query "[?!(starts_with(description, 'System-generated'))]"

# Get State Configurations (SC) of an automation account
az automation dsc configuration list --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME>

# Get State Configuration details
az automation dsc configuration show --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME> --name <DSC-CONFIG-NAME>

# Get State Configuration content
az automation dsc configuration show-content --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME> --name <DSC-CONFIG-NAME>

# Get hybrid worker groups for an automation account
az automation hrwg list --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME>

# Get hybrid worker group details
az automation hrwg show --automation-account-name <AUTOMATION-ACCOUNT> --resource-group <RG-NAME> --name <HYBRID-WORKER-GROUP>

# Get more details about a hybrid worker group (like VMs inside it)
az rest --method GET --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>>/providers/Microsoft.Automation/automationAccounts/<automation-account-name>/hybridRunbookWorkerGroups/<hybrid-worker-group-name>/hybridRunbookWorkers?&api-version=2021-06-22"
powershell
# Check user right for automation
az extension add --upgrade -n automation
az automation account list # if it doesn't return anything the user is not a part of an Automation group

# Gets Azure Automation accounts in a resource group
Get-AzAutomationAccount

# List & get DSC configs
Get-AzAutomationAccount | Get-AzAutomationDscConfiguration
Get-AzAutomationAccount | Get-AzAutomationDscConfiguration | where {$_.name -match '<name>'} | Export-AzAutomationDscConfiguration -OutputFolder . -Debug
## Automation Accounts named SecurityBaselineConfigurationWS... are there by default (not interesting)

# List & get Run books code
Get-AzAutomationAccount | Get-AzAutomationRunbook
Get-AzAutomationAccount | Get-AzAutomationRunbook | Export-AzAutomationRunbook -OutputFolder /tmp

# List credentials & variables & others
Get-AzAutomationAccount | Get-AzAutomationCredential
Get-AzAutomationAccount | Get-AzAutomationVariable
Get-AzAutomationAccount | Get-AzAutomationConnection
Get-AzAutomationAccount | Get-AzAutomationCertificate
Get-AzAutomationAccount | Get-AzAutomationSchedule
Get-AzAutomationAccount | Get-AzAutomationModule
Get-AzAutomationAccount | Get-AzAutomationPython3Package
## Exfiltrate credentials & variables and the other info loading them in a Runbook and printing them

# List hybrid workers
Get-AzAutomationHybridWorkerGroup -AutomationAccountName <AUTOMATION-ACCOUNT> -ResourceGroupName <RG-NAME>

Privilege Escalation & Post Exploitation

Az - Automation Accounts Privesc

References

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