Detection rules › Kusto

Tailscale Premium: Mass fan-out from single node

Status
available
Severity
high
Time window
15m
Group by
NodeId, Src, SrcNodeName, SrcOs, SrcTags, SrcUser
Source
github.com/Azure/Azure-Sentinel

Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise.

MITRE ATT&CK coverage

Rule body

id: f4a5b6c7-4d5e-6f7a-8b9c-0d1e2f3a4b5c
name: "Tailscale Premium: Mass fan-out from single node"
description: |
  Identifies when a single node initiates flows to 25 or more unique destinations within a 15-minute window. Sudden fan-out is consistent with port scanning, lateral discovery, or worm-style propagation. Requires Tailscale Premium or Enterprise.
severity: High
status: Available
requiredDataConnectors:
  - connectorId: TailscalePremiumCCF
    dataTypes:
      - Tailscale_Network_CL
queryFrequency: 15m
queryPeriod: 15m
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
  - LateralMovement
relevantTechniques:
  - T1018
  - T1021
  - T1046
query: |
  let dstThreshold = 25;
  Tailscale_Network_CL
  | where TimeGenerated > ago(15m)
  | where HasVirtualTraffic
  | mv-expand t = VirtualTraffic
  | extend Src = tostring(t.src), Dst = tostring(t.dst)
  | summarize UniqueDestinations = dcount(Dst), TopDestinations = make_set(Dst, 25) by NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags), Src
  | where UniqueDestinations >= dstThreshold
  | order by UniqueDestinations desc
entityMappings:
  - entityType: Host
    fieldMappings:
      - identifier: HostName
        columnName: SrcNodeName
  - 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 dstThreshold = 25;

Stage 1: source

Tailscale_Network_CL

Stage 2: where

| where TimeGenerated > ago(15m)

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)

Stage 6: summarize

| summarize UniqueDestinations = dcount(Dst), TopDestinations = make_set(Dst, 25) by NodeId, SrcNodeName, SrcUser, SrcOs, SrcTags=tostring(SrcTags), Src

Stage 7: where

| where UniqueDestinations >= dstThreshold

Stage 8: sort

| order by UniqueDestinations desc

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
NodeIdsummarize
Srcsummarize
SrcNodeNamesummarize
SrcOssummarize
SrcTagssummarize
SrcUsersummarize
TopDestinationssummarize
UniqueDestinationssummarize