Techniques for enumerating cloud resources have had extensive coverage over the years. Most of the existing techniques focus on subdomain enumeration, or identification of hosts in IP spaces associated with cloud providers. Additionally, very few of the prior blogs have focused on confirming the actual owners of cloud resources. Most of the existing research has typically said that there is no way to determine who the real owner of the resource is. 

As it turns out, there are ways to determine resource ownership for some Azure resource types. While it doesn’t apply to every Azure service, many services that support Entra ID authentication expose the hosting tenant ID as part of the authentication process. The tenant ID can then be reversed to full domain names associated with the tenant. If this is done at scale, enumeration and attribution can be used to map out resources owned by a specific organization. 

TL;DR 

  • Many Azure resources utilize subdomains for resource addressing 
  • These resources can be discovered through DNS hostname enumeration 
  • A subset of Azure resource types that support Entra ID authentication will expose their tenant ID through the authentication process 
  • An attacker can abuse this exposure to map resources back to specific tenants 
  • We wrote a tool (ATEAM) to automate this process 

Existing Azure Resource Enumeration Techniques 

As previously covered on the NetSPI blog, Azure makes significant use of subdomains for addressing resources. We can use Storage Accounts as an example. If we create a $StorageName Storage Account, the following DNS names will be reserved: 

  • $StorageName.blob.core.windows.net 
  • $StorageName.file.core.windows.net 
  • $StorageName.table.core.windows.net 
  • $StorageName.queue.core.windows.net 

Furthermore, if we configure the blob storage component to allow for static web hosting, an additional “web” subdomain will be reserved: 

  • $StorageName.z4.web.core.windows.net 
$StorageName.z4.web.core.windows.net

The story is the same for many other Azure resource types. There is typically one main subdomain used for the resource, with multiple auxiliary domains associated with it. These auxiliary domains can include regional (westus-01, canadaeast, etc.) and capacity (z1,z2, z3, etc.) reservations as well, so this can widen out the DNS enumeration surfaces.

 To find potential resources connected to an organization, we’ve traditionally used these hostnames/subdomains in combination with public DNS resources (VirusTotal), certificate records (crt.sh), and brute-force techniques to find live DNS records. Tools like bbot make this process significantly easier, as they can gather subdomains from multiple different sources.

blog.core.windows.net

As attackers, we have had to make some assumptions that these live records ($StorageName.blob.core.windows.net) could be tied to a specific target, based on the keywords (company/software name) used for naming.

Until recently, a resource name was not proof of ownership. For Example – the “AWS” Storage Account (in Azure) does not belong to AWS, the company.

Attribution of Azure Resources

Outside of having a custom domain name (IE: storageaccount.netspi.com) pointing to an Azure resource, or an SSL certificate being served, we have not had a reliable way of determining if a resource is associated with a specific tenant. In this blog, we will introduce an option that should be applicable to multiple different Azure services, along with a tool to automate the discovery and attribution process.

To start things off, we will show how this technique works for the Storage Account service. We will use an arbitrarily named Storage Account (0752a779955f4cbda44468) in a tenant (Darksideops.cloud) that we control. For context, the Storage Account does not have any public containers, and it is set up as a private endpoint, so it should have zero public exposure (outside of a DNS name).

We can use the following PowerShell code to make a web request to the Storage Account with the “x-ms-version” header and the “comp=blobs” API method.

try {
    Invoke-RestMethod -Uri "https://0752a779955f4cbda44468.blob.core.windows.net/?comp=blobs" -Method Get -Headers @{"x-ms-version"="2019-12-12"}
}
catch {
    $_.Exception.Response.Headers
}
Key Value
--- -----
x-ms-error-code {NoAuthenticationInformation}
WWW-Authenticate {Bearer authorization_uri=https://login.microsoftonline.com/977e0660-d4d3-4752-a79d-3ac9c4dbcf19/oauth2/authorize resource_id=https://storage.azure.com} 

Date {Sun, 19 Jan 2025 16:02:50 GMT 

To make this a little clearer, we’ve included the raw HTTP requests and responses:

HTTP Request:

GET /?comp=blobs HTTP/1.1 
x-ms-version: 2019-12-12 
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.19041.6093 
Host: 0752a779955f4cbda44468.blob.core.windows.net 
Connection: keep-alive 

HTTP Response:

HTTP/1.1 401 Server failed to authenticate the request. Please refer to the information in the www-authenticate header. 
Content-Length: 303 
Content-Type: application/xml 
Server: Windows-Azure-Blob/1.0 Microsoft-HTTPAPI/2.0 
x-ms-error-code: NoAuthenticationInformation 
x-ms-request-id: 32efc751-401e-0024-736d-f77f1b000000 
x-ms-version: 2019-12-12 
WWW-Authenticate: Bearer authorization_uri=https://login.microsoftonline.com/977e0660-d4d3-4752-a79d-3ac9c4dbcf19/oauth2/authorize resource_id=https://storage.azure.com 
Date: Sun, 19 Jan 2025 16:02:50 GMT 

We can see in the response that the server responds with an error and prompts with a WWW-Authenticate header. This header contains a login.microsoftonline.com link that includes the tenant ID (977e0660-d4d3-4752-a79d-3ac9c4dbcf19) of the owning tenant. Now that we have the tenant ID, we can use the Graph API (/v1.0/tenantRelationships/findTenantInformationByTenantId(tenantId='$tid')) to get the associated tenant domain. 

Tenant Domain Lookup Via Graph Explorer: 

Another thing to note is that this example shows that the technique works for Storage Accounts that are configured as private endpoints. While the account itself does not technically have any external exposures (files/folders/etc.), the DNS name is publicly enumerable, and the same Storage Account APIs are still available for it. This means that we can still identify the account and complete attribution against it. This concept (and the ability to find private endpoint attribution) also applies to multiple other services as well.

We’ve compiled a list of Azure services that are impacted by the attribution issue and highlighted them in the following table:

Resource TypeSubdomainsEnumeration TechniqueAttribution Technique
Key Vaultsvault.azure.netSubdomain Enumeration“WWW-Authenticate” Header
Storage Accountsblob.core.windows.netSubdomain Enumeration“WWW-Authenticate” Header
SharePointsharepoint.comSubdomain Enumeration“Report-To” Header
App Servicesazurewebsites.net
scm.azurewebsites.net
Subdomain Enumeration“Location” Header in Redirect
DataBricksazuredatabricks.netSubdomain Enumeration“Location” Header in Redirect
Azure Machine Learninginstances.azureml.msSubdomain Enumeration“Location” Header in Redirect
DevOpsdev.azure.comDirectory Enumeration“WWW-Authenticate” Header
Azure Provider Stringmanagement.azure.comResource String Disclosure“WWW-Authenticate” Header

It should be noted that the Azure Provider String listed above refers to the Azure management API URL that’s used for referencing an individual resource.

Example:
https://management.azure.com/subscriptions/155c4768-b71c-4e4b-a990-97407f43edda?api-version=2022-12-01

These strings can often be found in source code files or exposed through screenshots. If you’re a security researcher worried about exposing any of your tenant information, you might want to make sure that you’re fully redacting subscription IDs in your screenshots.

One last thing to note is the redirection to the “common” tenant that we see for some resources (primarily App Services). While it seems like a dead end, it is possible to determine the actual tenant for these “common” redirections. Depending on if you approach the resource with or without an active Entra ID session, you will get a different response. An unauthenticated session will give you the “common” tenant, while an authenticated session (ESTSAUTHPERSISTENT cookie present) will start a different redirection flow that exposes the tenant ID of the resource.

It should be noted that we don’t currently support credentials in the ATEAM tool (see below), but they could be added to help support unmasking the “common” tenant resources.

Introducing ATEAM

Given the utility of these techniques, we created a tool to enumerate and attribute Azure resources at scale.
We call it ATEAM – or Azure Tenant Enumeration and Attribution Module

The tool utilizes the methods outlined above for each of the impacted services to find and attribute live resources. For every resource that it finds attribution for, the tool stores a timestamped record in a local sqlite database (azure_tenants.db). The tool also allows for CSV, JSON, and HTML output of the results.

While we’re typically a PowerShell shop for Azure tools, this one made sense to write in Python. The large-scale DNS resolution and the use of a SQLite database were not really compatible with PowerShell.

It should be noted that for each live DNS record, there will typically be two HTTP requests that are sent. One to collect the tenant ID and another to get the company name for the tenant. Otherwise, this is a fairly simple process for each of the resource types that are supported.

Here’s an example HTML report of some discovered resources:

Usage notes:

The tool supports looking up individual resources (-r “myapp”), multiple keywords (-r “myapp” “myapp2” “myapp3”), or a list of keywords from a file (-f keywords.txt).

Additionally, we support generating permutations (prefix and suffix, with and without dashes) on the keywords with the -p flag. There is a default permutations.txt file in the repository that is used for that generation.

The tool also allows you to scale up the number of workers (-w 10) to allow for higher throughput.

You can find the tool in the NetSPI GitHubhttps://github.com/NetSPI/ATEAM 

Results

As part of this research, we compiled a list of observed hostnames under different Azure subdomains. Since there is frequently an overlap of resource names between different services, we thought it would be useful to pull resource names from as many Azure subdomains as possible. This resulted in around 1 million total keywords that could be used for finding and attributing Azure resources.

After enumerating the potential hostnames, we completed DNS enumeration against the vulnerable services to identify the potential attack surface area. This resulted in the following breakdown of live resources by service type:

MSRC Timeline

As part of this research, we reported multiple issues to MSRC. After the three initial reports were closed, we created a combined report, in an attempt to show the wider impact of the issues.

Storage Account Attribution

  • Mar 27, 2024 – Initial Report Date
  • Apr 23, 2024 – Case Close Date
  • Status – Complete, “by-design”

Key Vault Attribution

  • Mar 27, 2024 – Initial Report Date
  • May 28, 2024 – Case Close Date
  • Status – Complete, “working as intended”

App Service Attribution

  • Mar 27, 2024 – Initial Report Date
  • May 22, 2024 – Case Close Date
  • Status – Complete, “working as intended”

Combined Report – Azure Tenant Enumeration

This issue was reported to MSRC and we worked with MSRC on Coordinated Vulnerability Disclosure leading up to the presentation.

  • Feb 14, 2025 – Initial Report Date
  • Feb 26, 2025 – Case Close Date
    • Status – Complete, “valid, but does not pose an immediate threat”
  • Feb 28, 2025 – Coordinated Vulnerability Disclosure process begins
  • July 2025 – Coordination with MSRC and Product Team
“We appreciate your effort in bringing this to Microsoft’s attention. Upon reviewing the collection of these cases together, we have re-evaluated the ‘by design’ closure of these prior cases, and are investigating approaches to defend against these classes of reconnaissance techniques.”
Microsoft’s Response

Conclusion

This research was a year in the making. We initially started discovery on the attribution methods in Q2 of 2024 and slowly progressed the research over the course of a year. Although we had some hurdles, we ultimately were able to get the issues communicated to the right people and were able to publish this research. We want to express our appreciation for all the MSRC folks that looked at our tickets, the individuals that advocated for those issues, and the countless NetSPI co-workers that we kept showing this to over the last year.

Previous Research

As mentioned at the start, there have been multiple other research projects in this space, we’ve included several of them here:


Find Unexpected Cloud
Resource Exposures