Attackers are attempting to enumerate Azure Storage account keys to gain unauthorized access and escalate privileges within the cloud environment. SOC teams should proactively hunt for this behavior to detect potential credential compromise and unauthorized resource manipulation in Azure Sentinel.
KQL Query
SigninLogs
| where ResultType == "0"
| where AppDisplayName !in ("Office 365 Exchange Online", "Skype for Business Online", "Office 365 SharePoint Online")
| project SuccessLogonTime = TimeGenerated,UserPrincipalName, IPAddress,SuccessAppDisplayName = AppDisplayName
| join kind=inner
(
AzureActivity
| where tolower(OperationNameValue) endswith "listkeys/action"
| where ActivityStatus =~ "Succeeded"
| project CallerIpAddress, _ResourceId, SubscriptionId, ActivityStatus, Category, Authorization,OperationName
)
on $left.IPAddress == $right. CallerIpAddress
| project SubscriptionId, ActivityStatus, IPAddress, OperationName, UserPrincipalName
| 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 $left. IPAddress == $right. IpAddress
| summarize count () by TimeGenerated,IPCustomEntity=IpAddress,UserRoles,AccountCustomEntity=InitiatedBy,TargetResourceName
id: f19f913f-292a-41ed-9ac0-f3ea5e703d36
name: Storage Account Key Enumeration
description: |
'This query identifies attackers trying to enumerate the Storage keys as well as updating roles using AzureActivity,SigninLogs
and AuditLogs'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
- connectorId: AzureActivity
dataTypes:
- AzureActivity
- connectorId: AzureActiveDirectory
dataTypes:
- AuditLogs
tactics:
- InitialAccess
- LateralMovement
relevantTechniques:
- T1586
- T1570
query: |
SigninLogs
| where ResultType == "0"
| where AppDisplayName !in ("Office 365 Exchange Online", "Skype for Business Online", "Office 365 SharePoint Online")
| project SuccessLogonTime = TimeGenerated,UserPrincipalName, IPAddress,SuccessAppDisplayName = AppDisplayName
| join kind=inner
(
AzureActivity
| where tolower(OperationNameValue) endswith "listkeys/action"
| where ActivityStatus =~ "Succeeded"
| project CallerIpAddress, _ResourceId, SubscriptionId, ActivityStatus, Category, Authorization,OperationName
)
on $left.IPAddress == $right. CallerIpAddress
| project SubscriptionId, ActivityStatus, IPAddress, OperationName, UserPrincipalName
| 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 $left. IPAddress == $right. IpAddress
| summarize count () by TimeGenerated,IPCustomEntity=IpAddress,UserRoles,AccountCustomEntity=InitiatedBy,TargetResourceName
entityMappings:
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: AccountCustomEntity
- entityType: IP
fieldMappings:
- identifier: Address
columnName: IPCustomEntity
| Sentinel Table | Notes |
|---|---|
AuditLogs | Ensure this data connector is enabled |
AzureActivity | Ensure this data connector is enabled |
SigninLogs | Ensure this data connector is enabled |
Scenario: Azure Storage Account Key Rotation via Azure Portal
Description: An admin is manually rotating the storage account keys through the Azure Portal.
Filter/Exclusion: where ActivityName != "Storage account key rotated" or where OperationName != "Key rotation"
Scenario: Scheduled Job for Backup Using Azure CLI
Description: A scheduled job runs a backup script using Azure CLI that interacts with storage accounts, potentially triggering key enumeration.
Filter/Exclusion: where Caller != "Azure CLI" or where Caller != "backup-script-name"
Scenario: Admin Task for Role Assignment in Azure Active Directory
Description: An admin is assigning roles to users in Azure AD, which may be logged as a storage account activity.
Filter/Exclusion: where OperationName != "Role assignment" or where ResourceProvider != "Microsoft.Authorization"
Scenario: Azure DevOps Pipeline Deploying to Blob Storage
Description: A CI/CD pipeline deploys code to Azure Blob Storage, which may generate activity logs that match the detection rule.
Filter/Exclusion: where Caller != "Azure DevOps" or where Caller != "pipeline-name"
Scenario: Azure Monitor Log Analytics Job Querying Storage Logs
Description: A Log Analytics job is querying storage account logs for monitoring purposes, which may be flagged as key enumeration.
Filter/Exclusion: where Caller != "Log Analytics" or where Caller != "log-analytics-job-name"