Detection rules › Kusto
Tailscale Premium: Large outbound transfer over tailnet
Identifies when a single src-dst pair transfers more than 100 MB over the tailnet within a 1-hour window. Large bursts can indicate data staging, exfiltration, or a misconfigured backup. Requires Tailscale Premium or Enterprise.
MITRE ATT&CK coverage
| Tactic | Techniques |
|---|---|
| Exfiltration |
Rule body
id: d2e3f4a5-2b3c-4d5e-6f7a-8b9c0d1e2f3a
name: "Tailscale Premium: Large outbound transfer over tailnet"
description: |
Identifies when a single src-dst pair transfers more than 100 MB over the tailnet within a 1-hour window. Large bursts can indicate data staging, exfiltration, or a misconfigured backup. Requires Tailscale Premium or Enterprise.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: TailscalePremiumCCF
dataTypes:
- Tailscale_Network_CL
queryFrequency: 1h
queryPeriod: 1h
triggerOperator: gt
triggerThreshold: 0
tactics:
- Exfiltration
- Collection
relevantTechniques:
- T1041
- T1020
query: |
let bytesThreshold = 100 * 1024 * 1024;
Tailscale_Network_CL
| where TimeGenerated > ago(1h)
| where HasVirtualTraffic
| mv-expand t = VirtualTraffic
| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), Bytes = tolong(t.txBytes) + tolong(t.rxBytes), Pkts = tolong(t.txPkts) + tolong(t.rxPkts)
| summarize TotalBytes = sum(Bytes), TotalPackets = sum(Pkts) by NodeId, SrcNodeName, SrcUser, DstNodeName, DstUser, Src, Dst, Proto
| where TotalBytes > bytesThreshold
| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)
| order by TotalBytes desc
entityMappings:
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: SrcNodeName
- entityType: Host
fieldMappings:
- identifier: HostName
columnName: DstNodeName
- entityType: Account
fieldMappings:
- identifier: FullName
columnName: SrcUser
- entityType: IP
fieldMappings:
- identifier: Address
columnName: Src
incidentConfiguration:
createIncident: true
groupingConfiguration:
enabled: true
reopenClosedIncident: false
lookbackDuration: PT5H
matchingMethod: AllEntities
kind: Scheduled
version: 1.0.0
Stages and Predicates
Parameters
let bytesThreshold = 100 * 1024 * 1024;
Stage 1: source
Tailscale_Network_CL
Stage 2: where
| where TimeGenerated > ago(1h)
Stage 3: where
| where HasVirtualTraffic
Stage 4: mv-expand
| mv-expand t = VirtualTraffic
Stage 5: extend
| extend Src = tostring(t.src), Dst = tostring(t.dst), Proto = toint(t.proto), Bytes = tolong(t.txBytes) + tolong(t.rxBytes), Pkts = tolong(t.txPkts) + tolong(t.rxPkts)
Stage 6: summarize
| summarize TotalBytes = sum(Bytes), TotalPackets = sum(Pkts) by NodeId, SrcNodeName, SrcUser, DstNodeName, DstUser, Src, Dst, Proto
Stage 7: where
| where TotalBytes > bytesThreshold
Stage 8: extend
| extend TotalMB = round(TotalBytes / 1024.0 / 1024.0, 2)
Stage 9: sort
| order by TotalBytes desc
Indicators
These rows show field, operator, and value matches.
| Field | Kind | Values | Search |
|---|---|---|---|
TotalBytes | cross_field_compare |
| field:"TotalBytes" kind:cross_field_compare value:"bytesThreshold" |
Output fields
These fields are emitted when the rule matches.
| Field | Source |
|---|---|
Dst | summarize |
DstNodeName | summarize |
DstUser | summarize |
NodeId | summarize |
Proto | summarize |
Src | summarize |
SrcNodeName | summarize |
SrcUser | summarize |
TotalBytes | summarize |
TotalPackets | summarize |
TotalMB | extend |