DNS tunneling abuses DNS queries/responses to transport arbitrary payloads, typically encoding exfiltrated data in the left‑most subdomain labels.
Detection checklist
1. Query volume – Flag domains that receive >100 DNS queries/min from a single client.
tshark -Y "dns && frame.time_delta < 0.6" -T fields -e dns.qry.name | sort | uniq -c | awk '$1>100'2. Label length & entropy – Compute average label length >30 bytes and Shannon entropy >4.0 bits/byte.
import math, collections, sys
def shannon(s):
p = collections.Counter(s)
return -sum((c/len(s))*math.log2(c/len(s)) for c in p.values())3. Unusual record types – Look for high rates of TXT, NULL, or TYPE99 records.
zeek -r capture.pcap DNS | grep -E "TXT|NULL|TYPE99"4. Static vs dynamic domains – Domains with rapidly changing sub‑domains (e.g., base64 strings) indicate data encoding.
5. Response size – Answers >512 bytes (or >4096 bytes with EDNS0) without legitimate purpose are suspicious.
Comparison table
| Indicator | Normal traffic | Tunneling pattern |
|----------------------|----------------|-------------------|
| Queries/min per host | ≤20 | >100 |
| Avg label length | ≤15 bytes | ≥30 bytes |
| Entropy (bits/byte) | ≤3.5 | ≥4.0 |
| TXT/NULL usage | ≤5 % of queries| >30 % of queries |
Tooling tip – Deploy Zeek’s dns_exfiltration script (part of the policy package) and tune dns_exfiltration_threshold to 0.8 % of total DNS traffic.
Gotcha – Encrypted DNS (DoH/DoT) bypasses network‑level inspection; place detection at the recursive resolver or use TLS‑termination proxies to retain visibility.