Azure storage services offer a variety of options for storing and managing data in the cloud. However, storing data in the cloud also comes with some security risks and challenges that organizations should be aware of.

Common questions around Azure Storage Accounts are:

  • How can you ensure that your data is protected from unauthorized access, modification, or deletion?
  • How can you monitor the activity and performance of your storage account?
  • How can you troubleshoot any issues or anomalies that may arise?

In this blog post, I will demonstrate how to monitor Azure Storage Accounts from a security point of view. I will show you how to use collected data plane audit logs for certain use cases according to the storage service threat matrix. After reading the blog, you will have a better understanding of how Azure storage accounts should be monitored and you can enhance the security and security monitoring effectiveness on your environment.

Azure Storage Threat Matrix

The threat matrix for storage services was announced in April 2021, with the idea of identifying and analyzing potential threats against cloud storage services. The threat matrix is mapped to the MITRE ATT&CK framework which allows blue teamers to adapt and respond to new techniques.

Cloud storage accounts are popular targets for adversaries and there are several goals they are trying to achieve, here are a few examples

  • Accessing & exfiltrating sensitive data
  • Gaining a foothold in the environment
  • Lateral movement
  • Deploying malware to the environment
  • and many more

The threat matrix is found at the following link – https://microsoft.github.io/Threat-matrix-for-storage-services/.

In this blog, I’m focusing on a few use cases which are addressing to reconnaissance (TA0043), initial access (TA001), and defense evasion (TA002) tactics. Before jumping to the use cases let’s go through how auditing works in Azure resources as well as how you can tackle data plane auditing with Azure Policies.

Auditing in Azure

It’s essential to understand how Azure resources are audited and what layers are included. My fellow MVP Joosua Santasalo created the following picture a few years ago and I still find it very useful for describing management & data plane differences in the Azure audit trail.

Management Plane Activities

The Azure management plane audit refers to the auditing of the management plane activities, which is responsible for managing and controlling Azure resources, services, and operations. The management plane includes Azure Resource Manager, Azure Portal, Azure CLI, Azure PowerShell, and Azure API.

The Azure management plane audit is designed to provide visibility into activities and changes made to the management plane and to help identify potential security risks, compliance violations, and operational issues. It involves reviewing logs and audit trails generated by Azure services and tools that interact with the management plane and analyzing them to identify suspicious or anomalous activity.

The following activities are included in the Azure management plane audit:

  1. Authentication and authorization: This includes reviewing activities related to user authentication and authorization, such as changes to Role-based Access Control (RBAC) or changes to resource settings.
  2. Resource management: This includes reviewing activities related to the creation, modification, and deletion of Azure resources, such as virtual machines, storage accounts, and databases.
  3. Access control: This includes reviewing activities related to access control, such as changes to security groups, network security groups, and firewall rules.
  4. Monitoring and alerting: This includes reviewing activities related to monitoring and alerting, such as the configuration of Azure Monitor, alerts, and notifications.
  5. Configuration management: This includes reviewing activities related to the configuration of Azure services and tools, such as Azure policies, templates, and resource locks.

Overall, the Azure management plane audit is a critical component of Azure security and helps organizations maintain visibility and control over their Azure environment.

Note: Management plane activities are logged into the ‘AzureActivity’ table in Log Analytics.

Data plane activities

Refer to operations performed on the data stored in Azure services, such as Azure Key Vault, Storage Accounts, or other Azure services. These activities involve accessing, modifying, or deleting data, and they are distinct from management plane activities that involve managing and controlling the Azure environment.

Similar to the Azure management plane audit, the Azure data plane audit is designed to provide visibility into data-related activities and to help identify potential security risks, compliance violations, and operational issues.

The following activities are typically included in the Azure data plane audit:

  1. Data access: This includes reviewing activities related to accessing data stored in Azure services, such as reading, writing, and copying data.
  2. Data modification: This includes reviewing activities related to modifying data stored in Azure services, such as adding, updating, or deleting data.
  3. Data retention and deletion: This includes reviewing activities related to data retention and deletion policies, such as configuring retention periods and deleting data that is no longer needed.
  4. Data backup and recovery: This includes reviewing activities related to backing up and restoring data, such as creating backups, scheduling backups, and recovering data from backups.
  5. Data encryption and security: This includes reviewing activities related to data encryption and security, such as configuring encryption settings, managing access control, and monitoring security logs.

Overall, the Azure data plane audit is important for maintaining the security, compliance, and operational effectiveness of Azure services that store and process data. By monitoring data-related activities and analyzing audit trails, organizations can identify potential issues take appropriate actions to mitigate risks, and ensure the integrity and availability of their data.

Note: Data plane activities are logged into the ‘AzureDiagnostics’ table in Log Analytics or in some resources to resource-specific tables.

Auditing on Azure Storage Accounts

Let’s take a look at how Azure storage account auditing should be configured. I’ve been doing tens of Azure assessments and quite often find that Azure resources data plane audit (diagnostic settings) is not configured. The reason is quite often the same, it’s not taken into account in the environment security monitoring strategy, and not sure how it should be configured or cost impact would be too high.

Note: In Azure incident response and forensic cases, these are the logs you will need most (seen this too many times).

Diagnostic Settings in Azure Storage – AzPolicy

If an organization has an Infra-as-a-Code (IaC) approach needed settings can be configured on the DevOps platform when creating resources in the first place. An alternative approach is to configure diagnostic settings for an Azure storage account is the Azure Policy. There are several approaches here but I’ve done it by creating a policy initiative that contains all storage account services + storage account metrics log.

  • File services – audit logs
  • Table services – audit logs
  • Queue services – audit logs
  • Blob services – audit logs
  • Storage account – metrics

The outcome will look something like in the figures below where you can see diagnostic settings and status on the Azure storage account level.

After deploying policies remember to check your remediation tasks. AzPolicy will be deployed only to new resources and old ones need to be handled with remediation tasks.

One of the common questions I receive after deploying policies is “How I can audit policy compliance”? You can create a new audit policy or evaluate the status from the AzPolicy compliance blade. Remember that your compliance level is related to how the policies are configured. I’ve seen cases where there are misconfigurations in the deploy policy which actually doesn’t deploy anything but it 100% compliant. For that reason, I recommend some use cases (not all) to create audit policies as well.

Use Cases for Security Monitoring

First of all, thanks to Purav Desai for some of the use cases. The ones I’m going through in this blog are:

1) Public access – How to detect if someone spins up a storage account with public access, how this is seen in the Azure audit logs? Or is it detectable in native MS security solutions? If a change is made to allow public access to the storage account, how to detect that one?

2) Accessing the access storage account – In a typical Azure environment, there are a bunch of storage accounts that are used by different services, and for that reason, it is quite tricky to identify which of the requests are malicious and which are not. Many Azure services are calling storage services inside the Azure environment in a typical architecture and as well as by humans inside or outside the Azure environment.

In this scenario, the focus is on IP addresses and locations where storage accounts are accessed and how to detect when someone downloads content from the Azure storage blob.

3) Audit trail is disabled – In this scenario, an adversary is trying to avoid detection and prevent tracking of malicious activities. How does it look like in the audit logs that we can interpret?

Use Cases – Playing with the Data

Let’s start exploring the data and example use cases. The few use cases I’m going through contain background information as well as detection queries or security solutions that can be used as the additional approach here.

Use case 1 – Public Access (Reconnaissance – T0043)

Azure storage account has a public endpoint as many Azure services but public access to storage account data is not allowed by default. On the other hand, Azure storage can be configured to allow anonymous read access for containers and blogs which means that if configured, clients can read data in that container anonymously (without authorizing the request).

According to Microsoft, two separate settings affect anonymous access:

  • Anonymous access setting for the storage account: An Azure Resource Manager storage account offers a setting to allow or disallow anonymous access to the account. 
  • Configure the container’s anonymous access setting: By default, a container’s anonymous access setting is disabled, meaning that authorization is required for every request to the container or its data. 

The following table (learn.microsoft.com) summarizes the options and outcomes with different configuration combinations.

Anonymous access level for the container is set to Private (default setting)Anonymous access level for the container is set to ContainerAnonymous access level for the container is set to Blob
Anonymous access is disallowed for the storage accountNo anonymous access to any container in the storage account.

No anonymous access to any container in the storage account.

The storage account setting overrides the container setting.
No anonymous access to any container in the storage account.

The storage account setting overrides the container setting.
Anonymous access is allowed for the storage accountNo anonymous access to this container (default configuration).Anonymous access is permitted to this container and its blobs.Anonymous access is permitted to blobs in this container, but not to the container itself.

Take into account that allowing anonymous access could cause potential security risks in your environment. There are architectural dependencies for sure and there are scenarios when anonymous access to storage is needed. From a blue team point of view, you most probably would like to detect when storage account public access is allowed?

Detection

In Defender for Cloud (MDC), there is a proactive mechanism (AzPolicy) that detects storage accounts with public access from your environment. It’s all about Cloud Security Posture Management (CSPM) and requires someone to look after the recommendations (process & responsibility) and if there is an adversary doing activities, or misconfiguration by admin you most probably want to act quickly. This is where the detection rule (Sentinel / Monitor) comes into the game.

Figure below shows detection but take into account that MDC only detects management plane activity and changes in storage account level.

Note: If you have enabled Defender CSPM (DCSP) in your environment you can leverage two new features to detect the scenario; Attack Path & Cloud Security Explorer. The intro about the latter one is found at the bottom of this blog.

Query

The following queries can be used as a starting point to detect activities mentioned above. As you can see from queries we need to use both ‘AzureActivity’ & ‘StorageBlobLogs’ as well to detect activity from the needed changes. The reason for this is that activities at the storage account level are management plane activities (AzureActivity) and container/blob level activities are data plane (StorageBlobLogs) activities.

//Public access set at storage account level
AzureActivity
| where TimeGenerated >= ago(1d)
| where CategoryValue == "Administrative"
| where parse_json(Properties).resourceProviderValue == "MICROSOFT.STORAGE"
| where parse_json(Properties).message == "Microsoft.Storage/storageAccounts/write"
| extend ['ResourceName'] = tostring(parse_json(Properties).resource)
| extend ['ResourceId'] = tostring(parse_json(tostring(parse_json(Properties).responseBody)).id)
| extend ['Entity'] = tostring(Properties_d.entity)
| extend ['Operation'] = tostring(parse_json(Properties).message)
| extend ['AllowBlobPublicAccess'] = tostring(parse_json(tostring(parse_json(tostring(Properties_d.requestbody)).properties)).allowBlobPublicAccess)
| where AllowBlobPublicAccess contains "true"
| project TimeGenerated, Caller, CallerIpAddress, ['Entity'] , ['Operation'], ['AllowBlobPublicAccess']

//Public access set at container level
StorageBlobLogs
| where TimeGenerated >= ago(1d)
| where OperationName == 'SetContainerACL'
| project TimeGenerated, AccountName, OperationName, CorrelationId, ObjectKey, Category, CallerIpAddress, _ResourceId

Use Case 2 – Accessing Azure Storage Account (Initial Access – T001)

In a typical Azure environment, there are a bunch of storage accounts that are used by different services, and for that reason, it is quite tricky to identify which of the requests are malicious and which are not. Many Azure services are calling storage services inside the Azure environment in a typical architecture and as well as by humans inside or outside the Azure environment.

Query

We can filter requests by IP-address and location with the following queries. Shoot out for my friend and fellow MVP+MVR (how cool is that combo?) Joosua Santasalo, who helped me with adding pieces of the puzzle together.

//List blob operations excluding some of the IP-ranges from the query from dedicated storage account
let operationlist = dynamic(["GetBlob", "DeleteBlob", "GetBlobServiceProperties","GetContainerServiceMetadata"]);
StorageBlobLogs
| where TimeGenerated >= ago(10d)
| where AccountName == "contosohpsa" 
| where OperationName in (operationlist)
| where CallerIpAddress !startswith "10." and CallerIpAddress !startswith "172.16." and CallerIpAddress !startswith "192.168." and CallerIpAddress !startswith "169.254."
| project TimeGenerated, AccountName, OperationName, CallerIpAddress, AuthenticationType, Category, MetricResponseType, CorrelationId, RequesterObjectId, RequesterAppId, RequesterAudience, RequesterTenantId, RequesterUpn, UserAgentHeader, ServiceType

//List blob operations by summarizing IP-ranges & geo locations & using isPrivate function
let ranges =  dynamic(['10.0.0.0/8','172.16.0.0/12','192.168.0.0/16','100.64.0.0/10']);
let operationlist = dynamic(["GetBlob", "DeleteBlob", "GetBlobServiceProperties","GetContainerServiceMetadata"]);
StorageBlobLogs
| where TimeGenerated > now() -100d
| distinct CallerIpAddress, OperationName, StatusCode
| extend parsedIp = tostring(split(CallerIpAddress,':')[0])
| extend isPrivate = ipv4_is_in_any_range(parsedIp, ranges)
| where isPrivate == false
| summarize count(), make_set(OperationName) by  parsedIp
| extend location = geo_info_from_ip_address(parsedIp).country

Use Case 3 – Disable Audit Logs or Disable Cloud Workload Protection (Defense Evasion – T002)

Disabling audit logs or disabling cloud workload protection both go underneath ‘Defense Evasion‘ in the MITRE ATT&CK framework. The idea in both approaches is that the adversary is trying to avoid detection and prevent tracking of malicious activities.

Descriptions according to the threat matrix for storage services:

  • Attackers may disable storage account audit logs to prevent event tracking and avoid detection. Audit logs provide a detailed record of operations performed on a target storage account and may be used to detect malicious activities. Thus, disabling these logs can leave a resource vulnerable to attacks without being detected.
  • Attackers may disable the cloud workload protection service which raises security alerts upon detection of malicious activities in cloud storage services.

Query

With the following queries, you can detect when someone changes Defender for Cloud (MDC) storage account defender plans or deletes diagnostic settings from the storage account.

//Detect pricing changes in MDC Defender Storage Account plans
CloudAppEvents
| where ApplicationId == '12260'
| where ObjectName == "StorageAccounts"
| where Application == "Microsoft Azure"
| where ActionType == "Write Pricings"
| extend Action = tostring(parse_json(tostring(RawEventData.authorization)).action)
| extend Role = tostring(parse_json(tostring(parse_json(tostring(RawEventData.authorization)).evidence)).role)
| extend Scope = tostring(parse_json(tostring(RawEventData.authorization)).scope)
| extend EventCategory = tostring(RawEventData.eventCategory)
| extend Url = tostring(parse_json(tostring(RawEventData.httpRequest)).url)
| where parse_json(tostring(RawEventData.httpRequest)).method == "PUT"
| extend message_ = tostring(parse_json(tostring(RawEventData.properties)).message)
| project Action, Role, Scope, EventCategory, Url, AccountDisplayName, UserAgent, IPAddress, IPTags

//Detect Diagnostic settings delete action on storage account blobs
AzureActivity
| where CategoryValue == "Administrative"
| where ResourceProviderValue == "MICROSOFT.STORAGE"
| where Properties_d.message == "microsoft.insights/diagnosticSettings/delete"
//| where ActivitySubstatusValue == "OK"
| extend ['Caller'] = tostring(parse_json(Properties).caller)
| extend ['Entity'] = tostring(parse_json(Properties).entity)
| extend ['Message'] = tostring(parse_json(Properties).message)
| extend ['Resource'] = tostring(parse_json(Properties).resource)
| extend ['ResourceGroup'] = tostring(parse_json(Properties).resourceGroup)
| extend ['SubscriptionId'] = tostring(parse_json(Properties).subscriptionId)
| extend ['Action'] = tostring(parse_json(Authorization).action)

Summary

Auditing Azure storage accounts can be a painful task but hopefully, this writing gives you ideas on how it can be tackled. In this blog, I only scratched the surface about this topic and there are plenty of use cases that could be elaborated. For example, Defender for Cloud – Cloud Security Posture Management (DCSPM) provides detection and protection on this area but it’s worth to another blog post and is not covered on this one.

If you have any ideas pr feedback, don’t hesitate to contact me. Until next time!

References

Best practices for monitoring Azure Blob storage

Original threat matrix for storage services

Overview of Microsoft Defender for Storage

Threat Matrix for Azure Storage Services

Security recommendations for Azure Storage

Azure security baseline for Storage

Queries in GitHub repo