Detection rules › Kusto

StealthTalk - Multi new devices registration

Status
available
Severity
medium
Time window
1d
Group by
UserId
Source
github.com/Azure/Azure-Sentinel

Identifies a single StealthTalk user registering two or more previously-unseen devices within a 24-hour window. Sudden registration of multiple new devices is a strong indicator of account takeover (an attacker enrolling their own device after credential theft) or device-farm abuse. The rule fires when a user produces two or more distinct NewDeviceId values within the lookback window, regardless of operating system. Look at the OSList custom detail to spot cross-platform patterns (e.g. an iOS-only user suddenly enrolling an Android device).

MITRE ATT&CK coverage

Rule body kusto

id: f9d4c2a8-1b6e-4a3f-9c7d-8e2b1a3c5d7e
name: StealthTalk - Multi new devices registration
description: |
  Identifies a single StealthTalk user registering two or more previously-unseen devices within
  a 24-hour window. Sudden registration of multiple new devices is a strong indicator of
  account takeover (an attacker enrolling their own device after credential theft) or
  device-farm abuse.

  The rule fires when a user produces two or more distinct NewDeviceId values within the
  lookback window, regardless of operating system. Look at the OSList custom detail to spot
  cross-platform patterns (e.g. an iOS-only user suddenly enrolling an Android device).
severity: Medium
requiredDataConnectors:
  - connectorId: StealthTalkAnomalousAuth
    dataTypes:
      - StealthTalkAnomalousAuth_CL
queryFrequency: 30m
queryPeriod: 1d
triggerOperator: gt
triggerThreshold: 0
status: Available
tactics:
  - Persistence
  - InitialAccess
  - DefenseEvasion
relevantTechniques:
  - T1078
  - T1098
query: |
  let LookbackPeriod = 24h;
  let MinNewDevices  = 2;
  StealthTalkAnomalousAuth_CL
  | where TimeGenerated >= ago(LookbackPeriod)
  | where EventType == "NewDeviceLogin"
  | summarize
      NewDeviceCount  = dcount(NewDeviceId),
      NewDevices      = make_set(NewDeviceId),
      NewDeviceOSList = make_set(NewDeviceOS),
      FirstSeen       = min(TimeGenerated),
      LastSeen        = max(TimeGenerated),
      AppVersions     = make_set(AppVersion)
    by UserId
  | where NewDeviceCount >= MinNewDevices
  | extend
      AlertName    = "MultiNewDevicesRegistration",
      AlertDetails = strcat(
          "User ", UserId,
          " registered ", NewDeviceCount, " new devices within 24 hours.",
          " Device IDs: ", tostring(NewDevices), ".",
          " Operating systems: ", tostring(NewDeviceOSList), "."
      )
  | project
      TimeGenerated = LastSeen,
      UserId, NewDeviceCount, NewDevices, NewDeviceOSList,
      FirstSeen, LastSeen, AppVersions, AlertName, AlertDetails
entityMappings:
  - entityType: Account
    fieldMappings:
      - identifier: Name
        columnName: UserId
customDetails:
  NewDeviceCount: NewDeviceCount
  NewDevices: NewDevices
  NewDeviceOSList: NewDeviceOSList
  FirstSeen: FirstSeen
  LastSeen: LastSeen
alertDetailsOverride:
  alertDisplayNameFormat: 'StealthTalk: Multi New Devices - {{UserId}} ({{NewDeviceCount}} new devices)'
  alertDescriptionFormat: '{{AlertDetails}}'
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: 5h
    matchingMethod: Selected
    groupByEntities:
      - Account
suppressionEnabled: false
suppressionDuration: 5h
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let LookbackPeriod = 24h;
let MinNewDevices = 2;

Stage 1: source

StealthTalkAnomalousAuth_CL

Stage 2: where

| where TimeGenerated >= ago(LookbackPeriod)

Stage 3: where

| where EventType == "NewDeviceLogin"

Stage 4: summarize

| summarize
    NewDeviceCount  = dcount(NewDeviceId),
    NewDevices      = make_set(NewDeviceId),
    NewDeviceOSList = make_set(NewDeviceOS),
    FirstSeen       = min(TimeGenerated),
    LastSeen        = max(TimeGenerated),
    AppVersions     = make_set(AppVersion)
  by UserId
Threshold
ge 2

Stage 5: where

| where NewDeviceCount >= MinNewDevices

Stage 6: extend

| extend
    AlertName    = "MultiNewDevicesRegistration",
    AlertDetails = strcat(
        "User ", UserId,
        " registered ", NewDeviceCount, " new devices within 24 hours.",
        " Device IDs: ", tostring(NewDevices), ".",
        " Operating systems: ", tostring(NewDeviceOSList), "."
    )

Stage 7: project

| project
    TimeGenerated = LastSeen,
    UserId, NewDeviceCount, NewDevices, NewDeviceOSList,
    FirstSeen, LastSeen, AppVersions, AlertName, AlertDetails

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.

FieldKindValues
EventTypeeq
  • NewDeviceLogin transforms: cased
NewDeviceCountge
  • 2 transforms: cased

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.

FieldSource
AlertDetailsproject
AlertNameproject
AppVersionsproject
FirstSeenproject
LastSeenproject
NewDeviceCountproject
NewDeviceOSListproject
NewDevicesproject
TimeGeneratedproject
UserIdproject