Az - Function Apps

Reading time: 15 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

Informations de base

Azure Function Apps sont un service de calcul sans serveur qui vous permet d'exécuter de petits morceaux de code, appelés fonctions, sans gérer l'infrastructure sous-jacente. Elles sont conçues pour exécuter du code en réponse à divers déclencheurs, tels que des requêtes HTTP, des minuteries ou des événements d'autres services Azure comme Blob Storage ou Event Hubs. Les Function Apps prennent en charge plusieurs langages de programmation, y compris C#, Python, JavaScript et Java, ce qui les rend polyvalentes pour créer des applications basées sur des événements, automatiser des flux de travail ou intégrer des services. Elles sont rentables, car vous ne payez généralement que pour le temps de calcul utilisé lorsque votre code s'exécute.

note

Notez que les fonctions sont un sous-ensemble des App Services, par conséquent, de nombreuses fonctionnalités discutées ici seront également utilisées par les applications créées en tant qu'Azure Apps (webapp dans cli).

Différents Plans

  • Flex Consumption Plan : Offre un scalabilité dynamique et basée sur les événements avec un tarif à l'utilisation, ajoutant ou supprimant des instances de fonction en fonction de la demande. Il prend en charge le réseau virtuel et les instances pré-provisionnées pour réduire les démarrages à froid, ce qui le rend adapté aux charges de travail variables qui ne nécessitent pas de support de conteneur.
  • Traditional Consumption Plan : L'option sans serveur par défaut, où vous ne payez que pour les ressources de calcul lorsque les fonctions s'exécutent. Il s'adapte automatiquement en fonction des événements entrants et inclut des optimisations de démarrage à froid, mais ne prend pas en charge les déploiements de conteneurs. Idéal pour les charges de travail intermittentes nécessitant une mise à l'échelle automatique.
  • Premium Plan : Conçu pour des performances constantes, avec des travailleurs préchauffés pour éliminer les démarrages à froid. Il offre des temps d'exécution prolongés, un réseau virtuel, et prend en charge des images Linux personnalisées, ce qui le rend parfait pour des applications critiques nécessitant des performances élevées et des fonctionnalités avancées.
  • Dedicated Plan : Fonctionne sur des machines virtuelles dédiées avec une facturation prévisible et prend en charge la mise à l'échelle manuelle ou automatique. Il permet d'exécuter plusieurs applications sur le même plan, fournit une isolation de calcul, et garantit un accès réseau sécurisé via des environnements de services d'application, ce qui le rend idéal pour des applications de longue durée nécessitant une allocation de ressources constante.
  • Container Apps : Permet de déployer des applications de fonction conteneurisées dans un environnement géré, aux côtés de microservices et d'APIs. Il prend en charge des bibliothèques personnalisées, la migration d'applications héritées, et le traitement GPU, éliminant la gestion des clusters Kubernetes. Idéal pour des applications conteneurisées évolutives basées sur des événements.

Buckets de stockage

Lors de la création d'une nouvelle Function App non conteneurisée (mais en fournissant le code à exécuter), le code et d'autres données liées à la fonction seront stockés dans un compte de stockage. Par défaut, la console web créera un nouveau compte par fonction pour stocker le code.

De plus, en modifiant le code à l'intérieur du bucket (dans les différents formats dans lesquels il pourrait être stocké), le code de l'application sera modifié pour le nouveau et exécuté la prochaine fois que la fonction est appelée.

caution

Cela est très intéressant du point de vue des attaquants car l'accès en écriture sur ce bucket permettra à un attaquant de compromettre le code et d'escalader les privilèges aux identités gérées à l'intérieur de la Function App.

Plus d'informations à ce sujet dans la section sur l'escalade de privilèges.

Il est également possible de trouver les clés maître et fonctions 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.

Notez que les fonctions permettent également de stocker le code dans un emplacement distant en indiquant simplement l'URL.

Réseautique

En utilisant un déclencheur HTTP :

  • Il est possible de donner accès à une fonction depuis tout Internet sans nécessiter d'authentification ou de donner un accès basé sur IAM. Bien qu'il soit également possible de restreindre cet accès.
  • Il est également possible de donner ou restreindre l'accès à une Function App depuis un réseau interne (VPC).

caution

Cela est très intéressant du point de vue des attaquants car il pourrait être possible de pivoter vers des réseaux internes depuis une fonction vulnérable exposée à Internet.

Paramètres de l'application de fonction et variables d'environnement

Il est possible de configurer des variables d'environnement à l'intérieur d'une application, qui pourraient contenir des informations sensibles. De plus, par défaut, les variables d'environnement AzureWebJobsStorage et WEBSITE_CONTENTAZUREFILECONNECTIONSTRING (entre autres) sont créées. Celles-ci sont particulièrement intéressantes car elles contiennent la clé de compte pour contrôler avec des permissions COMPLETES le compte de stockage contenant les données de l'application. Ces paramètres sont également nécessaires pour exécuter le code depuis le compte de stockage.

Ces variables d'environnement ou paramètres de configuration contrôlent également comment la fonction exécute le code, par exemple si WEBSITE_RUN_FROM_PACKAGE existe, cela indiquera l'URL où le code de l'application est situé.

Sandbox de fonction

À l'intérieur de la sandbox linux, le code source est situé dans /home/site/wwwroot dans le fichier function_app.py (si Python est utilisé) l'utilisateur exécutant le code est app (sans permissions sudo).

Dans une fonction Windows utilisant NodeJS, le code était situé dans C:\home\site\wwwroot\HttpTrigger1\index.js, le nom d'utilisateur était mawsFnPlaceholder8_f_v4_node_20_x86 et faisait partie des groupes : Mandatory Label\High Mandatory Level Label, Everyone, BUILTIN\Users, NT AUTHORITY\INTERACTIVE, CONSOLE LOGON, NT AUTHORITY\Authenticated Users, NT AUTHORITY\This Organization, BUILTIN\IIS_IUSRS, LOCAL, 10-30-4-99\Dwas Site Users.

Identités gérées et métadonnées

Tout comme VMs, les fonctions peuvent avoir des Identités Gérées de 2 types : assignées par le système et assignées par l'utilisateur.

L'identité assignée par le système sera une identité gérée que seule la fonction qui l'a assignée pourra utiliser, tandis que les identités gérées assignées par l'utilisateur sont des identités gérées que tout autre service Azure pourra utiliser.

note

Tout comme dans VMs, les fonctions peuvent avoir 1 identité gérée assignée par le système et plusieurs identités assignées par l'utilisateur, il est donc toujours important d'essayer de trouver toutes celles-ci si vous compromettez la fonction car vous pourriez être en mesure d'escalader les privilèges à plusieurs identités gérées à partir d'une seule fonction.

Si aucune identité gérée par le système n'est utilisée mais qu'une ou plusieurs identités gérées par l'utilisateur sont attachées à une fonction, par défaut, vous ne pourrez pas obtenir de jeton.

Il est possible d'utiliser les scripts PEASS pour obtenir des jetons de l'identité gérée par défaut à partir du point de terminaison des métadonnées. Ou vous pourriez les obtenir manuellement comme expliqué dans :

Cloud SSRF - HackTricks

Notez que vous devez trouver un moyen de vérifier toutes les identités gérées qu'une fonction a attachées car si vous ne l'indiquez pas, le point de terminaison des métadonnées n'utilisera que la par défaut (voir le lien précédent pour plus d'informations).

Clés d'accès

note

Notez qu'il n'y a pas de permissions RBAC pour donner accès aux utilisateurs pour invoquer les fonctions. L'invocation de la fonction dépend du déclencheur sélectionné lors de sa création et si un déclencheur HTTP a été sélectionné, il pourrait être nécessaire d'utiliser une clé d'accès.

Lors de la création d'un point de terminaison à l'intérieur d'une fonction utilisant un déclencheur HTTP, il est possible d'indiquer le niveau d'autorisation de clé d'accès nécessaire pour déclencher la fonction. Trois options sont disponibles :

  • ANONYMOUS : Tout le monde peut accéder à la fonction par l'URL.
  • FUNCTION : Le point de terminaison n'est accessible qu'aux utilisateurs utilisant une clé de fonction, d'hôte ou maître.
  • ADMIN : Le point de terminaison n'est accessible qu'aux utilisateurs avec une clé maître.

Type de clés :

  • Clés de fonction : Les clés de fonction peuvent être soit par défaut soit définies par l'utilisateur et sont conçues pour accorder un accès exclusivement à des points de terminaison de fonction spécifiques au sein d'une Function App permettant un accès plus granulaire sur les points de terminaison.
  • Clés d'hôte : Les clés d'hôte, qui peuvent également être par défaut ou définies par l'utilisateur, fournissent un accès à tous les points de terminaison de fonction au sein d'une Function App avec un niveau d'accès FUNCTION.
  • Clé maître : La clé maître (_master) sert de clé administrative qui offre des permissions élevées, y compris l'accès à tous les points de terminaison de fonction (niveau d'accès ADMIN inclus). Cette clé ne peut pas être révoquée.
  • Clés système : Les clés système sont gérées par des extensions spécifiques et sont nécessaires pour accéder aux points de terminaison webhook utilisés par des composants internes. Des exemples incluent le déclencheur Event Grid et les Durable Functions, qui utilisent des clés système pour interagir en toute sécurité avec leurs API respectives.

tip

Exemple pour accéder à un point de terminaison API de fonction en utilisant une clé :

https://<function_uniq_name>.azurewebsites.net/api/<endpoint_name>?code=<access_key>

Authentification de base

Tout comme dans les App Services, les fonctions prennent également en charge l'authentification de base pour se connecter à SCM et FTP pour déployer du code en utilisant un nom d'utilisateur et un mot de passe dans une URL fournie par Azure. Plus d'informations à ce sujet dans :

Az - Azure App Services

Déploiements basés sur Github

Lorsqu'une fonction est générée à partir d'un dépôt Github, la console web Azure permet de créer automatiquement un Workflow Github dans un dépôt spécifique afin que chaque fois que ce dépôt est mis à jour, le code de la fonction soit mis à jour. En fait, le fichier yaml de l'action Github pour une fonction Python ressemble à ceci :

Yaml d'action Github
yaml
# Docs for the Azure Web Apps Deploy action: https://github.com/azure/functions-action
# More GitHub Actions for Azure: https://github.com/Azure/actions
# More info on Python, GitHub Actions, and Azure Functions: https://aka.ms/python-webapps-actions

name: Build and deploy Python project to Azure Function App - funcGithub

on:
push:
branches:
- main
workflow_dispatch:

env:
AZURE_FUNCTIONAPP_PACKAGE_PATH: "." # set this to the path to your web app project, defaults to the repository root
PYTHON_VERSION: "3.11" # set this to the python version to use (supports 3.6, 3.7, 3.8)

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup Python version
uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}

- name: Create and start virtual environment
run: |
python -m venv venv
source venv/bin/activate

- name: Install dependencies
run: pip install -r requirements.txt

# Optional: Add step to run tests here

- name: Zip artifact for deployment
run: zip release.zip ./* -r

- name: Upload artifact for deployment job
uses: actions/upload-artifact@v4
with:
name: python-app
path: |
release.zip
!venv/

deploy:
runs-on: ubuntu-latest
needs: build

permissions:
id-token: write #This is required for requesting the JWT

steps:
- name: Download artifact from build job
uses: actions/download-artifact@v4
with:
name: python-app

- name: Unzip artifact for deployment
run: unzip release.zip

- name: Login to Azure
uses: azure/login@v2
with:
client-id: ${{ secrets.AZUREAPPSERVICE_CLIENTID_6C3396368D954957BC58E4C788D37FD1 }}
tenant-id: ${{ secrets.AZUREAPPSERVICE_TENANTID_7E50AEF6222E4C3DA9272D27FB169CCD }}
subscription-id: ${{ secrets.AZUREAPPSERVICE_SUBSCRIPTIONID_905358F484A74277BDC20978459F26F4 }}

- name: "Deploy to Azure Functions"
uses: Azure/functions-action@v1
id: deploy-to-function
with:
app-name: "funcGithub"
slot-name: "Production"
package: ${{ env.AZURE_FUNCTIONAPP_PACKAGE_PATH }}

De plus, une Identité Gérée est également créée afin que l'Action Github du dépôt puisse se connecter à Azure avec elle. Cela se fait en générant une crédential fédérée sur l'Identité Gérée permettant à l'Émetteur https://token.actions.githubusercontent.com et à l'Identifiant de Sujet repo:<org-name>/<repo-name>:ref:refs/heads/<branch-name>.

caution

Par conséquent, quiconque compromettant ce dépôt pourra compromettre la fonction et les Identités Gérées qui y sont attachées.

Déploiements Basés sur des Conteneurs

Tous les plans ne permettent pas de déployer des conteneurs, mais pour ceux qui le font, la configuration contiendra l'URL du conteneur. Dans l'API, le paramètre linuxFxVersion aura quelque chose comme : DOCKER|mcr.microsoft.com/..., tandis que dans la console web, la configuration affichera les paramètres d'image.

De plus, aucun code source ne sera stocké dans le compte de stockage lié à la fonction car cela n'est pas nécessaire.

Énumération

bash
# List all the functions
az functionapp list

# List functions in an function-app (endpoints)
az functionapp function list \
--name <app-name> \
--resource-group <res-group>

# Get details about the source of the function code
az functionapp deployment source show \
--name <app-name> \
--resource-group <res-group>
## If error like "This is currently not supported."
## Then, this is probalby using a container

# Get more info if a container is being used
az functionapp config container show \
--name <name> \
--resource-group <res-group>

# Get settings (and privesc to the sorage account)
az functionapp config appsettings list --name <app-name> --resource-group <res-group>

# Get access restrictions
az functionapp config access-restriction show --name <app-name> --resource-group <res-group>

# Check if a domain was assigned to a function app
az functionapp config hostname list --webapp-name <app-name> --resource-group <res-group>

# Get SSL certificates
az functionapp config ssl list --resource-group <res-group>

# Get network restrictions
az functionapp config access-restriction show --name <app-name> --resource-group <res-group>

# Get acess restrictions
az functionapp config access-restriction show --name <app-name> --resource-group <res-group>

# Get connection strings
az rest --method POST --uri "https://management.azure.com/subscriptions/<subscription>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/config/connectionstrings/list?api-version=2022-03-01"
az rest --method GET --uri "https://management.azure.com/subscriptions/<subscription>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/config/configreferences/connectionstrings?api-version=2022-03-01"

# Get SCM credentials
az functionapp deployment list-publishing-credentials --name <app-name> --resource-group <res-group>

# Get function, system and master keys
az functionapp keys list --name <app-name> --resource-group <res-group>

# Get Host key
az rest --method POST --uri "https://management.azure.com/<subscription>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/functions/<function-endpoint-name>/listKeys?api-version=2022-03-01"

# Get source code with Master Key of the function
curl "<script_href>?code=<master-key>"
curl "https://<func-app-name>.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=<master-key>" -v

# Get source code using SCM access (Azure permissions or SCM creds)
az rest --method GET \
--url "https://<func-app-name>.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=<master-key>" \
--resource "https://management.azure.com/"

# Get source code with Azure permissions
az rest --url "https://management.azure.com/subscriptions/<subscription>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/hostruntime/admin/vfs/function_app.py?relativePath=1&api-version=2022-03-01"
## Another example
az rest --url "https://management.azure.com/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/Resource_Group_1/providers/Microsoft.Web/sites/ConsumptionExample/hostruntime/admin/vfs/HttpExample/index.js?relativePath=1&api-version=2022-03-01"

Escalade de privilèges

Az - Functions App Privesc

Références

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