Hunting for DNS Tunneling with Zeek
When to Use
- When hunting for data exfiltration over DNS covert channels
- After threat intelligence indicates DNS-based C2 frameworks targeting your industry
- When dns.log shows unusually high query volumes to specific domains
- During investigation of suspected data theft where no HTTP/S exfiltration is found
- When monitoring for tools like iodine, dnscat2, DNSExfiltrator, or DNS-over-HTTPS tunneling
Detection Gaps & Validation
- Low-and-slow tunnels evade volume/length thresholds. Padding and jitter keep queries under the 50-char length and 100/hr volume thresholds; rate-based rules miss them. Lean on per-domain unique-subdomain cardinality and qtype skew over long windows instead.
- DoH/DoT bypasses
dns.log entirely. Encrypted DNS to 8.8.8.8/1.1.1.1 over 443 never appears in Zeek dns.log — pivot to ssl.log JA3 fingerprints and proxy logs for these resolvers.
- Dictionary/word-encoded A-record tunnels stay below the Shannon-entropy >3.5 bits/char threshold; entropy alone is not sufficient.
- Validate the hunt fires: run
iodine, dnscat2, or DNSExfiltrator against a lab domain and confirm Zeek dns.log (query, qtype, query length) plus rita show-dns-tunneling flag it.
- FP tuning: legitimate high-entropy/long subdomains from CDNs, AV/EDR telemetry, and DGA-lookalike SaaS. Whitelist known parents and inspect
qtype distribution (excessive TXT/NULL/CNAME is the stronger signal than A).
Prerequisites
- Zeek deployed on network tap or SPAN port capturing DNS traffic
- Zeek dns.log with full query and response fields
- SIEM platform for dns.log analysis (Splunk, Elastic)
- RITA (Real Intelligence Threat Analytics) for automated DNS analysis
- Passive DNS data for historical domain resolution context
Workflow
- Analyze Query Length Distribution: DNS tunneling encodes data in subdomain labels, producing queries significantly longer than normal. Normal DNS queries average 20-30 characters; tunneling queries often exceed 50+ characters. Calculate mean and standard deviation of query lengths per domain.
- Calculate Subdomain Entropy: Tunneling encodes data using Base32/Base64, producing high-entropy subdomain strings. Calculate Shannon entropy of subdomain labels -- values above 3.5 bits/character strongly suggest encoded data.
- Count Unique Subdomains Per Domain: Legitimate domains have relatively few unique subdomains. DNS tunneling generates hundreds or thousands of unique subdomains under a single parent domain.
- Monitor DNS Record Type Distribution: TXT, NULL, CNAME, and MX records can carry more data than A records. Excessive TXT queries to a single domain indicate data transfer via DNS.
- Detect High Query Volume: Flag domains receiving more than 100 queries per hour from a single source, especially when combined with high subdomain uniqueness.
- Analyze Query Timing: DNS tunneling tools produce regular query patterns (beaconing) or burst patterns (data transfer). Apply frequency analysis to DNS query timestamps.
- Cross-Reference with conn.log: Correlate DNS queries with connection metadata to identify the process or endpoint generating suspicious queries.
- Validate with Domain Intelligence: Check suspicious domains against WHOIS data, certificate transparency, and threat intelligence feeds.
Key Concepts
| Concept |
Description |
| T1071.004 |
Application Layer Protocol: DNS |
| T1048.003 |
Exfiltration Over Alternative Protocol: DNS |
| T1572 |
Protocol Tunneling |
| Shannon Entropy |
Measure of randomness in subdomain strings |
| Zeek dns.log |
DNS query/response metadata |
| RITA |
Automated DNS tunneling detection from Zeek logs |
| iodine |
IPv4-over-DNS tunneling tool |
| dnscat2 |
DNS-based command-and-control tool |
| DNSExfiltrator |
Data exfiltration tool using DNS requests |
Detection Queries
Zeek Script -- DNS Tunnel Detection
@load base/protocols/dns
module DNSTunnel;
export {
redef enum Notice::Type += { DNSTunnel::Long_DNS_Query };
const query_length_threshold = 50 &redef;
const query_count_threshold = 100 &redef;
}
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) {
if ( |query| > query_length_threshold ) {
NOTICE([$note=DNSTunnel::Long_DNS_Query,
$msg=fmt("Long DNS query detected: %s (%d chars)", query, |query|),
$conn=c]);
}
}
Splunk -- DNS Tunneling Indicators from Zeek
index=zeek sourcetype=bro_dns
| rex field=query "(?<subdomain>[^.]+)\.(?<basedomain>[^.]+\.[^.]+)$"
| stats count dc(subdomain) as unique_subs avg(len(query)) as avg_len max(len(query)) as max_len by src basedomain
| where count > 100 AND (unique_subs > 50 OR avg_len > 40)
| sort -unique_subs
Splunk -- High Entropy Subdomain Detection
index=zeek sourcetype=bro_dns
| rex field=query "^(?<subdomain>[^.]+)"
| where len(subdomain) > 20
| eval char_count=len(subdomain)
| stats count dc(query) as unique_queries avg(char_count) as avg_sub_len by src query_type_name basedomain
| where unique_queries > 30 AND avg_sub_len > 25
| sort -unique_queries
RITA Analysis
rita import /path/to/zeek/logs dataset_name
rita show-dns-fqdn-ips-long dataset_name
rita show-exploded-dns dataset_name
rita show-dns-tunneling dataset_name --csv > dns_tunnel_results.csv
Common Scenarios
- dnscat2 C2: Encodes command-and-control traffic in DNS CNAME/TXT queries with Base64-encoded subdomain labels. Produces high query volumes with long, high-entropy subdomains.
- iodine IPv4 Tunnel: Creates a virtual network interface tunneling all IP traffic through DNS. Generates massive DNS query volumes with NULL record types.
- Data Exfiltration via DNS: Sensitive data encoded in subdomain labels (e.g.,
aGVsbG8gd29ybGQ.exfil.attacker.com), sent as A or TXT queries. Each query carries ~63 bytes of data.
- DNS-over-HTTPS Tunneling: Bypasses traditional DNS monitoring by sending DNS queries over HTTPS to public resolvers (8.8.8.8, 1.1.1.1), requiring TLS inspection for detection.
- Cobalt Strike DNS Beacon: Uses DNS A/TXT records for C2 communication with configurable subdomain encoding schemes.
Output Format
Hunt ID: TH-DNSTUNNEL-[DATE]-[SEQ]
Source IP: [Internal IP]
Source Host: [Hostname]
Target Domain: [Base domain]
Query Count: [Total queries in window]
Unique Subdomains: [Count]
Avg Query Length: [Characters]
Max Query Length: [Characters]
Subdomain Entropy: [Bits per character]
Primary Record Type: [A/TXT/CNAME/NULL]
Data Volume Estimate: [Bytes exfiltrated]
Risk Level: [Critical/High/Medium/Low]
1---2name: hunting-for-dns-tunneling-with-zeek3description: Detect DNS tunneling and data exfiltration by analyzing Zeek dns.log for high-entropy subdomain queries, excessive query volume, long query lengths, and unusual DNS record types indicating covert channel communication.4license: Apache-2.05---67# Hunting for DNS Tunneling with Zeek89## When to Use1011- When hunting for data exfiltration over DNS covert channels12- After threat intelligence indicates DNS-based C2 frameworks targeting your industry13- When dns.log shows unusually high query volumes to specific domains14- During investigation of suspected data theft where no HTTP/S exfiltration is found15- When monitoring for tools like iodine, dnscat2, DNSExfiltrator, or DNS-over-HTTPS tunneling1617## Detection Gaps & Validation1819- **Low-and-slow tunnels evade volume/length thresholds.** Padding and jitter keep queries under the 50-char length and 100/hr volume thresholds; rate-based rules miss them. Lean on per-domain unique-subdomain cardinality and qtype skew over long windows instead.20- **DoH/DoT bypasses `dns.log` entirely.** Encrypted DNS to 8.8.8.8/1.1.1.1 over 443 never appears in Zeek `dns.log` — pivot to `ssl.log` JA3 fingerprints and proxy logs for these resolvers.21- **Dictionary/word-encoded A-record tunnels** stay below the Shannon-entropy >3.5 bits/char threshold; entropy alone is not sufficient.22- **Validate the hunt fires:** run `iodine`, `dnscat2`, or `DNSExfiltrator` against a lab domain and confirm Zeek `dns.log` (`query`, `qtype`, `query` length) plus `rita show-dns-tunneling` flag it.23- **FP tuning:** legitimate high-entropy/long subdomains from CDNs, AV/EDR telemetry, and DGA-lookalike SaaS. Whitelist known parents and inspect `qtype` distribution (excessive TXT/NULL/CNAME is the stronger signal than A).2425## Prerequisites2627- Zeek deployed on network tap or SPAN port capturing DNS traffic28- Zeek dns.log with full query and response fields29- SIEM platform for dns.log analysis (Splunk, Elastic)30- RITA (Real Intelligence Threat Analytics) for automated DNS analysis31- Passive DNS data for historical domain resolution context3233## Workflow34351. **Analyze Query Length Distribution**: DNS tunneling encodes data in subdomain labels, producing queries significantly longer than normal. Normal DNS queries average 20-30 characters; tunneling queries often exceed 50+ characters. Calculate mean and standard deviation of query lengths per domain.362. **Calculate Subdomain Entropy**: Tunneling encodes data using Base32/Base64, producing high-entropy subdomain strings. Calculate Shannon entropy of subdomain labels -- values above 3.5 bits/character strongly suggest encoded data.373. **Count Unique Subdomains Per Domain**: Legitimate domains have relatively few unique subdomains. DNS tunneling generates hundreds or thousands of unique subdomains under a single parent domain.384. **Monitor DNS Record Type Distribution**: TXT, NULL, CNAME, and MX records can carry more data than A records. Excessive TXT queries to a single domain indicate data transfer via DNS.395. **Detect High Query Volume**: Flag domains receiving more than 100 queries per hour from a single source, especially when combined with high subdomain uniqueness.406. **Analyze Query Timing**: DNS tunneling tools produce regular query patterns (beaconing) or burst patterns (data transfer). Apply frequency analysis to DNS query timestamps.417. **Cross-Reference with conn.log**: Correlate DNS queries with connection metadata to identify the process or endpoint generating suspicious queries.428. **Validate with Domain Intelligence**: Check suspicious domains against WHOIS data, certificate transparency, and threat intelligence feeds.4344## Key Concepts4546| Concept | Description |47|---------|-------------|48| T1071.004 | Application Layer Protocol: DNS |49| T1048.003 | Exfiltration Over Alternative Protocol: DNS |50| T1572 | Protocol Tunneling |51| Shannon Entropy | Measure of randomness in subdomain strings |52| Zeek dns.log | DNS query/response metadata |53| RITA | Automated DNS tunneling detection from Zeek logs |54| iodine | IPv4-over-DNS tunneling tool |55| dnscat2 | DNS-based command-and-control tool |56| DNSExfiltrator | Data exfiltration tool using DNS requests |5758## Detection Queries5960### Zeek Script -- DNS Tunnel Detection61```zeek62@load base/protocols/dns63module DNSTunnel;6465export {66 redef enum Notice::Type += { DNSTunnel::Long_DNS_Query };67 const query_length_threshold = 50 &redef;68 const query_count_threshold = 100 &redef;69}7071event dns_request(c: connection, msg: dns_msg, query: string, qtype: count, qclass: count) {72 if ( |query| > query_length_threshold ) {73 NOTICE([$note=DNSTunnel::Long_DNS_Query,74 $msg=fmt("Long DNS query detected: %s (%d chars)", query, |query|),75 $conn=c]);76 }77}78```7980### Splunk -- DNS Tunneling Indicators from Zeek81```spl82index=zeek sourcetype=bro_dns83| rex field=query "(?<subdomain>[^.]+)\.(?<basedomain>[^.]+\.[^.]+)$"84| stats count dc(subdomain) as unique_subs avg(len(query)) as avg_len max(len(query)) as max_len by src basedomain85| where count > 100 AND (unique_subs > 50 OR avg_len > 40)86| sort -unique_subs87```8889### Splunk -- High Entropy Subdomain Detection90```spl91index=zeek sourcetype=bro_dns92| rex field=query "^(?<subdomain>[^.]+)"93| where len(subdomain) > 2094| eval char_count=len(subdomain)95| stats count dc(query) as unique_queries avg(char_count) as avg_sub_len by src query_type_name basedomain96| where unique_queries > 30 AND avg_sub_len > 2597| sort -unique_queries98```99100### RITA Analysis101```bash102rita import /path/to/zeek/logs dataset_name103rita show-dns-fqdn-ips-long dataset_name104rita show-exploded-dns dataset_name105rita show-dns-tunneling dataset_name --csv > dns_tunnel_results.csv106```107108## Common Scenarios1091101. **dnscat2 C2**: Encodes command-and-control traffic in DNS CNAME/TXT queries with Base64-encoded subdomain labels. Produces high query volumes with long, high-entropy subdomains.1112. **iodine IPv4 Tunnel**: Creates a virtual network interface tunneling all IP traffic through DNS. Generates massive DNS query volumes with NULL record types.1123. **Data Exfiltration via DNS**: Sensitive data encoded in subdomain labels (e.g., `aGVsbG8gd29ybGQ.exfil.attacker.com`), sent as A or TXT queries. Each query carries ~63 bytes of data.1134. **DNS-over-HTTPS Tunneling**: Bypasses traditional DNS monitoring by sending DNS queries over HTTPS to public resolvers (8.8.8.8, 1.1.1.1), requiring TLS inspection for detection.1145. **Cobalt Strike DNS Beacon**: Uses DNS A/TXT records for C2 communication with configurable subdomain encoding schemes.115116## Output Format117118```119Hunt ID: TH-DNSTUNNEL-[DATE]-[SEQ]120Source IP: [Internal IP]121Source Host: [Hostname]122Target Domain: [Base domain]123Query Count: [Total queries in window]124Unique Subdomains: [Count]125Avg Query Length: [Characters]126Max Query Length: [Characters]127Subdomain Entropy: [Bits per character]128Primary Record Type: [A/TXT/CNAME/NULL]129Data Volume Estimate: [Bytes exfiltrated]130Risk Level: [Critical/High/Medium/Low]131```