Delete and ReSAStore
A lesser-known risk of using Azure SAS
by Pallavi Sivakumaran
WIthSecure Intelligence
12 September 2023
Summary
Azure shared access signatures (SAS) allow for delegating access to storage resources within Azure. They provide fine-grained control over the resources that can be accessed, the type of access that is allowed, and the duration for which access is allowed. SAS come with the known security risk that “if a SAS is leaked, it can be used by anyone who obtains it, which can potentially compromise [the] storage account.” [1]
We discuss here a lesser-known risk, which we refer to as Use-after-Recreate in this post. This is the risk that, once a SAS token is generated, if the storage resource in question is deleted and a new one later created with the same name, the SAS token generated for the old resource will be valid for the new resource as well (as long as the expiration time of the SAS token hasn’t been reached).
This blog post provides a brief overview of SAS tokens and their structure, and describes the conditions that make it possible for a SAS token to be used even after the original resource it was associated with has been deleted.
Azure Storage
Azure Storage is a platform that provides different storage services, such as Blob, Table, Queue and File services. Each service is created within a storage account, where a single storage account can contain multiple services, and can be accessed via a REST API of the form: https://<storage_account_name>.<storage_service_type>.core.windows.net/
Blob Storage is particularly interesting, as it enables the storage of unstructured data (similar to S3 in AWS). With Blob Storage, data is stored as blobs in containers within the storage account. The URI for a data blob would be:
https://<storage_account_name>.blob.core.windows.net/<container_name>/<blob_name>
SAS Tokens
Shared access signatures are a simple mechanism for delegating access to storage resources such as a table, a container or even a single data blob.
A SAS token is a string specifying the type of resource that can be accessed, the level of access that is allowed, the validity period of the token, and possibly other controls. The most important part of the token is the signature, which is an HMAC-SHA256, computed over some of the preceding fields and then Base64-encoded.
Example of a SAS token:
sp=r&st=2023-06-06T10:38:47Z&se=2023-06-06T18:38:47Z&sv=2022-11-02&sr=c&sig=SIGNATURE
Here sp
refers to the permissions (r
=read
), st
is the validity start time, se
is the expiration, sv
is the version, sr
is the resource scope (c
=container
), and sig
is the signature. Different types of SAS may have different fields, each with several possible values [2][3][4].
There are three types of SAS: User delegation SAS, account SAS, and service SAS. These differ in terms of the key used to compute the signature, the level of access they enable, and the mechanisms for control that are available. With user delegation SAS, the key is created using Azure AD credentials, while account SAS and service SAS use a key associated with the storage account itself. Account SAS can enable access to multiple storage services at the same time, while service SAS provides access to a single service.
When a storage resource needs to be accessed, the resource URI, along with the entire SAS token, is used. For example, to read a blob named myblob.txt, which is stored in mycontainer within mystorageaccount, the request would be structured as follows:
https://mystorageaccount.blob.core.windows.net/mycontainer/myblob.txt?sp=r&st=2023-06-15T12:00:00Z&se=2023-06-15T20:00:00Z&sv=2022-11-02&sr=b&sig=SIGNATURE
Analysing SAS Tokens
By deliberately providing an incorrect signature in a request, we obtained the following error message:
<AuthenticationErrorDetail>Signature did not match. String to sign used was r 2023-06-15T12:00:00Z 2023-06-15T20:00:00Z /blob/mystorageaccount/mycontainer/myblob.txt https 2022-11-02 b </AuthenticationErrorDetail>
This indicates that Azure Storage computes a signature over some of the token fields as well as a restructured version of the resource URI, and checks whether the computed value matches the signature within the token. We later found details of the exact format of the string that is signed [2][3][4].
The remaining parameters within the token (such as resource scope, validity period, etc) would also need to be checked by Azure Storage, to ensure that the request is actually authorised at the time of the request, for the specified resource, before allowing access.
Stored Access Policies
Stored access policies are a mechanism in Azure for grouping together, and providing an additional layer of control over, service SAS [5]. The policies allow for changing the start time, expiration time and permissions associated with a SAS. They also allow for revocation of a SAS (by deleting the stored access policy) without the need to regenerate the storage account key. However, if the stored access policy is later recreated with the same name, or modified to add permissions or extend the expiration time, then the SAS will become usable again.
To control the use of a SAS via a stored access policy, the policy must be associated at the time of creation of the SAS. That is, you can't retroactively associate a stored access policy with a SAS after the SAS has been generated. Stored access policies also cannot be used with user delegation SAS or account SAS.
Known Risks with SAS
The fact that the SAS token is just a string with a signature computed over fixed string components gives rise to several risks.
In particular, it means an account SAS or a service SAS that is not associated with a stored access policy is always valid until either the expiry date specified during its generation is reached, or until the key used to compute the signature is regenerated. That is, there is no direct revocation for these types of SAS. User delegation SAS can be cancelled either by revoking the user delegation key or by changing/removing RBAC role assignments from the principal that was used to create the SAS. However, if the latter option is used, re-assigning the roles will re-enable access via the user delegation SAS.
There is also no log created when a SAS is generated, and no way to list existing SAS, which complicates auditing. The reason for this is possibly because a SAS can be generated offline, without any calls to Azure APIs, as it is simply an HMAC-SHA256 over a string of a defined format.
The Risk of Use-after-Recreate
The risks listed above have been discussed in forums and in Microsoft’s own documentation. However, the nature of SAS tokens gives rise to a concern that is not often discussed: this is the risk of a SAS token remaining valid even if the associated resource is deleted and another resource is created with the same name.
For example, let’s say there is a storage account called mystorageaccount
, with a container mycontainer
. A container-level SAS token is generated with a validity start time of 2023-01-01 and expiry time of 2023-12-31. However, in June 2023, the container is deleted. A natural expectation would be that all SAS tokens for that container are invalidated and cannot be used any more.
However, we have found that if a new container called mycontainer
is later created within mystorageaccount
, the previously generated SAS token will remain valid until its expiry. This means that some users could gain access to resources that were never intended for them. And since SAS tokens can allow for different types of access, including resource creation and deletion, this runs the risk of information disclosure and tampering.
We have tested this against Blob Storage and found that if a container is deleted and recreated with the same name, then previously created container-level SAS will still be usable with the new container. If a blob is deleted and recreated with the same name, then previously created container-level and blob-level SAS will be usable with the new blob. This is true regardless of whether the SAS is an account, service or user delegation SAS. However, if a storage account is deleted and recreated with the same name, then any existing account or service SAS will not work, as a storage account is always generated with fresh keys and signatures computed with the old key would no longer be valid. We found that user delegation SAS also stopped working if a storage account was deleted and recreated, indicating that the user delegation keys associated with a storage account may be getting revoked when the storage account is deleted.
Attack Scenario (credit: Roberto Jordaney)
While SAS tokens are recommended to be used for short-term access and/or associated with a stored access policy, we nevertheless highlight the possibility of attack with long-term ad-hoc SAS tokens using the following scenario as an example:
We consider an example application that allows customers to upload their photos and files, to be stored in a container (within a single storage account) that is named based on the customer's name. For example, Robert Smith would be assigned a container named rsmith.
The customers are provided with ad-hoc SAS tokens that are valid for fairly long periods of time.
An attacker could potentially register under multiple usernames (causing containers to be created for each one) and obtain valid SAS tokens for each name. They could then cancel their registrations, thereby also causing the associated containers to be deleted (and freeing up the name(s) for future customers).
If the usernames chosen by the attacker occur with high probability among the general population, then it is likely that at least a few new customer registrations will result in the same (container) names as previously registered by the attacker. The attacker would therefore be able to use the collected SAS tokens to read and potentially also delete files belonging to legitimate customers.
Rotating the storage account keys would be undesirable in this scenario because it would impact a large number of customers.
Prevention and Remediation
Prevention and remediation for the Use-after-Recreate issue is much the same as for other concerns relating to SAS.
For existing account SAS and for service SAS that are not associated with a stored access policy, the only recourse is to rotate the storage account key. While Microsoft warns that this can impact other applications that depend on the storage account key, it also recommends rotating storage account keys on a regular basis and avoiding hardcoded SAS tokens/access keys within applications [6].
For existing service SAS that are associated with a stored access policy, modify the policy expiration time (such that the SAS will expire immediately) or remove permissions from the policy, or delete the policy entirely. Note that if the stored access policy is later re-modified or recreated (with the same name) with sufficient permissions and new expiration, then the service SAS will become usable again. Take steps to avoid this by always using unique names for stored access policies, i.e., don't create one with the same name as a previously deleted policy.
For existing user delegation SAS, revoke and regenerate the user delegation key.
For new SAS tokens, Microsoft recommends user delegation SAS over account or service SAS [1]. However, this type of SAS is only available for Azure Blob Storage and Azure Data Lake Storage Gen2 [4]. For other types of storage, or if user delegation SAS is not possible for some reason, specify short validity periods and associate the SAS tokens with a stored access policy.
Possible Recourse for Microsoft
We raised the concern of Use-after-Recreate to the Microsoft Security Response Centre. The case was resolved by MSRC as it did not meet the bar for servicing by their security team. However, they mentioned that the information has been passed internally to the relevant teams.
The fundamental reason for the Use-after-Recreate issue is that a signature computed over the same string using the same key will always be the same. We present the following as a potential way of resolving the issue: instead of computing the SAS over storage resources' names, Microsoft could assign the highest-level resources within a storage account (such as containers and tables) a unique identifier during their creation, and compute the SAS over this value. This way, even if the resource is deleted and a new one later created with the same name, the unique identifier of the new resource would be different and would therefore result in a different signature; all old SAS would be invalid and unusable with the new resource.
Future Work
Most of our testing was performed against Blob Storage. However, looking at the computation of SAS for Tables and Files suggests that they too would exhibit the same problem. There is also the possibility that similar storage access mechanisms in other cloud platforms, such as pre-signed URLs in AWS, may suffer from the same issue. These are all potential avenues for future research.
References:
[2] https://learn.microsoft.com/en-us/rest/api/storageservices/create-service-sas
[3] https://learn.microsoft.com/en-us/rest/api/storageservices/create-account-sas
[4] https://learn.microsoft.com/en-us/rest/api/storageservices/create-user-delegation-sas
[5] https://learn.microsoft.com/en-us/rest/api/storageservices/define-stored-access-policy
[6] https://learn.microsoft.com/en-us/azure/storage/common/storage-account-keys-manage