Adversaries may upload a file to Azure Storage to exfiltrate data or establish persistence and then delete it to avoid detection. SOC teams should proactively hunt for this behavior to identify potential data exfiltration or covert command and control activities in their Azure Sentinel environment.
KQL Query
let threshold = 5m;
let StorageData =
union
StorageFileLogs,
StorageBlobLogs;
StorageData
| where StatusText =~ "Success"
| where OperationName =~ "PutBlob" or OperationName =~ "PutRange"
| extend Uri = tostring(split(Uri, "?", 0)[0])
| join (
StorageData
| where StatusText =~ "Success"
| where OperationName =~ "DeleteBlob" or OperationName =~ "DeleteFile"
| extend Uri = tostring(split(Uri, "?", 0)[0])
| project OperationName, DeletedTime=TimeGenerated, Uri
) on Uri
| project TimeGenerated, DeletedTime, Uri, CallerIpAddress, UserAgentHeader, ResponseMd5, StorageAccount=AccountName
| extend windowEnd = TimeGenerated+threshold
| where DeletedTime between (TimeGenerated .. windowEnd)
id: 276731f6-ae09-4469-9fa0-c0791a5a0d8d
name: Azure Storage File Create and Delete
description: |
'This hunting query will try to identify instances where a file us uploaded to file storage and then deleted
within a given threshold. By default the query will find instances where a file is uploaded and deleted within
5 minutes. This hunting query will help detect automated exfiltration.'
requiredDataConnectors: []
tactics:
- Exfiltration
relevantTechniques:
- T1020
- T1537
tags:
- Ignite2021
query: |
let threshold = 5m;
let StorageData =
union
StorageFileLogs,
StorageBlobLogs;
StorageData
| where StatusText =~ "Success"
| where OperationName =~ "PutBlob" or OperationName =~ "PutRange"
| extend Uri = tostring(split(Uri, "?", 0)[0])
| join (
StorageData
| where StatusText =~ "Success"
| where OperationName =~ "DeleteBlob" or OperationName =~ "DeleteFile"
| extend Uri = tostring(split(Uri, "?", 0)[0])
| project OperationName, DeletedTime=TimeGenerated, Uri
) on Uri
| project TimeGenerated, DeletedTime, Uri, CallerIpAddress, UserAgentHeader, ResponseMd5, StorageAccount=AccountName
| extend windowEnd = TimeGenerated+threshold
| where DeletedTime between (TimeGenerated .. windowEnd)
entityMappings:
- entityType: IP
fieldMappings:
- identifier: Address
columnName: CallerIpAddress
Scenario: Scheduled Backup Job Completes
Description: A backup job runs and uploads files to Azure Storage, then deletes them after the backup is complete.
Filter/Exclusion: Exclude events where the operation_name is DeleteBlob and the resource_type is blob, and the caller is a known backup service (e.g., Azure Backup, Veeam, or Acronis).
Scenario: Temporary File Creation and Cleanup by a DevOps Tool
Description: A CI/CD pipeline (e.g., Azure DevOps, GitHub Actions) creates temporary files in Azure Storage during a build, then deletes them after the build completes.
Filter/Exclusion: Exclude events where the operation_name is DeleteBlob and the caller is a known DevOps tool (e.g., azure-pipelines, github-actions, or jenkins).
Scenario: User Uploads and Deletes a File for Testing
Description: A user uploads a test file to Azure Storage and then deletes it after verifying its contents.
Filter/Exclusion: Exclude events where the operation_name is DeleteBlob and the caller is a user account with a known test pattern (e.g., [email protected] or [email protected]).
Scenario: Log File Rotation in Azure Storage
Description: A log aggregation tool (e.g., Azure Monitor, Logstash) uploads log files to Azure Storage, then rotates and deletes old logs.
Filter/Exclusion: Exclude events where the operation_name is DeleteBlob and the resource_type is blob, and the caller is a log management tool (e.g., logstash, azure-monitor, or splunk).
**Scenario: Admin Task to