Detection rules › Kusto

Rare client observed with high reverse DNS lookup count

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

'Identifies clients with a high reverse DNS counts that could be carrying out reconnaissance or discovery activity. Alerts are generated if the IP performing such reverse DNS lookups was not seen doing so in the preceding 7-day period.'

MITRE ATT&CK coverage

TacticTechniques
DiscoveryT1046 Network Service Discovery

Rule body kusto

id: 15ae38a2-2e29-48f7-883f-863fb25a5a06
name: Rare client observed with high reverse DNS lookup count
description: |
  'Identifies clients with a high reverse DNS counts that could be carrying out reconnaissance or discovery activity.
  Alerts are generated if the IP performing such reverse DNS lookups was not seen doing so in the preceding 7-day period.'
severity: Medium
status: Available
requiredDataConnectors:
  - connectorId: DNS
    dataTypes:
      - DnsEvents
queryFrequency: 1d
queryPeriod: 8d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - Discovery
relevantTechniques:
  - T1046
query: |
  let starttime = 8d;
  let endtime = 1d;
  let threshold = 10;
  DnsEvents
  | where TimeGenerated > ago(endtime)
  | where Name has "in-addr.arpa"
  | summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), dcount(Name), ReverseDNSLookup_List = make_set(Name,100) by ClientIP
  | where dcount_Name > threshold
  | project StartTimeUtc, EndTimeUtc, ClientIP , dcount_Name, ReverseDNSLookup_List
  // Filter out previously seen IPs
  // Returns all the records from the left side that don't have matches from the right
  | join kind=leftanti (DnsEvents
      | where TimeGenerated between(ago(starttime)..ago(endtime))
      | where Name has "in-addr.arpa"
      | summarize dcount(Name) by ClientIP, bin(TimeGenerated, 1d)
      | where dcount_Name > threshold
      | project ClientIP , dcount_Name
  ) on ClientIP
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: ClientIP
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let starttime = 8d;
let endtime = 1d;
let threshold = 10;

Stage 1: source

DnsEvents

Stage 2: where

| where TimeGenerated > ago(endtime)

Stage 3: where

| where Name has "in-addr.arpa"

Stage 4: summarize

| summarize StartTimeUtc = min(TimeGenerated), EndTimeUtc = max(TimeGenerated), dcount(Name), ReverseDNSLookup_List = make_set(Name,100) by ClientIP

Stage 5: where

| where dcount_Name > threshold

Stage 6: project

| project StartTimeUtc, EndTimeUtc, ClientIP , dcount_Name, ReverseDNSLookup_List

Stage 7: join (negated)

| join kind=leftanti (DnsEvents
    | where TimeGenerated between(ago(starttime)..ago(endtime))
    | where Name has "in-addr.arpa"
    | summarize dcount(Name) by ClientIP, bin(TimeGenerated, 1d)
    | where dcount_Name > threshold
    | project ClientIP , dcount_Name
) on ClientIP

Exclusions

Top-level NOT(...) conjuncts: predicates this rule actively suppresses.

FieldKindExcluded values
Namematchin-addr.arpa
dcount_Namegt10

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
Namematch
  • in-addr.arpa transforms: term
dcount_Namegt
  • 10 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
ClientIPproject
EndTimeUtcproject
ReverseDNSLookup_Listproject
StartTimeUtcproject
dcount_Nameproject