Domain Generation Algorithms
Adversaries use Domain Generation Algorithms (DGAs) to dynamically identify C2 destinations by algorithmically generating large numbers of candidate domain names. Only the operator-registered domain resolves successfully; all others return NXDOMAIN. This makes blocking impractical — defenders cannot predict the full space of generated domains. DGAs may produce random character strings (e.g., istgmxdejdnxuyla.ru) or concatenate dictionary words (e.g., cityjulydish.net). Many implementations are time-seeded, generating different candidate domains hourly or daily. Some incorporate a shared secret seed to prevent defender prediction. Detection focuses on statistical anomalies: abnormally high NXDOMAIN failure rates from a single host, domain names with low vowel ratios or high character entropy, rapid successive queries to many unique failing domains, and beaconing patterns once a DGA domain resolves. Malware families using DGA include QakBot, Conficker, Ursnif, DarkWatchman, BONDUPDATER, POSHSPY, CHOPSTICK, Aria-body, Milan, SombRAT, and MiniDuke. APT41 changes C2 monthly via DGA; TA551 generates URLs from executed macros.
// T1568.002 — DGA Detection via NXDOMAIN Rate and Domain Entropy Analysis
// Requires: Azure DNS Analytics solution (DnsEvents table) or DNS server logs ingested via AMA/CEF
// Tune NXDomainThreshold and UniqueDomainsThreshold against your environment's DNS failure baseline
let TimeWindow = 1h;
let NXDomainThreshold = 20;
let UniqueDomainsThreshold = 15;
DnsEvents
| where TimeGenerated > ago(TimeWindow)
| where ResultCode == 3 // NXDOMAIN — query returned 'no such domain'
| extend Name = tolower(Name)
| extend DomainParts = split(Name, '.')
| extend SLD = tostring(DomainParts[array_length(DomainParts) - 2])
| where strlen(SLD) >= 7
| extend DomainLength = strlen(SLD)
// Vowel ratio: legitimate human-readable domains average 35-45% vowels
// DGA random-character strings typically have <20% vowels
| extend VowelCount = countof(SLD, 'a') + countof(SLD, 'e') + countof(SLD, 'i') + countof(SLD, 'o') + countof(SLD, 'u')
| extend VowelRatio = todouble(VowelCount) / todouble(DomainLength)
// Digit ratio: many DGAs embed digits; legitimate SLDs rarely exceed 30% digits
| extend DigitCount = countof(SLD, '0') + countof(SLD, '1') + countof(SLD, '2') + countof(SLD, '3') + countof(SLD, '4') + countof(SLD, '5') + countof(SLD, '6') + countof(SLD, '7') + countof(SLD, '8') + countof(SLD, '9')
| extend DigitRatio = todouble(DigitCount) / todouble(DomainLength)
| extend IsLikelyDGA = iff(
VowelRatio < 0.20 // Very few vowels — algorithmically-generated random string
or DigitRatio > 0.35 // High digit density — e.g., Conficker, QakBot variants
or DomainLength > 16, // Abnormally long SLD uncommon in legitimate domains
1, 0)
| summarize
TotalNXDomain = count(),
UniqueNXDomains = dcount(Name),
LikelyDGACount = countif(IsLikelyDGA == 1),
SampleDomains = make_set(Name, 10),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated)
by Computer, ClientIP
| where TotalNXDomain >= NXDomainThreshold and UniqueNXDomains >= UniqueDomainsThreshold
| extend DurationMinutes = iff(
datetime_diff('minute', LastSeen, FirstSeen) < 1,
todouble(1),
todouble(datetime_diff('minute', LastSeen, FirstSeen)))
| extend QueryRate = round(todouble(UniqueNXDomains) / DurationMinutes, 2)
| extend AlertSeverity = case(
LikelyDGACount > 10 and QueryRate > 5.0, 'Critical',
LikelyDGACount > 5 or QueryRate > 2.0, 'High',
'Medium')
| project
Computer, ClientIP,
TotalNXDomain, UniqueNXDomains, LikelyDGACount,
QueryRate, DurationMinutes,
AlertSeverity, SampleDomains,
FirstSeen, LastSeen
| sort by LikelyDGACount desc, TotalNXDomain desc Data Sources
Required Tables
False Positives
- Cloud infrastructure with GUID-based hostnames (Azure, AWS, GCP auto-generated resource names) performing DNS lookups that fail when services are deprovisioned or accessed cross-region
- Security scanning and threat intelligence tools (Nessus, Qualys, Shodan crawlers, passive DNS enrichment pipelines) performing bulk DNS enumeration generating high NXDOMAIN rates
- Content delivery networks using algorithmically-generated short-TTL subdomains with low vowel ratios — some Akamai, Cloudflare, and Fastly edge-node hostnames match entropy thresholds
- Software development and CI/CD pipelines running integration tests that generate randomized ephemeral test domain names, or microservices discovery in misconfigured service meshes
- Misconfigured DNS resolvers, split-horizon DNS setups, or VPN clients resolving internal domains against a public resolver — causing legitimate internal hostnames to return NXDOMAIN
References (12)
- https://attack.mitre.org/techniques/T1568/002/
- https://umbrella.cisco.com/blog/2016/10/10/domain-generation-algorithms-effective/
- http://go.cybereason.com/rs/996-YZT-709/images/Cybereason-Lab-Analysis-Dissecting-DGAs-Eight-Real-World-DGA-Variants.pdf
- https://unit42.paloaltonetworks.com/threat-brief-understanding-domain-generation-algorithms-dga/
- https://arxiv.org/pdf/1611.00791.pdf
- https://datadrivensecurity.info/blog/posts/2014/Oct/dga-part2/
- https://www.welivesecurity.com/2017/12/21/sednit-update-fancy-bear-spent-year/
- https://www.fireeye.com/blog/threat-research/2017/03/dissecting_one_ofap.html
- http://blog.talosintelligence.com/2017/09/avast-distributes-malware.html
- https://learn.microsoft.com/en-us/azure/sentinel/dns-domain-generation-algorithm
- https://docs.microsoft.com/en-us/sysinternals/downloads/sysmon
- https://github.com/redcanaryco/atomic-red-team/blob/master/atomics/T1568.002/T1568.002.md
Unlock Pro Content
Get the full detection package for T1568.002 including response playbook, investigation guide, and atomic red team tests.