// Why DNS?
DNS is the internet's phone book � it resolves domain names to IP addresses. Critically for defenders, almost every network connection starts with a DNS query. This makes DNS logs an exceptionally rich data source for threat hunting: you can see what domains endpoints are resolving, detect malware C2 communication, identify data exfiltration via DNS tunneling, and spot DGA (Domain Generation Algorithm) malware before it successfully reaches a live C2 server.
DNS is also frequently overlooked. Many organisations have robust firewall and endpoint logging but sparse or no DNS query logging, creating a significant blind spot that attackers exploit.
Sysmon Event ID 22 captures DNS queries on Windows endpoints. For network-level DNS, enable logging on your DNS resolver (Windows DNS Server, Pi-hole, BIND) or use a cloud DNS service with logging such as Cloudflare Gateway or Route 53 Resolver Query Logging.
// DNS Record Types
| Record | Purpose | Abuse Potential |
|---|---|---|
A | Maps hostname to IPv4 address | Most common for C2; watch for fast-flux (A records rotating every few seconds) |
AAAA | Maps hostname to IPv6 address | IPv6 C2 often bypasses IPv4-only firewall rules |
CNAME | Alias pointing to another hostname | Used in domain fronting; long CNAME chains can obscure real destination |
MX | Mail exchange record | Low abuse, but querying unexpected MX records may indicate mail-related recon |
TXT | Freeform text � used for SPF, DKIM, domain verification | High: TXT records commonly used for DNS tunneling data exfiltration and C2 |
NS | Nameserver for a domain | Attacker-controlled NS can reroute queries; watch for rare NS changes |
PTR | Reverse DNS (IP to hostname) | Reconnaissance � reverse lookups of internal IPs may indicate lateral movement |
ANY | Request all record types | Used in DNS amplification DDoS; endpoints querying ANY are unusual |
// Suspicious Patterns
| Pattern | Indicator | Why It's Suspicious |
|---|---|---|
| High-entropy domain names | x7kqpz2m.evil.com | DGA � algorithmically generated names have high Shannon entropy |
| Very short TTL | TTL < 60 seconds | Fast-flux � rotating IPs to evade blocklists and attribution |
| Very long subdomain labels | AAAAAAAAAAAAAAAAAAAAAA.evil.com | DNS tunneling � data encoded in the subdomain portion of the query |
| High query volume to one domain | 1000+ queries/hour from one host | DNS beaconing � malware polling C2 via frequent DNS requests |
| Queries to non-existent domains (NXDOMAIN storms) | Many NXDOMAIN responses in sequence | DGA malware failing to reach live C2 domains � still highly diagnostic |
| Newly registered domain | Domain registered < 30 days ago | Attackers often register domains shortly before use |
| Direct IP resolution bypassing DNS | Outbound connection to IP with no prior DNS query | Malware hardcoding C2 IP � bypasses DNS-based controls |
| TXT record queries from endpoints | Internal host querying TXT records | Legitimate apps rarely query TXT; common in DNS tunneling |
// DGA Detection
Domain Generation Algorithms (DGAs) are used by malware to generate thousands of pseudo-random domain names. Only the attacker knows which domains are live on a given day � this makes blocklisting impractical. Detection relies on spotting the statistical properties of DGA names.
DGA indicators
| Indicator | Normal Range | DGA Range |
|---|---|---|
| Shannon entropy of domain name | 2.0�3.5 bits | 3.5�5.0+ bits |
| Consonant-to-vowel ratio | 1.5�2.5 | 4.0+ (lots of consonants) |
| Presence in Alexa/Tranco top 1M | Yes | No |
| Trigram frequency | High (real words) | Low (random character sequences) |
| NXDOMAIN rate for the domain | Low | High � most DGA domains don't resolve |
NXDOMAIN storms are often more diagnostic than the domains themselves. If a host generates 500 NXDOMAIN responses in an hour for domains that look like xkqvzrpml.com, it's likely running DGA malware � even if you can't identify the specific family.
// DNS Tunneling
DNS tunneling encodes arbitrary data (commands, stolen files, C2 traffic) inside DNS queries and responses � exploiting the fact that DNS is almost never blocked at the perimeter. Tools like iodine, dnscat2, and DNSExfiltrator are commonly used.
Detection indicators
| Indicator | Description |
|---|---|
| Long subdomain labels | Labels approaching the 63-character limit packed with base32/base64 encoded data |
| High query volume per domain | Hundreds of queries per minute to the same authoritative domain |
| Large TXT or NULL record responses | Response payload carrying encoded commands or shell output |
| Uncommon record types | TXT, NULL, CNAME, MX used for data exfil instead of A/AAAA |
| Unique subdomains per query | Every query has a different subdomain � each carrying a unique data chunk |
| Data rate anomaly | DNS data volume far exceeds what resolution alone would require |
// C2 over DNS
Beyond full tunneling, many malware families use DNS for lightweight C2 beaconing � periodically resolving a domain and using the returned IP address as an encoded command. This is harder to detect because each individual query looks legitimate.
Beaconing detection
Look for regularity � malware beacons on a fixed schedule while humans browse irregularly. A host querying the same domain every 60 seconds for hours is a strong beacon signal. Use jitter analysis (standard deviation of query intervals) to separate malware from legitimate polling software.
// Hunting Queries
High-volume DNS queries from single endpoint (beaconing)
Event
| where Source == "Microsoft-Windows-Sysmon"
| where EventID == 22
| where TimeGenerated > ago(1h)
| extend QueryName = extract(@"QueryName: ([^\s]+)", 1, RenderedDescription)
| summarize QueryCount = count(), Domains = dcount(QueryName) by Computer
| where QueryCount > 500
| sort by QueryCount desc
Long subdomain labels (DNS tunneling)
Event
| where Source == "Microsoft-Windows-Sysmon" and EventID == 22
| extend QueryName = extract(@"QueryName: ([^\s]+)", 1, RenderedDescription)
| extend NameLen = strlen(QueryName)
| where NameLen > 60
| project TimeGenerated, Computer, QueryName, NameLen
| sort by NameLen desc
NXDOMAIN storm detection (DGA)
// Requires DNS server logs with response codes ingested into Sentinel
DnsEvents
| where TimeGenerated > ago(1h)
| where ResultCode == 3 // NXDOMAIN
| summarize NXCount = count() by ClientIP
| where NXCount > 100
| sort by NXCount desc