# Az - Function Apps

{{#include ../../../banners/hacktricks-training.md}}

## Informazioni di Base

**Azure Function Apps** sono un **servizio di calcolo serverless** che consente di eseguire piccoli pezzi di codice, chiamati **funzioni**, senza gestire l'infrastruttura sottostante. Sono progettate per eseguire codice in risposta a vari trigger, come **richieste HTTP, timer o eventi da altri servizi Azure** come Blob Storage o Event Hubs. Le Function Apps supportano più linguaggi di programmazione, tra cui C#, Python, JavaScript e Java, rendendole versatili per costruire **applicazioni basate su eventi**, automatizzare flussi di lavoro o integrare servizi. Sono economiche, poiché di solito si paga solo per il tempo di calcolo utilizzato quando il codice viene eseguito.

> [!NOTE]
> Nota che **le Funzioni sono un sottoinsieme dei Servizi App**, pertanto, molte delle funzionalità discusse qui saranno utilizzate anche dalle applicazioni create come Azure Apps (`webapp` in cli).

### Piani Differenti

- **Piano di Consumo Flessibile**: Offre **scalabilità dinamica e basata su eventi** con prezzi pay-as-you-go, aggiungendo o rimuovendo istanze di funzione in base alla domanda. Supporta **rete virtuale** e **istanze pre-provisionate** per ridurre i cold start, rendendolo adatto per **carichi di lavoro variabili** che non richiedono supporto per container.
- **Piano di Consumo Tradizionale**: L'opzione serverless predefinita, dove **si paga solo per le risorse di calcolo quando le funzioni vengono eseguite**. Si scala automaticamente in base agli eventi in arrivo e include **ottimizzazioni per i cold start**, ma non supporta il deployment di container. Ideale per **carichi di lavoro intermittenti** che richiedono scalabilità automatica.
- **Piano Premium**: Progettato per **prestazioni costanti**, con **lavoratori pre-riscaldati** per eliminare i cold start. Offre **tempi di esecuzione estesi, rete virtuale** e supporta **immagini Linux personalizzate**, rendendolo perfetto per **applicazioni mission-critical** che necessitano di alte prestazioni e funzionalità avanzate.
- **Piano Dedicato**: Esegue su macchine virtuali dedicate con **fatturazione prevedibile** e supporta scalabilità manuale o automatica. Consente di eseguire più app sullo stesso piano, fornisce **isolamento di calcolo** e garantisce **accesso sicuro alla rete** tramite App Service Environments, rendendolo ideale per **applicazioni a lungo termine** che necessitano di allocazione costante delle risorse.
- **Container Apps**: Consente di distribuire **app di funzione containerizzate** in un ambiente gestito, insieme a microservizi e API. Supporta librerie personalizzate, migrazione di app legacy e **elaborazione GPU**, eliminando la gestione del cluster Kubernetes. Ideale per **applicazioni containerizzate scalabili e basate su eventi**.

### **Storage Buckets**

Quando si crea una nuova Function App non containerizzata (ma si fornisce il codice da eseguire), il **codice e altri dati relativi alla Funzione saranno memorizzati in un account di Storage**. Per impostazione predefinita, la console web creerà un nuovo account per ogni funzione per memorizzare il codice.

Inoltre, modificando il codice all'interno del bucket (nei diversi formati in cui potrebbe essere memorizzato), il **codice dell'app verrà modificato con il nuovo e verrà eseguito** la prossima volta che la Funzione viene chiamata.

> [!CAUTION]
> Questo è molto interessante dal punto di vista di un attaccante poiché **l'accesso in scrittura su questo bucket** consentirà a un attaccante di **compromettere il codice e aumentare i privilegi** alle identità gestite all'interno della Function App.
>
> Maggiori informazioni su questo nella **sezione sull'escalation dei privilegi**.

È anche possibile trovare le **chiavi master e delle funzioni** memorizzate nell'account di storage nel contenitore **`azure-webjobs-secrets`** all'interno della cartella **`<app-name>`** nei file JSON che puoi trovare all'interno.

Nota che le Funzioni consentono anche di memorizzare il codice in una posizione remota semplicemente indicando l'URL.

### Networking

Utilizzando un trigger HTTP:

- È possibile dare **accesso a una funzione da tutto Internet** senza richiedere alcuna autenticazione o dare accesso basato su IAM. Anche se è possibile limitare questo accesso.
- È anche possibile **dare o limitare l'accesso** a una Function App da **una rete interna (VPC)**.

> [!CAUTION]
> Questo è molto interessante dal punto di vista di un attaccante poiché potrebbe essere possibile **pivotare verso reti interne** da una Function vulnerabile esposta a Internet.

### **Impostazioni della Function App & Variabili d'Ambiente**

È possibile configurare variabili d'ambiente all'interno di un'app, che potrebbero contenere informazioni sensibili. Inoltre, per impostazione predefinita, le variabili d'ambiente **`AzureWebJobsStorage`** e **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** (tra le altre) vengono create. Queste sono particolarmente interessanti perché **contengono la chiave dell'account per controllare con PERMESSI COMPLETI l'account di storage contenente i dati dell'applicazione**. Queste impostazioni sono anche necessarie per eseguire il codice dall'Account di Storage.

Queste variabili d'ambiente o parametri di configurazione controllano anche come la Funzione esegue il codice, ad esempio se **`WEBSITE_RUN_FROM_PACKAGE`** esiste, indicherà l'URL dove si trova il codice dell'applicazione.

### **Function Sandbox**

All'interno della sandbox linux il codice sorgente si trova in **`/home/site/wwwroot`** nel file **`function_app.py`** (se viene utilizzato python) l'utente che esegue il codice è **`app`** (senza permessi sudo).

In una **funzione Windows** che utilizza NodeJS il codice si trovava in **`C:\home\site\wwwroot\HttpTrigger1\index.js`**, il nome utente era **`mawsFnPlaceholder8_f_v4_node_20_x86`** ed era parte dei **gruppi**: `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à Gestite & Metadati**

Proprio come [**VM**](vms/index.html), le Funzioni possono avere **Identità Gestite** di 2 tipi: Assegnate dal sistema e Assegnate dall'utente.

L'**identità assegnata dal sistema** sarà un'identità gestita che **solo la funzione** a cui è assegnata potrà utilizzare, mentre le **identità gestite assegnate dall'utente** sono identità gestite che **qualsiasi altro servizio Azure potrà utilizzare**.

> [!NOTE]
> Proprio come in [**VM**](vms/index.html), le Funzioni possono avere **1 identità gestita assegnata dal sistema** e **diverse identità assegnate dall'utente**, quindi è sempre importante cercare di trovare tutte se si compromette la funzione perché si potrebbe essere in grado di aumentare i privilegi a diverse identità gestite da una sola Funzione.
>
> Se non viene utilizzata un'identità gestita dal sistema ma una o più identità gestite dall'utente sono collegate a una funzione, per impostazione predefinita non sarà possibile ottenere alcun token.

È possibile utilizzare gli [**script PEASS**](https://github.com/peass-ng/PEASS-ng) per ottenere token dall'identità gestita predefinita dall'endpoint dei metadati. Oppure potresti ottenerli **manualmente** come spiegato in:

{{#ref}}
https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html#azure-vm
{{#endref}}

Nota che è necessario trovare un modo per **controllare tutte le Identità Gestite a cui una funzione è collegata** poiché se non lo indichi, l'endpoint dei metadati utilizzerà **solo quella predefinita** (controlla il link precedente per ulteriori informazioni).

## Chiavi di Accesso

> [!NOTE]
> Nota che non ci sono permessi RBAC per dare accesso agli utenti per invocare le funzioni. L'**invocazione della funzione dipende dal trigger** selezionato quando è stata creata e se è stato selezionato un Trigger HTTP, potrebbe essere necessario utilizzare una **chiave di accesso**.

Quando si crea un endpoint all'interno di una funzione utilizzando un **trigger HTTP**, è possibile indicare il **livello di autorizzazione della chiave di accesso** necessario per attivare la funzione. Sono disponibili tre opzioni:

- **ANONYMOUS**: **Tutti** possono accedere alla funzione tramite l'URL.
- **FUNCTION**: L'endpoint è accessibile solo agli utenti che utilizzano una **chiave di funzione, host o master**.
- **ADMIN**: L'endpoint è accessibile solo agli utenti con una **chiave master**.

**Tipo di chiavi:**

- **Chiavi di Funzione:** Le chiavi di funzione possono essere predefinite o definite dall'utente e sono progettate per concedere accesso esclusivamente a **endpoint di funzione specifici** all'interno di una Function App consentendo un accesso più dettagliato sugli endpoint.
- **Chiavi Host:** Le chiavi host, che possono essere anch'esse predefinite o definite dall'utente, forniscono accesso a **tutti gli endpoint di funzione all'interno di una Function App con livello di accesso FUNCTION**.
- **Chiave Master:** La chiave master (`_master`) funge da chiave amministrativa che offre permessi elevati, incluso l'accesso a tutti gli endpoint di funzione (incluso il livello di accesso ADMIN). Questa **chiave non può essere revocata.**
- **Chiavi di Sistema:** Le chiavi di sistema sono **gestite da estensioni specifiche** e sono necessarie per accedere agli endpoint webhook utilizzati dai componenti interni. Esempi includono il trigger Event Grid e le Funzioni Durature, che utilizzano chiavi di sistema per interagire in modo sicuro con le rispettive API.

> [!TIP]
> Esempio per accedere a un endpoint API di funzione utilizzando una chiave:
>
> `https://<function_uniq_name>.azurewebsites.net/api/<endpoint_name>?code=<access_key>`

### Autenticazione di Base

Proprio come nei Servizi App, le Funzioni supportano anche l'autenticazione di base per connettersi a **SCM** e **FTP** per distribuire codice utilizzando un **nome utente e una password in un URL** fornito da Azure. Maggiori informazioni su questo in:

{{#ref}}
az-app-services.md
{{#endref}}

### Distribuzioni Basate su Github

Quando una funzione è generata da un repo Github, la console web di Azure consente di **creare automaticamente un Workflow Github in un repository specifico** in modo che ogni volta che questo repository viene aggiornato, il codice della funzione venga aggiornato. In realtà, il file yaml dell'azione Github per una funzione python appare così:

<details>

<summary>Github Action Yaml</summary>
```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 }}
```
</details>

Inoltre, viene creata un'**Identità Gestita** affinché l'azione Github del repository possa accedere ad Azure con essa. Questo avviene generando una credenziale Federata sull'**Identità Gestita** che consente all'**Emittente** `https://token.actions.githubusercontent.com` e all'**Identificatore del Soggetto** `repo:<org-name>/<repo-name>:ref:refs/heads/<branch-name>`.

> [!CAUTION]
> Pertanto, chiunque comprometta quel repository sarà in grado di compromettere la funzione e le Identità Gestite ad essa collegate.

### Distribuzioni Basate su Container

Non tutti i piani consentono di distribuire container, ma per quelli che lo fanno, la configurazione conterrà l'URL del container. Nell'API, l'impostazione **`linuxFxVersion`** avrà qualcosa come: `DOCKER|mcr.microsoft.com/...`, mentre nella console web, la configurazione mostrerà le **impostazioni dell'immagine**.

Inoltre, **nessun codice sorgente sarà memorizzato nell'account di archiviazione** relativo alla funzione poiché non è necessario.

## Enumerazione

{{#tabs }}
{{#tab name="az cli" }}
```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"

```
{{#endtab }}

{{#tab name="Az Powershell" }}
```bash
Get-Command -Module Az.Functions

# Lists all Function Apps in the current subscription or in a specific resource group.
Get-AzFunctionApp  -ResourceGroupName <String>

# Displays the regions where Azure Function Apps are available for deployment.
Get-AzFunctionAppAvailableLocation

# Retrieves details about Azure Function App plans in a subscription or resource group.
Get-AzFunctionAppPlan -ResourceGroupName <String> -Name <String>

# Retrieves the app settings for a specific Azure Function App.
Get-AzFunctionAppSetting -Name <FunctionAppName> -ResourceGroupName <ResourceGroupName>
```
{{#endtab }}
{{#endtabs }}


## Escalation dei privilegi

{{#ref}}
../az-privilege-escalation/az-functions-app-privesc.md
{{#endref}}

## Riferimenti

- [https://learn.microsoft.com/en-us/azure/azure-functions/functions-openapi-definition](https://learn.microsoft.com/en-us/azure/azure-functions/functions-openapi-definition)

{{#include ../../../banners/hacktricks-training.md}}
