Adversaries may use compromised user accounts to upload malicious files to Azure Blob Storage as part of lateral movement or data exfiltration. SOC teams should proactively hunt for this behavior to detect potential data breaches or persistent access within their Azure Sentinel environment.
KQL Query
//Period of time to look back in signin logs
let lookback = 1m;
let TargetFile = "mimikatz.exe"; //Example
union
StorageFileLogs,
StorageBlobLogs
//Collect file uploads
| where StatusText =~ "Success"
| where OperationName =~ "PutBlob" or OperationName =~ "PutRange"
| extend FileName = extract(@"\/([\w\-. ]+)\?", 1, Uri)
//Uncomment below to enable file specific matching
//| where FileName =~ TargetFile
//Caller IP has the port appended, remove it
| extend CallerIpAddress = tostring(split(CallerIpAddress, ":", 0)[0])
| extend FileUploadTime = TimeGenerated
| extend WindowStart = FileUploadTime - lookback
| join (
SigninLogs
| project AzureLoginTime=TimeGenerated, UserPrincipalName, IPAddress, LoginUserAgent=UserAgent
) on $left.CallerIpAddress == $right.IPAddress
//Look back in the signinlogs for the most recent login
| where AzureLoginTime between (WindowStart .. FileUploadTime)
| project AccountUsed=UserPrincipalName, AzureLoginTime, OperationName, FileUploadPath=Uri, CallerIpAddress, LoginUserAgent, UploadUserAgent=UserAgentHeader
//Optional user agent check
| where LoginUserAgent =~ UploadUserAgent
//Pack and summarise the matching login events by the upload event
| extend p = pack("AccountUsed", AccountUsed, "IPUsed", CallerIpAddress, "AzureLoginTime", AzureLoginTime, "UserAgent", LoginUserAgent)
| summarize LoginEvents=make_bag(p) by FileUploadPath, OperationName, UploadUserAgent
id: bee57113-7b9d-4158-958c-a7f3d534c6c4
name: User Account Linked to Storage Account File Upload
description: |
'This hunting query will try to identify the user account used to perform a file upload to blob storage.
This query can be used to match all file upload events, or filtering can be applied on filename to search for a specific upload.'
requiredDataConnectors:
- connectorId: AzureActiveDirectory
dataTypes:
- SigninLogs
tactics:
- CredentialAccess
relevantTechniques:
- T1528
tags:
- Ignite2021
query: |
//Period of time to look back in signin logs
let lookback = 1m;
let TargetFile = "mimikatz.exe"; //Example
union
StorageFileLogs,
StorageBlobLogs
//Collect file uploads
| where StatusText =~ "Success"
| where OperationName =~ "PutBlob" or OperationName =~ "PutRange"
| extend FileName = extract(@"\/([\w\-. ]+)\?", 1, Uri)
//Uncomment below to enable file specific matching
//| where FileName =~ TargetFile
//Caller IP has the port appended, remove it
| extend CallerIpAddress = tostring(split(CallerIpAddress, ":", 0)[0])
| extend FileUploadTime = TimeGenerated
| extend WindowStart = FileUploadTime - lookback
| join (
SigninLogs
| project AzureLoginTime=TimeGenerated, UserPrincipalName, IPAddress, LoginUserAgent=UserAgent
) on $left.CallerIpAddress == $right.IPAddress
//Look back in the signinlogs for the most recent login
| where AzureLoginTime between (WindowStart .. FileUploadTime)
| project AccountUsed=UserPrincipalName, AzureLoginTime, OperationName, FileUploadPath=Uri, CallerIpAddress, LoginUserAgent, UploadUserAgent=UserAgentHeader
//Optional user agent check
| where LoginUserAgent =~ UploadUserAgent
//Pack and summarise the matching login events by the upload event
| extend p = pack("AccountUsed", AccountUsed, "IPUsed", CallerIpAddress, "AzureLoginTime", AzureLoginTime, "UserAgent", LoginUserAgent)
| summarize LoginEvents=make_bag(p) by FileUploadPath, OperationName, UploadUserAgent
| Sentinel Table | Notes |
|---|---|
SigninLogs | Ensure this data connector is enabled |
Scenario: Scheduled Backup Job Using Azure Storage
Description: A legitimate scheduled backup job (e.g., using Azure Backup or third-party tools like Veeam) uploads files to a storage account as part of a routine data protection process.
Filter/Exclusion: Exclude file uploads with filenames containing keywords like backup, snapshot, or restore, or filter by the known backup service account.
Scenario: User Uploading Files via Azure Portal
Description: A user manually uploads files to a storage account via the Azure portal or Azure Storage Explorer as part of their daily tasks (e.g., sharing documents with team members).
Filter/Exclusion: Exclude uploads from known user accounts with access to storage accounts, or filter by file extensions commonly used for user-generated content (e.g., .docx, .xlsx).
Scenario: DevOps Pipeline Artifact Upload
Description: A CI/CD pipeline (e.g., Azure DevOps, GitHub Actions) uploads build artifacts to a storage account for deployment or testing purposes.
Filter/Exclusion: Exclude uploads from known CI/CD service accounts or filter by filenames containing artifact, build, or package.
Scenario: Log File Upload for Monitoring
Description: A system or application uploads log files (e.g., using Azure Monitor or custom scripts) to a storage account for centralized logging and analysis.
Filter/Exclusion: Exclude uploads with filenames containing log, audit, or monitor, or filter by the source IP or user agent associated with the logging service.
Scenario: File Sync via Azure File Sync
Description: A file sync service (e.g., Azure File Sync) synchronizes files between on-premises file servers and Azure Blob Storage, triggering file upload events.
Filter/Exclusion: Exclude uploads from the Azure File Sync