Query Details

RULE 13 Storage Anonymous Public Access

Query

// Rule    : Azure - Storage Account Accessed Anonymously or via SAS from Public Internet
// Severity: Medium
// Tactics : Collection, Exfiltration
// MITRE   : T1530
// Freq    : PT1H   Period: PT2H
//==========================================================================================

let PrivateRanges = dynamic(["10.", "172.16.", "172.17.", "172.18.", "172.19.", "172.20.",
    "172.21.", "172.22.", "172.23.", "172.24.", "172.25.", "172.26.", "172.27.", "172.28.",
    "172.29.", "172.30.", "172.31.", "192.168.", "168.63.", "169.254."]);
let SensitiveOps = dynamic(["GetBlob", "ListBlobs", "GetBlobProperties",
    "GetContainerProperties", "ListContainers", "GetContainerACL"]);
// Minimum thresholds — tune per environment
let AnonAccessThreshold = 5;
let SASBytesGBThreshold = 0.05;  // 50 MB via SAS triggers review
StorageBlobLogs
| where TimeGenerated > ago(2h)
| where AuthenticationType in~ ("Anonymous", "SasToken")
| where OperationName in (SensitiveOps)
| where StatusCode == 200
| where isnotempty(CallerIpAddress)
| where not(CallerIpAddress has_any (PrivateRanges))
| summarize
    AccessCount      = count(),
    DistinctBlobs    = dcount(ObjectKey),
    DistinctContainers = dcount(tostring(split(ObjectKey, "/")[0])),
    TotalBytesGB     = round(sum(ResponseBodySize) / 1073741824.0, 3),
    Operations       = make_set(OperationName, 5),
    StorageAccounts  = make_set(AccountName, 5),
    FirstAccess      = min(TimeGenerated),
    LastAccess       = max(TimeGenerated)
    by CallerIpAddress, AuthenticationType
| where (AuthenticationType =~ "Anonymous" and AccessCount >= AnonAccessThreshold)
    or  (AuthenticationType =~ "SasToken"  and TotalBytesGB >= SASBytesGBThreshold)
| extend
    CallerIP   = CallerIpAddress,
    IsAnonymous = AuthenticationType =~ "Anonymous"

Explanation

This query is designed to identify potentially suspicious access to Azure Storage Accounts from the public internet. It focuses on access that is either anonymous or uses a Shared Access Signature (SAS) token. Here's a simplified breakdown of what the query does:

  1. Private IP Ranges and Sensitive Operations: It defines a list of private IP address ranges and sensitive operations related to accessing storage blobs and containers.

  2. Thresholds: It sets thresholds for triggering alerts:

    • Anonymous access must occur at least 5 times.
    • SAS token access must involve transferring at least 50 MB of data.
  3. Data Source: The query examines logs from the last 2 hours (StorageBlobLogs) to find relevant access events.

  4. Filtering Criteria:

    • It looks for access using either "Anonymous" or "SasToken" authentication.
    • It focuses on sensitive operations that were successful (status code 200).
    • It excludes access from private IP addresses.
  5. Data Aggregation:

    • It counts the number of accesses, distinct blobs, and containers accessed.
    • It calculates the total data transferred in gigabytes.
    • It collects a set of operations performed and storage accounts accessed.
    • It records the first and last access times for each IP address.
  6. Alert Conditions:

    • An alert is triggered if anonymous access meets or exceeds the threshold of 5 accesses.
    • An alert is also triggered if SAS token access involves transferring 50 MB or more.
  7. Output: The query outputs details about the suspicious access, including the caller's IP address, whether the access was anonymous, and other relevant metrics.

Overall, this query helps identify potential data exfiltration or unauthorized access attempts to Azure Storage Accounts from the public internet, which could indicate a security risk.

Details

David Alonso profile picture

David Alonso

Released: March 12, 2026

Tables

StorageBlobLogs

Keywords

AzureStorageAccountAccessInternetLogsOperationsBlobsContainersBytesIPAddressTimeAuthenticationResponseSize

Operators

letdynamicin~in==isnotemptynothas_anysummarizecountdcounttostringsplitroundsummake_setminmaxby=~orextend

Actions