Detection rules › Kusto
Netskope - Excessive Downloads Detection (Spike vs Baseline)
Detects users with excessive download activity compared to their 7-day baseline. Triggers when current download volume exceeds 3x the average.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Collection | T1074 Data Staged, T1530 Data from Cloud Storage |
Rule body kusto
id: dd0ebd84-ffbe-45df-848b-0615ac446b04
name: Netskope - Excessive Downloads Detection (Spike vs Baseline)
description: |
Detects users with excessive download activity compared to their 7-day baseline. Triggers when current download volume exceeds 3x the average.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: NetskopeWebTxConnector
dataTypes:
- NetskopeWebTransactions_CL
queryFrequency: 1h
queryPeriod: 7d
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
- Collection
relevantTechniques:
- T1530
- T1074
query: |
let lookbackPeriod = 7d;
let currentPeriod = 1h;
let threshold = 3;
let baseline = NetskopeWebTransactions_CL
| where TimeGenerated between (ago(lookbackPeriod) .. ago(currentPeriod))
| where isnotempty(CsUsername)
| where XCsAppActivity =~ 'Download' or ScBytes > 0
| summarize
BaselineAvgBytes = avg(ScBytes),
BaselineTotalBytes = sum(ScBytes),
BaselineCount = count()
by CsUsername
| extend BaselineDailyAvg = BaselineTotalBytes / 7;
let current = NetskopeWebTransactions_CL
| where TimeGenerated > ago(currentPeriod)
| where isnotempty(CsUsername)
| where XCsAppActivity =~ 'Download' or ScBytes > 0
| summarize
CurrentTotalBytes = sum(ScBytes),
CurrentCount = count(),
Apps = make_set(XCsApp),
Files = make_set(XCsAppObjectName)
by CsUsername;
current
| join kind=inner baseline on CsUsername
| where CurrentTotalBytes > (BaselineDailyAvg * threshold)
| extend
SpikeMultiplier = round(CurrentTotalBytes / BaselineDailyAvg, 2),
CurrentTotalMB = round(CurrentTotalBytes / 1048576.0, 2),
BaselineDailyMB = round(BaselineDailyAvg / 1048576.0, 2)
| project
TimeGenerated = now(),
User = CsUsername,
CurrentDownloadMB = CurrentTotalMB,
BaselineDailyAvgMB = BaselineDailyMB,
SpikeMultiplier,
DownloadCount = CurrentCount,
ApplicationsUsed = Apps,
FilesDownloaded = Files
entityMappings:
- entityType: Account
fieldMappings:
- identifier: Name
columnName: User
version: 1.0.0
kind: Scheduled
Stages and Predicates
Parameters
let lookbackPeriod = 7d;
let currentPeriod = 1h;
let threshold = 3;
Let binding: baseline
let baseline = NetskopeWebTransactions_CL
| where TimeGenerated between (ago(lookbackPeriod) .. ago(currentPeriod))
| where isnotempty(CsUsername)
| where XCsAppActivity =~ 'Download' or ScBytes > 0
| summarize
BaselineAvgBytes = avg(ScBytes),
BaselineTotalBytes = sum(ScBytes),
BaselineCount = count()
by CsUsername
| extend BaselineDailyAvg = BaselineTotalBytes / 7;
Derived from lookbackPeriod, currentPeriod.
The stages below define let current (the rule's main pipeline source).
Stage 1: source
NetskopeWebTransactions_CL
Stage 2: where
| where TimeGenerated > ago(currentPeriod)
Stage 3: where
| where isnotempty(CsUsername)
Stage 4: where
| where XCsAppActivity =~ 'Download' or ScBytes > 0
Stage 5: summarize
| summarize
CurrentTotalBytes = sum(ScBytes),
CurrentCount = count(),
Apps = make_set(XCsApp),
Files = make_set(XCsAppObjectName)
by CsUsername
The stages below run on current (the outer pipeline).
Stage 6: join
current
| join kind=inner baseline on CsUsername
Stage 7: where
| where CurrentTotalBytes > (BaselineDailyAvg * threshold)
Stage 8: extend
| extend
SpikeMultiplier = round(CurrentTotalBytes / BaselineDailyAvg, 2),
CurrentTotalMB = round(CurrentTotalBytes / 1048576.0, 2),
BaselineDailyMB = round(BaselineDailyAvg / 1048576.0, 2)
Stage 9: project
| project
TimeGenerated = now(),
User = CsUsername,
CurrentDownloadMB = CurrentTotalMB,
BaselineDailyAvgMB = BaselineDailyMB,
SpikeMultiplier,
DownloadCount = CurrentCount,
ApplicationsUsed = Apps,
FilesDownloaded = Files
Indicators
Each row is a field, operator, and value that the rule matches. The corpus column counts how many other rules in the catalog look for the same combination: high numbers point to widely-used, community-vetted indicators. Blank or 1 shows that the indicator is specific to this rule.
| Field | Kind | Values |
|---|---|---|
CsUsername | is_not_null | |
CurrentTotalBytes | cross_field_compare |
|
ScBytes | gt |
|
XCsAppActivity | eq |
|
Output fields
Fields the rule emits when it matches. Chronicle authors list these in the outcome block; they appear on the detection and $risk_score drives alerting. Sentinel / Defender XDR rules build them up through project / summarize / extend stages. Sentinel maps these into alert fields via entityMappings and customDetails; Defender XDR custom detections surface them as alert fields directly.
| Field | Source |
|---|---|
ApplicationsUsed | project |
BaselineDailyAvgMB | project |
CurrentDownloadMB | project |
DownloadCount | project |
FilesDownloaded | project |
SpikeMultiplier | project |
TimeGenerated | project |
User | project |