Detection rules › Kusto

Whisper Security - Domain Registrar Change Anomaly

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

Detects domains whose registrar has changed across WHOIS history snapshots within the last 30 days. Registrar changes may indicate domain hijacking or adversary infrastructure acquisition.

MITRE ATT&CK coverage

TacticTechniques
Resource Development

Rule body

id: b6e26c67-f596-5c0f-8614-c88d715508c5
name: Whisper Security - Domain Registrar Change Anomaly
description: |
  Detects domains whose registrar has changed across WHOIS history snapshots within the last 30 days. Registrar changes may indicate domain hijacking or adversary infrastructure acquisition.
severity: Medium
status: Available
requiredDataConnectors:
- connectorId: WhisperSecurityConnector
  dataTypes:
  - WhisperHistory_CL
queryFrequency: 1h
queryPeriod: 14d
triggerOperator: gt
triggerThreshold: 0
tactics:
- ResourceDevelopment
relevantTechniques:
- T1584
query: |
  // MITRE ATT&CK: T1584 - Compromise Infrastructure
  // Tactic: Resource Development
  // Detects registrar changes across WHOIS history snapshots
  let lookbackPeriod = 14d;
  WhisperHistory_CL
      | where TimeGenerated > ago(lookbackPeriod)
      | where indicatorType == "domain"
      | where isnotempty(registrar)
      | summarize Registrars = make_set(registrar), SnapshotCount = count(), EarliestSnapshot = min(snapshotDate), LatestSnapshot = max(snapshotDate) by indicator
      | where array_length(Registrars) > 1
      | mv-expand CurrentRegistrar = Registrars
      | extend CurrentRegistrar = tostring(CurrentRegistrar)
      | join kind=inner (
          WhisperHistory_CL
          | where TimeGenerated > ago(lookbackPeriod)
          | where indicatorType == "domain"
          | where isnotempty(registrar)
          | serialize
          | extend PreviousRegistrar = prev(registrar), PreviousIndicator = prev(indicator)
          | where indicator == PreviousIndicator
          | where registrar != PreviousRegistrar
          | where isnotempty(PreviousRegistrar)
          | project indicator, OldRegistrar = PreviousRegistrar, NewRegistrar = registrar, ChangeDate = snapshotDate
      ) on indicator
      | project TimeGenerated = ChangeDate, DnsDomain = indicator, OldRegistrar, NewRegistrar, ChangeDate, SnapshotCount
      | distinct TimeGenerated, DnsDomain, OldRegistrar, NewRegistrar, ChangeDate, SnapshotCount
entityMappings:
- entityType: DNS
  fieldMappings:
  - identifier: DomainName
    columnName: DnsDomain
customDetails:
  OldRegistrar: OldRegistrar
  NewRegistrar: NewRegistrar
  ChangeDate: ChangeDate
incidentConfiguration:
  createIncident: true
  groupingConfiguration:
    enabled: true
    reopenClosedIncident: false
    lookbackDuration: PT5H
    matchingMethod: AllEntities
version: 1.0.0
kind: Scheduled

Stages and Predicates

Parameters

let lookbackPeriod = 14d;

Stage 1: source

WhisperHistory_CL

Stage 2: where

| where TimeGenerated > ago(lookbackPeriod)

Stage 3: where

| where indicatorType == "domain"

Stage 4: where

| where isnotempty(registrar)

Stage 5: summarize

| summarize Registrars = make_set(registrar), SnapshotCount = count(), EarliestSnapshot = min(snapshotDate), LatestSnapshot = max(snapshotDate) by indicator

Stage 6: where

| where array_length(Registrars) > 1

Stage 7: mv-expand

| mv-expand CurrentRegistrar = Registrars

Stage 8: extend

| extend CurrentRegistrar = tostring(CurrentRegistrar)

Stage 9: join

| join kind=inner (
        WhisperHistory_CL
        | where TimeGenerated > ago(lookbackPeriod)
        | where indicatorType == "domain"
        | where isnotempty(registrar)
        | serialize
        | extend PreviousRegistrar = prev(registrar), PreviousIndicator = prev(indicator)
        | where indicator == PreviousIndicator
        | where registrar != PreviousRegistrar
        | where isnotempty(PreviousRegistrar)
        | project indicator, OldRegistrar = PreviousRegistrar, NewRegistrar = registrar, ChangeDate = snapshotDate
    ) on indicator

Stage 10: project

| project TimeGenerated = ChangeDate, DnsDomain = indicator, OldRegistrar, NewRegistrar, ChangeDate, SnapshotCount

Stage 11: distinct

| distinct TimeGenerated, DnsDomain, OldRegistrar, NewRegistrar, ChangeDate, SnapshotCount

Indicators

These rows show field, operator, and value matches.

Output fields

These fields are emitted when the rule matches.

FieldSource
ChangeDateproject
DnsDomainproject
NewRegistrarproject
OldRegistrarproject
SnapshotCountproject
TimeGeneratedproject