This article is part of a blog series where I explain common Azure vulnerabilities, how to create a lab such that it reproduces the issues, and how to exploit it. To follow this tutorial, you’ll need an Azure account and Azure CLI tool installed on your machine both of which you can get for free.
- You can can create an account for free at https://azure.microsoft.com/
- You can install Azure CLI from https://docs.microsoft.com/en-us/cli/azure/install-azure-cli
- You can find the source code of the challenges at https://github.com/andrei8055/Azure-security-challenges
- Consider getting Azure certified? Check my article on how to get Certified Azure Red Team Proffessional
- Part #1: Create an Azure Vulnerable Lab: Part #1 – Anonymous Blob Access
- Part #2: Create an Azure Vulnerable Lab: Part #2 – Environment Variables
- Part #3: Create an Azure Vulnerable Lab: Part #3 – Soft Deleted Blobs
1. Managed Identities
Using Managed Identities it is possible to grant a resource (such as VM/WebApp/Function/etc) access to other resource (such as Vaults/Storage Accounts/etc.) For example, if we want to give our web application access to a private storage account container without having to deal with how we safely store connection strings in config files or source code, we could use a managed identity.
First we enable the managed identity for the web application:
Once enabled, we are given the possibility to configure the roles assigned for this identity (i.e: permissions granted to the service that we enabled the identity for).
Under each role, we can see in details what permissions are included. Azure allows also to configure custom roles in case the built-in ones are not suitable for your case.
Similarly, to see who has permissions granted for a give resource, we can check that under the Access Control (IAM) -> View access to this resource.
So in our case, we should see under the Storage Account that the web application has Reader and Data Access:
Now that we have the basics of how Managed Identity works, let’s see how can we exploit this. Since the web application has access to the storage account, and we compromised the web application we should be able to get as well access to the storage account. Long story short, we get the same permissions that the resource we compromised had. Based on how poorly the Identity roles are assigned, it can be the case that the permissions are assigned at Subscription level, effectively granting us access to all resources inside it!
While in our case it looks that the permissions are proper (we are limiting access only to the Storage Account that we need access to) and limit the roles to Reader and Data Access (instead of Contributor or Owner), there is still a catch. Our web app needs permissions only to the “images” container, but the managed identity configured has enough permissions to list the access keys to the whole Storage Account granting us access to any other containers hosted on the same account.
2. Creating a vulnerable Managed Identity
As usual, we start by creating a storage account, and add 2 private containers inside it: first the “images” container used by our web app to display some dummy data, and the “flag” container that contains some sensitive data. The “flag” container is not even referenced in our web application.
##Create storage account az storage account create --resource-group $resourceGroupName --name $blobStorageAccName --location $location --sku $storageAccType --access-tier Cool ##Create containers az storage container create --name $flagContainerName --public-access "off" --account-name $blobStorageAccName az storage container create --name $imagesContainerName --public-access "off" --account-name $blobStorageAccName
Afterwards, we push the dummy data to the “images” container, and the flag.txt to the “flag” container.
##Upload blob flag az storage blob upload --account-name $blobStorageAccName --container-name $flagContainerName --file ".\flags\flag.txt" --name "flag.txt" ##Upload dummy images az storage blob upload --container-name $imagesContainerName --account-name $blobStorageAccName --file ".\resources\1.png" --name "1.png" az storage blob upload --container-name $imagesContainerName --account-name $blobStorageAccName --file ".\resources\2.png" --name "2.png" az storage blob upload --container-name $imagesContainerName --account-name $blobStorageAccName --file ".\resources\3.png" --name "3.png"
After we deploy the app and configure the “CTF” firewall such that is only access from our IP address, we assign the Managed Identity to the web application. As shown earlier, we will give our web app the “Reader and Data Access” role:
##Get Storage Account ID $storageAccountID = az storage account show --name $blobStorageAccName --query id -o tsv ##Assign web app identity az webapp identity assign -g $resourceGroupName -n $webappName --role "Reader and Data Access" --scope $storageAccountID
Checking the web app source code, we can see that is trying to access the “images” container, but there are no credentials or connection strings that we can use, and no mention of the “flag” container. In fact, the developer of this app might have no idea about the existence of other containers as it may be someone else granting the appropriate role for his application to run:
@app.route('/files', methods = ['POST', 'GET']) def files(): account_url = "https://0xpwnstorageacc.blob.core.windows.net" container_name = "images" creds = DefaultAzureCredential() blob_service_client = BlobServiceClient(account_url=account_url, credential=creds) container_client = blob_service_client.get_container_client(container_name) blobs_list = container_client.list_blobs() output = "" for blob in blobs_list: output = output + blob.name + '\n' return output
3. Exploiting Azure Managed Identity
Abusing the command injection on the web app, we can make a curl request to the $IDENTITY_ENDPOINT URL stored in the environment variables and get an access token and account id (client id in the response) which can be used to authenticate to Azure.
curl "$IDENTITY_ENDPOINT?resource=https://management.azure.com/&api-version=2017-09-01" -H secret:$IDENTITY_HEADER
Using the Azure Powershell module, we can connect to Azure with the access token:
PS> Install-Module -Name Az -Repository PSGallery -Force PS> Connect-AzAccount -AccessToken <access_token> -AccountId <client_id>
Once connected, you should see details about the Subscription and Tenant that the Managed Identity we are impersonating has access to. Using the Get-AzResource Azure Powershell cmdlet, we can check which resources inside the subscription we can access:
To list the roles assigned to the managed, we can use the Azure Powershell cmdlet Get-AzRoleAssignment. This cmdlet requires additionally a graph token which we can get from the https://graph.microsoft.com/ endpoint, but also the permission to list roles and permissions for identities which our Identity does not have.
However, we can still try to access the Storage Account keys without these permissions and see if we are successful. For that we will use the Get-AzStorageAccountKey cmdley with the Resource Group Name and Account Name that we found in the previous step.
Get storage account keys:
>Get-AzStorageAccountKey -ResourceGroupName "0xpwnlab" -AccountName "0xpwnstorageacc" KeyName Value Permissions CreationTi me ------- ----- ----------- ---------- key1 L175hccq[...]lhMVTfTH9DJiQ== Full 3/12/20... key2 vcZiPzJph[...]ZkS7bAYkKvA== Full 3/12/20...
If the command returns two keys, than it means that our identity had permissions to list them. Let’s use these keys in azure storage explorer and see if there are other containers stored on the same account. In the azure storage explorer, we click the connect icon and select storage account or service
On the second step, this time we select the Account name and key option:
For the Account name we use the name that we enumerated in the Get-AzResource step, while for the key we can use either of the two we found:
Once we connect, on the left side menu we should find a new storage account, we 2 containers: the images container used by the web app, but also a new one containing the flag.
And that’s it! We just seen how abusing a command injection into a web app we discovered that it had a managed identity associated to it. After we got the JWT access token, we connected to the Azure Powershell and enumerated the resources that we have access to. The improper permissions set for the managed identity allowed us to read the access key for the whole Storage Account and discover another private container that was not referenced anywhere, containing the flag sensitive information.
Want to know more about similar topics? I’m available for freelance projects and mentorship classes on Mentorcruise: