New toxic combination of default and common configurations in Azure: How to investigate in JupiterOne

by

Thanks to Chad Richts and Austin Kelleher for their assistance co-authoring this article.

Hey there JupiterOne security community and Azure customers! We have important information and investigative tools to share about a prevalent configuration risk with Azure Storage Accounts. Our partners at Orca have figured out how to exploit Azure Functions to acquire access tokens and gain higher privileged access, which could allow attackers to access important business assets or even execute remote code.

When this issue was discovered, it was shared with the Microsoft Security Response Center, which acknowledged that it's an important problem, but it's not exactly a vulnerability. Rather, it's a flaw in the design that can't be trivially changed. That means that the danger is still there, and although Microsoft wants to change the default behavior, there is no timeline as of this writing.

Microsoft previously warned that using access keys for storage authorization can be risky, especially if you need more precise access control. Even though they don't recommend it, Shared Key authorization is turned on by default for all Azure Storage Accounts.

To reduce the risk of this issue, organizations can try disabling Azure Shared Key authorization where possible, and use Azure Active Directory authentication instead. Below you can find JupiterOne queries to determine if you are exposed to the published attack path.

But first…

What is the issue?

We are going to get a bit technical here and provide an overview of the issue. If you are only interested in finding out whether you might be affected, you can skip ahead to the ‘Finding the issue in your Azure environment’ section.

Azure Storage Accounts can be accessed in multiple ways, including through granular permissions using Azure AD credentials or Shared Keys. Shared Keys are widely used because they are easy to set up, but they come with a significant drawback - they provide complete access to both the management of Storage Accounts and the data stored within them.

According to Microsoft, "Storage account access keys provide full access to the configuration of a storage account, as well as the data. Always be careful to protect your access keys."

The reason this is an issue is because many built-in roles, as well as custom roles, use the Microsoft.Storage/storageAccounts/listkeys/action permission to provide access to data in a Storage Account. This means that if a role has this permission and the Storage Account has Shared Keys enabled (which it does by default), the role will have access to both the management of and data within the Storage Account. In fact, if a user tries to access a Storage Account without the correct permissions, they will be prompted to provide the listKeys action permission.

This means that users may inadvertently assign an action that provides far more permissive access than they intended. For example, the built-in Storage Account Contributor role should not have access to data in the Storage Account because it does not have any DataAction permissions. However, if the Storage Account has access keys enabled, the role can still read and write data because of the Microsoft.Storage/storageAccounts/listkeys/action permission.

The issue is made worse by the fact that Azure Function Apps code is stored in Storage Accounts, and Azure Functions often require more privileged roles to perform their tasks. As a result, a user who is only intended to have read access to a blob may inadvertently gain access to more privileged roles by being able to modify the code running on an Azure Function.

While exploiting this issue requires a combination of specific settings and relationships, it is a common issue that can have serious security implications. JupiterOne can help identify if you are at risk by navigating these settings and relationships. 

Finding the issue in your Azure environment

Let’s look at how you can use JupiterOne to identify whether you are exposed to this issue.

Azure Storage Accounts with AllowSharedKeyAccess Enabled

The allowSharedKeyAccess property has been added in JupiterOne for Azure storage accounts. Because this property only exists if it is explicitly set on the storage account, many times the value will be undefined (null). Also, since Shared Key access is allowed by default, both undefined (null) and true values for this property are equivalent. Shared Key access is allowed for a storage account unless explicitly disabled, allowSharedKeyAccess = false.

Query to find which storage accounts allow Shared Key access

It would not be surprising if the following query returned every storage account in your environment, because Shared Key access is allowed for a storage account unless explicitly disabled:

FIND azure_storage_account WITH allowSharedKeyAccess != false

Query to find storage accounts that allow Shared Key access explicitly

The following query should help identify situations where your storage accounts were intentionally allowed Shared Key access for valid reasons as opposed to default behavior:

FIND azure_storage_account WITH allowSharedKeyAccess = true

Query to find the storage accounts that allow Shared Key access by default

If it turns out you are only interested in storage accounts that were unintentionally allowing Shared Key access due to the default behavior, this query would be most succinct:

FIND azure_storage_account WITH allowSharedKeyAccess = undefined

Azure users with listKeys permissions

Going a bit deeper, it may help isolate the specific Azure users who have the ability to list storage account keys because that specific permission was key (pun intended; forgive me) to the published attack path.

Query to find which active Azure users can list storage account keys

FIND azure_user WITH active = true
THAT ASSIGNED azure_role_assignment THAT USES azure_role_definition WITH actions $= "/storageAccounts/listKeys/action" /* Look for wildcard actions, "Owner" role for example */ OR actions = "*"
RETURN
azure_user.displayName, azure_user.email, azure_user.userType, azure_role_definition.id, azure_role_definition.name, azure_role_definition.roleName, azure_role_definition.description, azure_role_definition.actions, azure_role_definition.webLink

Query to find which active Azure users have privileged access, and can list storage account keys

Taking this further, we can begin isolating the situations with the highest impact or blast radius by filtering on the Azure role assignment’s RBAC scope. The higher scope, the more access this user will have when leveraging the role. 

For example, if the role assignment is at the “Management Group” scope, the role assignment could apply to multiple subscriptions, and therefore multiple resource groups, and therefore many resources:

FIND azure_user WITH active = true
THAT ASSIGNED azure_role_assignment /* Management group scope is the highest level */ WITH scope ~= "/managementGroups/" /* Check for the subscription scope or the resource group scope. Do not consider the resource scope */ OR (scope ^= "/subscriptions/" AND scope !~= "/providers/") THAT USES azure_role_definition WITH actions $= "/storageAccounts/listKeys/action" /* Look for wildcard actions, e.g. "Owner" role */ OR actions = "*"
RETURN
azure_user.displayName, azure_user.email, azure_user.userType, azure_role_definition.id, azure_role_definition.name, azure_role_definition.roleName, azure_role_definition.description, azure_role_definition.actions, azure_role_definition.webLink

Conclusions

To summarize, here are the key points you should take away from this article.

  • Just because your Azure Storage Account has AllowSharedKeyAccess enabled by default or explicitly, does not necessarily mean you have any active threats.
  • Configuring AllowSharedKeyAccess to be disabled for your storage accounts is a great blanket protection. The prevalence of storage accounts with AllowSharedKeyAccess enabled will be high.
  • Using queries to determine whether there are valid uses of Shared Key access that need to be maintained, and whether there are principals who can assume roles that enable them to list access keys, is important.
  • Understanding the blast radius/impact that those principals could have if compromised is even more compelling.

Thanks for tuning in!

Akash Ganapathi
Akash Ganapathi

Akash Ganapathi comes from an enterprise security, data privacy, and data analysis background, working exclusively in the B2B software solutions space throughout his career. He is currently a Principal Security Solutions Architect at JupiterOne.

Keep Reading

Introducing Continuous Controls Monitoring (CCM) | JupiterOne
November 7, 2024
Blog
Introducing Continuous Controls Monitoring (CCM)

CCM delivers real-time visibility, proactive risk management, and streamlined compliance for security.

Now Available: JupiterOne’s Public Postman Workspace | JupiterOne
October 31, 2024
Blog
Now Available: JupiterOne’s Public Postman Workspace

Explore JupiterOne’s Public Postman Workspace to streamline your workflows and enhance your security operations.

Prioritizing Exploitable Vulnerabilities to Protect Your Business Critical Assets | JupiterOne
October 16, 2024
Blog
Prioritizing Exploitable Vulnerabilities to Protect Your Business Critical Assets

Vulnerability scanners flood teams with alerts, but CTEM helps prioritize based on exploitability and business impact, ensuring focus on the most critical threats.

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.

15 Mar 2022
Blog
One line headline, one line headline

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud eiut.