Detection rules › Kusto

Potential DGA(Domain Generation Algorithm) detected via Repetitive Failures - Static threshold based (ASIM DNS Solution)

Status
available
Severity
medium
Time window
10d
Group by
DNSQueryCount, SrcIpAddr
Source
github.com/Azure/Azure-Sentinel

This rule identifies clients with a high NXDomain count, which could be indicative of a DGA (cycling through possible C2 domains where most C2s are not live). An alert is generated when a new IP address is seen (based on not being seen associated with NXDomain records in prior 10-day baseline period). It utilizes ASIM normalization and is applied to any source that supports the ASIM DNS schema.

MITRE ATT&CK coverage

TacticTechniques
Command & ControlT1008 Fallback Channels, T1568 Dynamic Resolution

Event coverage

ProviderEventTitle
SysmonEvent ID 22DNSEvent (DNS query)

Rule body kusto

id: 89ba52fa-96a7-4653-829a-ca49bb13336c
name: Potential DGA(Domain Generation Algorithm) detected via Repetitive Failures - Static threshold based (ASIM DNS Solution)
description: |
  'This rule identifies clients with a high NXDomain count, which could be indicative of a DGA (cycling through possible C2 domains where most C2s are not live). An alert is generated when a new IP address is seen (based on not being seen associated with NXDomain records in prior 10-day baseline period). It utilizes [ASIM](https://aka.ms/AboutASIM) normalization and is applied to any source that supports the ASIM DNS schema.'
severity: Medium
status: Available 
tags:
  - Schema: ASimDns
    SchemaVersion: 0.1.6
requiredDataConnectors: []
queryFrequency: 1d
queryPeriod: 10d
triggerOperator: gt
triggerThreshold: 0
tactics:
  - CommandAndControl
relevantTechniques:
  - T1568
  - T1008
query: |
  let threshold = 100;
  let lookback = 10d;
  let referenceendtime = 1d;
  let nxDomainDnsEvents = (stime: datetime, etime: datetime) {
    _Im_Dns(responsecodename='NXDOMAIN', starttime=stime, endtime=etime)
    | where DnsQueryTypeName in ("A", "AAAA")
    | where ipv4_is_match("127.0.0.1", SrcIpAddr) == False
    | where DnsQuery !contains "/" and DnsQuery contains "."
  };
  nxDomainDnsEvents (stime=ago(referenceendtime), etime=now())
  | summarize
    StartTimeUtc = min(TimeGenerated),
    EndTimeUtc = max(TimeGenerated),
    DNSQueryCount=dcount(DnsQuery)
    by SrcIpAddr
  | where DNSQueryCount > threshold
  // Filter out previously seen IPs
  | join kind=leftanti (nxDomainDnsEvents (stime=ago(lookback), etime=ago(referenceendtime))
    | summarize DNSQueryCount=dcount(DnsQuery) by SrcIpAddr, bin(TimeGenerated,1d)
    | where DNSQueryCount > threshold)
    on SrcIpAddr
  // Pull out sample NXDomain responses for those remaining potentially infected IPs
  | join kind = inner (nxDomainDnsEvents (stime=ago(lookback), etime=now())
    | summarize by DnsQuery, SrcIpAddr)
    on SrcIpAddr
  | summarize
    StartTimeUtc = min(StartTimeUtc),
    EndTimeUtc = max(EndTimeUtc),
    DNSQueries=make_list(DnsQuery, 100)
    by SrcIpAddr, DNSQueryCount
  | extend DNSQueryThreshold=threshold
entityMappings:
  - entityType: IP
    fieldMappings:
      - identifier: Address
        columnName: SrcIpAddr
eventGroupingSettings:
  aggregationKind: AlertPerResult
customDetails:
  DNSQueries: DNSQueries
  DNSQueryThreshold: DNSQueryThreshold
  DNSQueryCount: DNSQueryCount
alertDetailsOverride:
  alertDisplayNameFormat: "[Static threshold] Potential DGA (Domain Generation Algorithm) originating from client IP: '{{SrcIpAddr}}' has been detected."
  alertDescriptionFormat: "Client has been identified with high NXDomain count which could be indicative of a DGA (cycling through possible C2 domains where most C2s are not live). This client is found to be communicating with multiple Domains which do not exist.\n\nDGA DNS query count baseline is: '{{DNSQueryThreshold}}'\n\nCurrent failed DNS query count from this client: '{{DNSQueryCount}}'\n\nDNS queries requested by this client inlcude: '{{DNSQueries}}'"
version: 1.0.2
kind: Scheduled

Stages and Predicates

Parameters

let threshold = 100;
let lookback = 10d;
let referenceendtime = 1d;

The stages below define let nxDomainDnsEvents (the rule's main pipeline source).

Stage 1: source

_Im_Dns

Stage 2: where

where DnsQueryTypeName in~ ("A", "AAAA")

Stage 3: where

where not (ipv4_is_in_range(SrcIpAddr, "127.0.0.1"))

Stage 4: where

where not (DnsQuery contains "/") and DnsQuery contains "."

The stages below run on nxDomainDnsEvents (the outer pipeline).

Stage 5: summarize

summarize DNSQueryCount, EndTimeUtc, StartTimeUtc by SrcIpAddr
Threshold
gt 100

Stage 6: where

where DNSQueryCount > 100

Stage 7: join (negated)

join kind=leftanti (...)

Stage 8: join

join kind=inner (...)

Stage 9: summarize

summarize DNSQueries, EndTimeUtc, StartTimeUtc by SrcIpAddr, DNSQueryCount

Stage 10: extend

extend DNSQueryThreshold

Exclusions

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

FieldKindExcluded values
SrcIpAddrcidr_match127.0.0.1
DnsQuerycontains/
DNSQueryCountgt100

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
DNSQueryCountgt
  • 100 transforms: cased
DnsQuerycontains
  • . corpus 2 (kusto 2)
DnsQueryTypeNamein
  • A transforms: cased
  • AAAA 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
DNSQueriessummarize
DNSQueryCountsummarize
EndTimeUtcsummarize
SrcIpAddrsummarize
StartTimeUtcsummarize
DNSQueryThresholdextend