Create an Azure Vulnerable Lab: Part #4 – Managed Identities

Want to know more about similar topics? I’m available for freelance projects and mentorship classes on Mentorcruise: MentorCruise

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.

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).

Lastly, we assign one or more roles (which is a set of permissions) for that identity. A role can be assigned assigned at Subscription level, Resource group, Storage Account, Vault or SQL and it propagates “downwards” in the Azure architecture layer.

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: MentorCruise

4 thoughts on “Create an Azure Vulnerable Lab: Part #4 – Managed Identities”

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s