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.
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:
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:
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:
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
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:
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!