Microsoft Azure utilizes blobs to house large amounts of unstructured data. An Azure blob can be configured to be accessed from specific IP addresses only. This is the common configuration and a known best practice that helps prevent incidents of data leakage. However, a user can mistakenly configure an Azure blob to be accessed from any public IP or from unauthorized IP addresses leaving them vulnerable to potentially malicious activity.
The implications of a misconfigured Azure blob can be significant: sensitive data exposed to the public domain, potential data breaches and leaks, and regulatory compliance risks. Just a glance at recent incidents, such as those reported by Bleeping Computer, SOCRadar, and The Register, underscores the urgency of monitoring these configurations.
The detection rules from CardinalOps that we have provided below have been designed to keep your Azure storage environment secure by monitoring configurations that may allow unauthorized access. By focusing on the “publicNetworkAccess” setting within Azure storage accounts, you will be able to detect any instances where an Azure blob is configured to permit access from public networks.
Let’s take a look.
Detection Rule – Azure – Storage Account Configured for Public Network Access Enabled:
An Azure storage account contains data objects including blobs, file shares, queues, tables, and disks. Administrators can manage different settings and properties for every storage account in the organization, some of which can be used for security purposes. The “publicNetworkAccess” can be used to allow or block access from public networks (i.e. not private endpoints).
Attackers who’ve gained privileges may attempt to configure the storage account to allow access from public networks in order to impair the organization’s defenses.
The rule alerts when an Azure storage account is configured to allow public network access.
To read more about Azure Blob Storage security best practices click here.
To read more about this configuration click here.
NOTE: The queries provided below include an exclusion placeholder “<whitelisted IP addresses>“ where whitelisted IPs should be entered before implementing the rule. Tuning for these detections can also be based on the subscription identifier, storage accounts, user IDs, and more depending on the usage patterns with your organization.
Splunk Query:
index=<index> sourcetype=<sourcetype> operationName.value=”Microsoft.Storage/storageAccounts/write” status.value IN (“Succeeded”, “Success”) “publicNetworkAccess”
| spath input=properties.requestbody
| mvexpand properties.networkAcls.ipRules{}.value
| search properties.publicNetworkAccess=”Enabled” OR (properties.networkAcls.defaultAction=”” NOT properties.networkAcls.ipRules{}.value IN (<whitelisted IP addresses>))
| stats values(httpRequest.clientIpAddress) as httpRequest.clientIpAddress, values(properties.*) as *, values(resourceGroupName) as resourceGroupName, values(properties.entity) as properties.entity, values(resourceId) as resourceId count by caller
Sentinel Query:
AzureActivity
| where OperationNameValue =~ “MICROSOFT.STORAGE/STORAGEACCOUNTS/WRITE”
| where ActivityStatusValue in~ (“Success”, “Succeeded”)
| extend publicNetworkAccess = parse_json(tostring(parse_json(tostring(parse_json(Properties).responseBody)).properties)).publicNetworkAccess
| extend defaultAction = tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).responseBody)).properties)).networkAcls)).defaultAction)
| extend clientIpAddress = tostring(parse_json(HTTPRequest).clientIpAddress)
| extend callerName = tostring(parse_json(Properties).resource)
| extend ipRules = todynamic(parse_json(tostring(parse_json(tostring(parse_json(tostring(parse_json(tostring(parse_json(Properties).responseBody)).properties)).networkAcls)).ipRules)))
| mv-expand ipRules
| extend aclIpAdress = tostring(ipRules.value)
| extend aclAction = tostring(ipRules.action)
| mv-apply newIpAddress = todynamic(“true”) on (
where publicNetworkAccess =~ “enabled” and aclAction =~ “allow”
| where aclIpAdress !in (<whitelisted IP addresses>))
| where defaultAction =~ “allow” or newIpAddress =~ “true”
| project-reorder Caller, _ResourceId, Properties, defaultAction, publicNetworkAccess, aclAction, aclIpAdress, TimeGenerated
Validation Steps
These steps are critical in order to validate that the rule will work correctly and also in order to simulate what the alert looks like so that you can confirm that it contains all the required information.
Follow these steps to create new storage accounts to perform the test on:
- Log into the Azure portal.
- Search for “Storage Accounts”.
- Click on “+ Create”.
- Fill in the required fields, click on “Review” > “Create”.
Follow these steps to configure allow public access to the storage account above:
- Log into the Azure portal.
- Search for Storage Accounts.
- Choose the storage account from the list.
- In the left panel, under the Security + networking category, select Networking.
- Under Public network access, check the Enabled from all networks choice.
Follow these steps to delete the storage account created above:
- Log into the Azure portal.
- Search for “Storage Accounts”.
- Mark the checkbox next to the relevant account’s name.
- Click on “Delete” and confirm the action
Building Effective Detections is (Still) Difficult
Detection engineering continues to be a challenging process for security operations teams, with some of the main issues deriving from navigating a chaotic and evolving threat landscape, an ever-increasing need for speed, and the management of complexity within the data and systems that are required for effective detections.
Take the above Sentinel query as an example – Microsoft has recently changed the ActivityStatusValues being reported (from Success to Succeeded), and it seems that both of them can now be seen (presumably because it’s a transition period, but we don’t know this for sure). Developing a query that will fire properly requires a vast, and adaptive, knowledge of Microsoft Azure while also putting an emphasis on continuous awareness and visibility into whether or not your rules have broken or become misconfigured over time.
Detection Posture Management
Building and maintaining a strong detection posture is an essential, yet often overlooked, element to a strong cyber defense. Detection engineering teams need to balance the need to not only onboard detections for best practices (such as this Azure blob configuration) and new emerging threats and vulnerabilities, but also to constantly be searching for and remediating any misconfigurations or excess noise in their existing rules that are creating potential gaps and openings for threat actors.
The CardinalOps platform continuously assesses and strengthens the detection coverage of your existing tools by ensuring that you have the necessary detections for the threats most relevant to your unique environment and organization and that they are working as you need them to. If you are interested in seeing how easy it can be to improve your detection posture, request a demo today.