When attackers know a volume alert exists, they cap the rate, send on a schedule and spread the theft across weeks. Per-hour and per-day thresholds do not see that pattern. Cumulative volume per destination and the regularity of small uploads do, which is why you need long retention and a few well-chosen aggregates.
Signals at a Glance
| Signal | Where | ATT&CK |
|---|---|---|
| 30-day cumulative bytes-out to one external destination from one host | Firewall, proxy, Zeek | T1030 |
| Uploads of near-constant size at a near-constant interval | Proxy, Zeek conn.log | T1029 |
| Egress only inside a narrow scheduled window each day | Flow data | T1029 |
| Long-lived session with steady low-rate outbound bytes | Zeek conn.log | T1030 |
| Scripted, recurring upload from a scheduled task or cron job | Process creation, task logs | T1020 |
Cumulative Volume per Destination
Aggregate over a window long enough to expose the total. A destination that no one else in the organisation talks to, and that has received a few hundred megabytes from one host across a month, is more interesting than a one-off 1 GB upload to a CDN:
// Sentinel (KQL) - 30-day cumulative bytes to rare external destinations
// tune/validate: needs 30+ days of retention in the flow table
let rare = CommonSecurityLog
| where TimeGenerated > ago(30d) and DeviceAction == "allow" and ipv4_is_private(DestinationIP) == false
| summarize hosts = dcount(SourceIP) by DestinationIP
| where hosts <= 2;
CommonSecurityLog
| where TimeGenerated > ago(30d) and DeviceAction == "allow"
| where DestinationIP in ((rare | project DestinationIP))
| summarize totalMB = round(sum(SentBytes) / 1048576.0, 1), activeDays = dcount(bin(TimeGenerated, 1d)),
sessions = count() by SourceIP, DestinationIP
| where totalMB > 200 and activeDays >= 10
| order by totalMB desc
Regular Small Uploads
The timing signal is the same as for beaconing, but the focus is outbound payload. Group by source and destination, then look for low variance in both the interval between sessions and the bytes sent:
index=zeek sourcetype="bro:conn:json" local_orig=true local_resp=false earliest=-14d | rename "id.orig_h" as src, "id.resp_h" as dest | sort 0 src, dest, _time | streamstats current=f last(_time) as prev_time by src, dest | eval gap = _time - prev_time | stats count as sessions avg(gap) as gap_avg stdev(gap) as gap_sd avg(orig_bytes) as b_avg stdev(orig_bytes) as b_sd by src, dest | where sessions > 30 AND gap_avg > 60 AND (gap_sd / gap_avg) < 0.25 AND (b_sd / b_avg) < 0.5 | eval total_mb = round(sessions * b_avg / 1048576, 1) | sort - total_mb ``` tune/validate: exclude telemetry, NTP and update endpoints through a lookup ```
- Require a minimum number of active days so a single large session does not qualify; this detection targets persistence of the egress, not its size.
- Check scheduled tasks and cron entries on the source host for a recurring job that matches the interval you found.
- Retention is the dependency. Thirty days of flow records at minimum, and ninety where you can afford it, since the slowest operators move data across a quarter.
- Use rarity as the main filter: very few hosts talking to the same destination, and none of them in a role that explains it.
MITRE CAR analytics on scheduled and periodic communication, RITA over Zeek conn.log for interval scoring, Elastic and Splunk ESCU content for beaconing and data-transfer-size analytics, and Zeek conn.log semantics. Baseline your own environment — telemetry agents, monitoring and software updates are low-and-slow by design.
Build Detections That Fire
Mjolnir Security helps blue teams turn threat intelligence into high-fidelity detections across endpoint, network, identity and cloud telemetry.
- Detection Engineering We build and tune detections mapped to ATT&CK from your own telemetry.
- Threat Hunting as a Service Hypothesis-driven hunts for the behaviours on this page.
- 24/7 Incident Response Containment when a detection fires. Call +1 833 403 5875.
Stay ahead of emerging threats. Get notified when we publish new intelligence reports and advisories.
Subscribe to Alerts