# Az - Function Apps {{#include ../../../banners/hacktricks-training.md}} ## Información Básica **Azure Function Apps** son un **servicio de computación sin servidor** que te permite ejecutar pequeñas piezas de código, llamadas **funciones**, sin gestionar la infraestructura subyacente. Están diseñadas para ejecutar código en respuesta a varios desencadenadores, como **solicitudes HTTP, temporizadores o eventos de otros servicios de Azure** como Blob Storage o Event Hubs. Las Function Apps admiten múltiples lenguajes de programación, incluidos C#, Python, JavaScript y Java, lo que las hace versátiles para construir **aplicaciones impulsadas por eventos**, automatizar flujos de trabajo o integrar servicios. Son rentables, ya que generalmente solo pagas por el tiempo de computación utilizado cuando se ejecuta tu código. > [!NOTE] > Ten en cuenta que **Functions son un subconjunto de los App Services**, por lo tanto, muchas de las características discutidas aquí también serán utilizadas por aplicaciones creadas como Azure Apps (`webapp` en cli). ### Diferentes Planes - **Flex Consumption Plan**: Ofrece **escalado dinámico impulsado por eventos** con precios de pago por uso, añadiendo o eliminando instancias de función según la demanda. Soporta **redes virtuales** y **instancias pre-provisionadas** para reducir los inicios en frío, lo que lo hace adecuado para **cargas de trabajo variables** que no requieren soporte de contenedores. - **Traditional Consumption Plan**: La opción sin servidor predeterminada, donde **solo pagas por los recursos de computación cuando se ejecutan las funciones**. Escala automáticamente según los eventos entrantes e incluye **optimización de inicios en frío**, pero no admite implementaciones de contenedores. Ideal para **cargas de trabajo intermitentes** que requieren escalado automático. - **Premium Plan**: Diseñado para **rendimiento consistente**, con **trabajadores precalentados** para eliminar los inicios en frío. Ofrece **tiempos de ejecución extendidos, redes virtuales**, y soporta **imágenes de Linux personalizadas**, lo que lo hace perfecto para **aplicaciones críticas** que necesitan alto rendimiento y características avanzadas. - **Dedicated Plan**: Se ejecuta en máquinas virtuales dedicadas con **facturación predecible** y soporta escalado manual o automático. Permite ejecutar múltiples aplicaciones en el mismo plan, proporciona **aislamiento de computación**, y asegura **acceso seguro a la red** a través de App Service Environments, lo que lo hace ideal para **aplicaciones de larga duración** que necesitan asignación de recursos consistente. - **Container Apps**: Permite desplegar **aplicaciones de función en contenedores** en un entorno gestionado, junto con microservicios y APIs. Soporta bibliotecas personalizadas, migración de aplicaciones heredadas, y **procesamiento GPU**, eliminando la gestión del clúster de Kubernetes. Ideal para **aplicaciones escalables en contenedores impulsadas por eventos**. ### **Buckets de Almacenamiento** Al crear una nueva Function App no contenedorizada (pero proporcionando el código para ejecutar), los **códigos y otros datos relacionados con la función se almacenarán en una cuenta de almacenamiento**. Por defecto, la consola web creará una nueva por función para almacenar el código. Además, al modificar el código dentro del bucket (en los diferentes formatos en que podría estar almacenado), el **código de la aplicación se modificará al nuevo y se ejecutará** la próxima vez que se llame a la función. > [!CAUTION] > Esto es muy interesante desde la perspectiva de un atacante, ya que **el acceso de escritura sobre este bucket** permitirá a un atacante **comprometer el código y escalar privilegios** a las identidades gestionadas dentro de la Function App. > > Más sobre esto en la **sección de escalada de privilegios**. También es posible encontrar las **claves maestra y de funciones** almacenadas en la cuenta de almacenamiento en el contenedor **`azure-webjobs-secrets`** dentro de la carpeta **``** en los archivos JSON que puedes encontrar dentro. Ten en cuenta que Functions también permiten almacenar el código en una ubicación remota simplemente indicando la URL a la misma. ### Networking Usando un desencadenador HTTP: - Es posible dar **acceso a una función desde todo Internet** sin requerir ninguna autenticación o dar acceso basado en IAM. Aunque también es posible restringir este acceso. - También es posible **dar o restringir acceso** a una Function App desde **una red interna (VPC)**. > [!CAUTION] > Esto es muy interesante desde la perspectiva de un atacante, ya que podría ser posible **pivotar a redes internas** desde una Function vulnerable expuesta a Internet. ### **Configuraciones de Function App & Variables de Entorno** Es posible configurar variables de entorno dentro de una aplicación, que podrían contener información sensible. Además, por defecto se crean las variables de entorno **`AzureWebJobsStorage`** y **`WEBSITE_CONTENTAZUREFILECONNECTIONSTRING`** (entre otras). Estas son especialmente interesantes porque **contienen la clave de la cuenta para controlar con PERMISOS COMPLETOS la cuenta de almacenamiento que contiene los datos de la aplicación**. Estas configuraciones también son necesarias para ejecutar el código desde la cuenta de almacenamiento. Estas variables de entorno o parámetros de configuración también controlan cómo la Function ejecuta el código, por ejemplo, si **`WEBSITE_RUN_FROM_PACKAGE`** existe, indicará la URL donde se encuentra el código de la aplicación. ### **Sandbox de Funciones** Dentro del sandbox de linux, el código fuente se encuentra en **`/home/site/wwwroot`** en el archivo **`function_app.py`** (si se usa python) el usuario que ejecuta el código es **`app`** (sin permisos de sudo). En una **función de Windows** usando NodeJS, el código se encontraba en **`C:\home\site\wwwroot\HttpTrigger1\index.js`**, el nombre de usuario era **`mawsFnPlaceholder8_f_v4_node_20_x86`** y formaba parte de los **grupos**: `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`. ### **Identidades Gestionadas & Metadatos** Al igual que [**VMs**](vms/index.html), las Functions pueden tener **Identidades Gestionadas** de 2 tipos: Asignadas por el sistema y Asignadas por el usuario. La **asignada por el sistema** será una identidad gestionada que **solo la función** que la tiene asignada podrá usar, mientras que las identidades gestionadas **asignadas por el usuario** son identidades gestionadas que **cualquier otro servicio de Azure podrá usar**. > [!NOTE] > Al igual que en [**VMs**](vms/index.html), las Functions pueden tener **1 identidad gestionada asignada por el sistema** y **varias asignadas por el usuario**, por lo que siempre es importante intentar encontrar todas ellas si comprometes la función porque podrías ser capaz de escalar privilegios a varias identidades gestionadas desde solo una Function. > > Si no se usa una identidad gestionada por el sistema pero una o más identidades gestionadas por el usuario están adjuntas a una función, por defecto no podrás obtener ningún token. Es posible usar los [**scripts PEASS**](https://github.com/peass-ng/PEASS-ng) para obtener tokens de la identidad gestionada predeterminada desde el punto de metadatos. O podrías obtenerlos **manualmente** como se explica en: {{#ref}} https://book.hacktricks.wiki/en/pentesting-web/ssrf-server-side-request-forgery/cloud-ssrf.html#azure-vm {{#endref}} Ten en cuenta que necesitas encontrar una manera de **verificar todas las Identidades Gestionadas que una función tiene adjuntas** ya que si no lo indicas, el punto de metadatos **solo usará la predeterminada** (consulta el enlace anterior para más información). ## Claves de Acceso > [!NOTE] > Ten en cuenta que no hay permisos RBAC para dar acceso a los usuarios para invocar las funciones. La **invocación de la función depende del desencadenador** seleccionado cuando se creó y si se seleccionó un desencadenador HTTP, podría ser necesario usar una **clave de acceso**. Al crear un endpoint dentro de una función usando un **desencadenador HTTP**, es posible indicar el **nivel de autorización de clave de acceso** necesario para activar la función. Hay tres opciones disponibles: - **ANONYMOUS**: **Todos** pueden acceder a la función a través de la URL. - **FUNCTION**: El endpoint solo es accesible para usuarios que usan una **clave de función, host o maestra**. - **ADMIN**: El endpoint solo es accesible para usuarios con una **clave maestra**. **Tipo de claves:** - **Claves de Función:** Las claves de función pueden ser predeterminadas o definidas por el usuario y están diseñadas para otorgar acceso exclusivamente a **endpoints de función específicos** dentro de una Function App, permitiendo un acceso más granular sobre los endpoints. - **Claves de Host:** Las claves de host, que también pueden ser predeterminadas o definidas por el usuario, proporcionan acceso a **todos los endpoints de función dentro de una Function App con nivel de acceso FUNCTION**. - **Clave Maestra:** La clave maestra (`_master`) sirve como una clave administrativa que ofrece permisos elevados, incluyendo acceso a todos los endpoints de función (nivel de acceso ADMIN incluido). Esta **clave no puede ser revocada.** - **Claves del Sistema:** Las claves del sistema son **gestionadas por extensiones específicas** y son necesarias para acceder a los endpoints de webhook utilizados por componentes internos. Ejemplos incluyen el desencadenador de Event Grid y Durable Functions, que utilizan claves del sistema para interactuar de manera segura con sus respectivas APIs. > [!TIP] > Ejemplo para acceder a un endpoint de API de función usando una clave: > > `https://.azurewebsites.net/api/?code=` ### Autenticación Básica Al igual que en los App Services, las Functions también admiten autenticación básica para conectarse a **SCM** y **FTP** para desplegar código usando un **nombre de usuario y contraseña en una URL** proporcionada por Azure. Más información al respecto en: {{#ref}} az-app-services.md {{#endref}} ### Despliegues Basados en Github Cuando se genera una función a partir de un repositorio de Github, la consola web de Azure permite **crear automáticamente un flujo de trabajo de Github en un repositorio específico** para que cada vez que se actualice este repositorio, se actualice el código de la función. De hecho, el yaml de Github Action para una función de python se ve así:
Github Action Yaml ```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 }} ```
Además, se crea una **Identidad Administrada** para que la Acción de Github del repositorio pueda iniciar sesión en Azure con ella. Esto se hace generando una credencial Federada sobre la **Identidad Administrada** que permite al **Emisor** `https://token.actions.githubusercontent.com` y al **Identificador de Sujeto** `repo:/:ref:refs/heads/`. > [!CAUTION] > Por lo tanto, cualquier persona que comprometa ese repositorio podrá comprometer la función y las Identidades Administradas asociadas a ella. ### Implementaciones Basadas en Contenedores No todos los planes permiten desplegar contenedores, pero para aquellos que sí, la configuración contendrá la URL del contenedor. En la API, la configuración **`linuxFxVersion`** tendrá algo como: `DOCKER|mcr.microsoft.com/...`, mientras que en la consola web, la configuración mostrará los **ajustes de imagen**. Además, **no se almacenará código fuente en la** cuenta de almacenamiento relacionada con la función, ya que no es necesario. ## Enumeración {{#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 \ --resource-group # Get details about the source of the function code az functionapp deployment source show \ --name \ --resource-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 \ --resource-group # Get settings (and privesc to the sorage account) az functionapp config appsettings list --name --resource-group # Get access restrictions az functionapp config access-restriction show --name --resource-group # Check if a domain was assigned to a function app az functionapp config hostname list --webapp-name --resource-group # Get SSL certificates az functionapp config ssl list --resource-group # Get network restrictions az functionapp config access-restriction show --name --resource-group # Get acess restrictions az functionapp config access-restriction show --name --resource-group # Get connection strings az rest --method POST --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/connectionstrings/list?api-version=2022-03-01" az rest --method GET --uri "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//config/configreferences/connectionstrings?api-version=2022-03-01" # Get SCM credentials az functionapp deployment list-publishing-credentials --name --resource-group # Get function, system and master keys az functionapp keys list --name --resource-group # Get Host key az rest --method POST --uri "https://management.azure.com//resourceGroups//providers/Microsoft.Web/sites//functions//listKeys?api-version=2022-03-01" # Get source code with Master Key of the function curl "?code=" curl "https://.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=" -v # Get source code using SCM access (Azure permissions or SCM creds) az rest --method GET \ --url "https://.azurewebsites.net/admin/vfs/home/site/wwwroot/function_app.py?code=" \ --resource "https://management.azure.com/" # Get source code with Azure permissions az rest --url "https://management.azure.com/subscriptions//resourceGroups//providers/Microsoft.Web/sites//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 # 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 -Name # Retrieves the app settings for a specific Azure Function App. Get-AzFunctionAppSetting -Name -ResourceGroupName ``` {{#endtab }} {{#endtabs }} ## Escalación de Privilegios {{#ref}} ../az-privilege-escalation/az-functions-app-privesc.md {{#endref}} ## Referencias - [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}}