← Back to SOC feed Coverage →

Storage Alerts Correlation with CommonSecurityLogs & AuditLogs

kql MEDIUM Azure-Sentinel
T1190T1078
AuditLogsCommonSecurityLogSecurityAlert
huntingmicrosoftofficial
This rule was pulled from an open-source repository and enriched with AI. Validate in a test environment before deploying to production.
View original rule at Azure-Sentinel →
Retrieved: 2026-06-03T23:00:00Z · Confidence: medium

Hunt Hypothesis

Adversaries may be exfiltrating data through Azure Storage by leveraging compromised credentials to access sensitive files. SOC teams should proactively hunt for this behavior to detect and mitigate potential data breaches and unauthorized access in their Azure Sentinel environment.

KQL Query

SecurityAlert
| where AlertName has_any ('Access from  a suspicious IP to a storage file share','	Access from a Tor exit node to a storage blob container')
| extend EntitiesDynamicArray = parse_json(Entities) | mv-expand EntitiesDynamicArray
// Parsing relevant entity column extract hostname and IP address
| extend EntityType = tostring(parse_json(EntitiesDynamicArray).Type), EntityAddress = tostring(EntitiesDynamicArray.Address)
| extend IpAddress = iif(EntityType == 'ip', EntityAddress, '')
| where isnotempty(IpAddress) 
| join kind=inner (
CommonSecurityLog 
| where DeviceVendor =~ "Fortinet"
| where ApplicationProtocol has_any ("SSL","RDP")
| where LogSeverity has_any ("2","3")
| where isnotempty(SourceIP) and isnotempty(DestinationIP) and SourceIP != "0.0.0.0"
| where DeviceAction !in ("close", "client-rst", "server-rst", "deny") and DestinationPort != 161
| project DeviceProduct,LogSeverity,DestinationPort,DestinationIP,Message,SourceIP,SourcePort,Activity,SentBytes,ReceivedBytes
) on $left.IpAddress == $right.DestinationIP
| join kind=inner (
AuditLogs
| where LoggedByService =~ "Core Directory"
| where Category =~ "RoleManagement"
| extend IpAddress = case(
isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)) and tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) != 'null', tostring(parse_json(tostring(InitiatedBy.user)).ipAddress), 
isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).ipAddress)) and tostring(parse_json(tostring(InitiatedBy.app)).ipAddress) != 'null', tostring(parse_json(tostring(InitiatedBy.app)).ipAddress),
'Not Available')
| extend InitiatedBy = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)), 
tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), tostring(parse_json(tostring(InitiatedBy.app)).displayName)), UserRoles = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
| extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName)) 
)
on IpAddress
| summarize count () by TimeGenerated,IpAddress, UserRoles,SourcePort, DestinationPort, AccountCustomEntity =InitiatedBy

Analytic Rule Definition

id: 860a8df2-8d19-4c60-bf61-de1c02422797
name: Storage Alerts Correlation with CommonSecurityLogs & AuditLogs
description: |
  'This query combines different Storage alerts with CommonSecurityLogs and AuditLogs helping analysts investigate any possible storage related attacks faster
   thus reducing Mean Time To Respond'
requiredDataConnectors:
  - connectorId: AzureSecurityCenter
    dataTypes:
      - SecurityAlert (ASC)
  - connectorId: Fortinet
    dataTypes:
      - CommonSecurityLog
  - connectorId: AzureActiveDirectory
    dataTypes:
      - AuditLogs
tactics:
  - InitialAccess
  - Impact
relevantTechniques:
  - T1190
  - T1078
query: |
  SecurityAlert
  | where AlertName has_any ('Access from  a suspicious IP to a storage file share','	Access from a Tor exit node to a storage blob container')
  | extend EntitiesDynamicArray = parse_json(Entities) | mv-expand EntitiesDynamicArray
  // Parsing relevant entity column extract hostname and IP address
  | extend EntityType = tostring(parse_json(EntitiesDynamicArray).Type), EntityAddress = tostring(EntitiesDynamicArray.Address)
  | extend IpAddress = iif(EntityType == 'ip', EntityAddress, '')
  | where isnotempty(IpAddress) 
  | join kind=inner (
  CommonSecurityLog 
  | where DeviceVendor =~ "Fortinet"
  | where ApplicationProtocol has_any ("SSL","RDP")
  | where LogSeverity has_any ("2","3")
  | where isnotempty(SourceIP) and isnotempty(DestinationIP) and SourceIP != "0.0.0.0"
  | where DeviceAction !in ("close", "client-rst", "server-rst", "deny") and DestinationPort != 161
  | project DeviceProduct,LogSeverity,DestinationPort,DestinationIP,Message,SourceIP,SourcePort,Activity,SentBytes,ReceivedBytes
  ) on $left.IpAddress == $right.DestinationIP
  | join kind=inner (
  AuditLogs
  | where LoggedByService =~ "Core Directory"
  | where Category =~ "RoleManagement"
  | extend IpAddress = case(
  isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)) and tostring(parse_json(tostring(InitiatedBy.user)).ipAddress) != 'null', tostring(parse_json(tostring(InitiatedBy.user)).ipAddress), 
  isnotempty(tostring(parse_json(tostring(InitiatedBy.app)).ipAddress)) and tostring(parse_json(tostring(InitiatedBy.app)).ipAddress) != 'null', tostring(parse_json(tostring(InitiatedBy.app)).ipAddress),
  'Not Available')
  | extend InitiatedBy = iff(isnotempty(tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName)), 
  tostring(parse_json(tostring(InitiatedBy.user)).userPrincipalName), tostring(parse_json(tostring(InitiatedBy.app)).displayName)), UserRoles = tostring(parse_json(tostring(InitiatedBy.user)).ipAddress)
  | extend TargetResourceName = tolower(tostring(TargetResources.[0].displayName)) 
  )
  on IpAddress
  | summarize count () by TimeGenerated,IpAddress, UserRoles,SourcePort, DestinationPort, AccountCustomEntity =InitiatedBy
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: FullName
        columnName: AccountCustomEntity
  - entityType: IP
    f

Required Data Sources

Sentinel TableNotes
AuditLogsEnsure this data connector is enabled
CommonSecurityLogEnsure this data connector is enabled
SecurityAlertEnsure this data connector is enabled

MITRE ATT&CK Context

References

False Positive Guidance

Original source: https://github.com/Azure/Azure-Sentinel/blob/main/Hunting Queries/MultipleDataSources/StorageAlertCorrelationwithCommonSecurityLogsandAuditLogs.yaml