↳ Source GitHubRègle analytiqueMedium

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

Description

'This rule makes use of the series decompose anomaly method to detect clients with a high NXDomain response count, which could be indicative of a DGA (cycling through possible C2 domains where most C2s are not live). An alert is generated when new IP address DNS activity is identified as an outlier when compared to the baseline, indicating a recurring pattern. It utilizes [ASIM](https://aka.ms/AboutASIM) normalization and is applied to any source that supports the ASIM DNS schema. The rule also depends on the "**DNSEssentialsCustomParser**" Parser, so please make sure it is installed and configured before you use this Rule.'
Type de règle
Scheduled
Version
1.0.3
Statut déclaré
Available
Fréquence
1d
Période analysée
14d
Déclenchement
gt 0

Couverture MITRE déclarée

Requête KQL

Requête originale, sans modification.

let threshold = 2.5;
let min_t = ago(14d);
let max_t = now();
let timeframe = 1d;
// calculate avg. eps(events per second)
let eps = materialize (_Im_Dns
  | project TimeGenerated
  | where TimeGenerated > ago(5m)
  | count
  | extend Count = Count / 300);
let maxSummarizedTime = toscalar (
  union isfuzzy=true 
      (
      DNSEssentialsCustomParser
      | where Type in ("DNS_Summarized_Logs_ip_CL", "DNS_Summarized_Logs_ipV1_CL") 
      | where EventTime_t >= min_t
      | summarize max_TimeGenerated=max(EventTime_t)
      | extend max_TimeGenerated = datetime_add('hour', 1, max_TimeGenerated)
      ),
      (
      print(min_t)
      | project max_TimeGenerated = print_0
      )
  | summarize maxTimeGenerated = max(max_TimeGenerated) 
  );
let summarizationexist = materialize(
  union isfuzzy=true 
      (
      DNSEssentialsCustomParser
      | where Type in ("DNS_Summarized_Logs_ip_CL", "DNS_Summarized_Logs_ipV1_CL")
      | where EventTime_t > ago(1d) 
      | project v = int(2)
      ),
      (
      print int(1) 
      | project v = print_0
      )
  | summarize maxv = max(v)
  | extend sumexist = (maxv > 1)
  );
let allData = union isfuzzy=true
      (
      (datatable(exists: int, sumexist: bool)[1, false]
      | where toscalar(eps) > 1000
      | join (summarizationexist) on sumexist)
      | join (
          _Im_Dns(responsecodename='NXDOMAIN', starttime=todatetime(ago(2d)), endtime=now())
          | where TimeGenerated > maxSummarizedTime
          | summarize Count=count() by SrcIpAddr, DnsQuery, bin(TimeGenerated, 1h)
          | extend EventTime = TimeGenerated, Count = toint(Count), exists=int(1)
          )
          on exists
      | project-away exists, maxv, sum*
      ),
      (
      (datatable(exists: int, sumexist: bool)[1, false]
      | where toscalar(eps) between (501 .. 1000)
      | join (summarizationexist) on sumexist)
      | join (
          _Im_Dns(responsecodename='NXDOMAIN', starttime=todatetime(ago(3d)), endtime=now())
          | where TimeGenerated > maxSummarizedTime
          | summarize Count=count() by SrcIpAddr, DnsQuery, bin(TimeGenerated, 1h)
          | extend EventTime = TimeGenerated, Count = toint(Count), exists=int(1)
          )
          on exists
      | project-away exists, maxv, sum*
      ),
      (
      (datatable(exists: int, sumexist: bool)[1, false]
      | where toscalar(eps) <= 500
      | join (summarizationexist) on sumexist)
      | join (
          _Im_Dns(responsecodename='NXDOMAIN', starttime=todatetime(ago(4d)), endtime=now())
          | where TimeGenerated > maxSummarizedTime
          | summarize Count=count() by SrcIpAddr, DnsQuery, bin(TimeGenerated, 1h)
          | extend EventTime = TimeGenerated, Count = toint(Count), exists=int(1)
          )
          on exists
      | project-away exists, maxv, sum*
      ),
      (
      DNSEssentialsCustomParser
      | where Type in ("DNS_Summarized_Logs_ip_CL", "DNS_Summarized_Logs_ipV1_CL")
      | where EventTime_t > min_t and EventResultDetails_s == 'NXDOMAIN'
      | project-rename
          SrcIpAddr=SrcIpAddr_s,
          DnsQuery=DnsQuery_s,
          Count=count__d,
          EventTime=EventTime_t
      | extend Count = toint(Count)
      );
allData
| make-series QueryCount=dcount(DnsQuery) on EventTime from min_t to max_t step timeframe by SrcIpAddr
// include calculated Anomalies, Score and Baseline
| extend (anomalies, score, baseline) = series_decompose_anomalies(QueryCount, threshold, -1, 'linefit')
| mv-expand anomalies, score, baseline, EventTime, QueryCount
| extend
  anomalies = toint(anomalies),
  score = toint(score),
  baseline = toint(baseline),
  EventTime = todatetime(EventTime),
  Total = tolong(QueryCount)
| where EventTime >= ago(timeframe)
| where score >= threshold * 2
// Join allData to include DnsQuery details
| join kind=inner(allData
  | where TimeGenerated >= ago(timeframe)
  | summarize DNSQueries = make_set(DnsQuery, 1000) by SrcIpAddr)
  on SrcIpAddr
| project-away SrcIpAddr1

Entités déclarées

IP

Contenus associés

Liens établis à partir des identifiants déclarés et des manifests des solutions.

Traçabilité de la source

GitHub

Les valeurs affichées proviennent des fichiers du dépôt Azure/Azure-Sentinel. Elles décrivent le modèle publié, pas la configuration de votre workspace.

Identifiant source
01191239-274e-43c9-b154-3a042692af06
Autres fichiers source 2Solutions/DNS Essentials/Analytic Rules/PotentialDGADetectedviaRepetitiveFailuresAnomalyBased.yamlsource ↗Solutions/DNS Essentials/Data/Solution_DNS.jsonsolution-membership ↗
GSTEP / SUIVI DU CATALOGUE

Ajouté au catalogue : 16 sept. 2026 · 05:49 UTC
Dernier changement observé : 16 sept. 2026 · 05:49 UTC

Dates de synchronisation GSTEP, distinctes des dates de publication du contenu source.