What it does. Detects DNS tunneling and covert-channel data exfiltration by analyzing Part of mukul975/Anthropic-Cybersecurity-Skills (817 security skills) (mukul975/Anthropic-Cybersecurity-Skills).
Install
npx skills add mukul975/Anthropic-Cybersecurity-Skills --skill hunting-for-dns-tunneling-with-zeek, or copy the skill folder into ~/.claude/skills/hunting-for-dns-tunneling-with-zeek/.
- Raw file:
curl -sL https://raw.githubusercontent.com/mukul975/Anthropic-Cybersecurity-Skills/HEAD/skills/hunting-for-dns-tunneling-with-zeek/SKILL.md
SKILL.md (verbatim)
name: hunting-for-dns-tunneling-with-zeek
description: Detects DNS tunneling and covert-channel data exfiltration by analyzing
Zeek dns.log for high-entropy subdomain queries, excessive query volume, abnormally
long query lengths, and unusual DNS record types (TXT/NULL/CNAME). Use when hunting
for DNS-based data exfiltration or C2 covert channels in network traffic, or when
triaging suspicious DNS query volume/patterns surfaced by Zeek logs.
domain: cybersecurity
subdomain: threat-hunting
tags:
- threat-hunting
- dns-tunneling
- zeek
- data-exfiltration
- covert-channel
- mitre-t1071-004
- network-monitoring
version: '1.0'
author: mahipal
license: Apache-2.0
d3fend_techniques:
- Application Protocol Command Analysis
- Network Isolation
- Network Traffic Analysis
- Client-server Payload Profiling
- DNS Traffic Analysis
nist_csf:
- DE.CM-01
- DE.AE-02
- DE.AE-07
- ID.RA-05
mitre_attack:
- T1046
- T1057
- T1082
- T1083
- T1048
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
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.
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]
Other files in this skill
assets/template.md (verbatim)
DNS Tunneling Hunt Template
| Field |
Value |
| Hunt ID |
TH-DNSTUNNEL-YYYY-MM-DD-NNN |
| Analyst |
|
| Date |
|
| Status |
[ ] In Progress / [ ] Complete |
Hypothesis
Adversaries are using DNS tunneling to establish covert C2 channels or exfiltrate data by encoding information in DNS query subdomain labels.
DNS Tunneling Findings
| # |
Source IP |
Host |
Domain |
Queries |
Unique Subs |
Avg Length |
Entropy |
Record Types |
Risk |
| 1 |
|
|
|
|
|
|
|
|
|
Data Exfiltration Estimate
| Domain |
Total Queries |
Avg Subdomain Size |
Estimated Data Volume |
Assessment |
|
|
|
|
|
Recommendations
- Sinkhole: [DNS domains to sinkhole]
- Block: [Domains at DNS resolver and firewall]
- Isolate: [Source endpoints for investigation]
- Monitor: [Deploy DNS tunneling detection rules]
references/api-reference.md (verbatim)
API Reference: DNS Tunneling Detection with Zeek
Detection Heuristics
| Indicator |
Threshold |
Score |
| Shannon entropy |
> 3.5 |
+40 |
| Avg subdomain length |
> 30 chars |
+30 |
| Tunnel query type ratio |
> 50% TXT/NULL/CNAME |
+20 |
| High query volume |
> 500 queries |
+10 |
Zeek dns.log Fields
| Index |
Field |
Description |
| 0 |
ts |
Timestamp |
| 2 |
id.orig_h |
Source IP |
| 4 |
id.resp_h |
DNS server |
| 9 |
query |
Query name |
| 13 |
qtype_name |
Query type (A, TXT, etc.) |
| 21 |
answers |
Response answers |
| Tool |
Encoding |
Query Type |
| iodine |
Base128 |
NULL, TXT |
| dnscat2 |
Hex/Base64 |
CNAME, TXT, MX |
| dns2tcp |
Base64 |
TXT |
| Cobalt Strike |
Hex |
A, AAAA, TXT |
Shannon Entropy Reference
| Data Type |
Entropy |
| Normal hostnames |
2.0 - 3.0 |
| Base32 encoded |
3.5 - 4.0 |
| Base64 encoded |
4.0 - 5.0 |
| Hex encoded |
3.5 - 4.0 |
Python Libraries
| Library |
Use |
math |
Entropy calculation |
csv |
TSV log parsing |
collections.defaultdict |
Domain aggregation |
dpkt |
PCAP DNS parsing |
dnslib |
DNS packet construction |
Zeek Scripts for DNS Analysis
@load base/protocols/dns
redef DNS::max_pending_queries = 1000;
event dns_request(c: connection, msg: dns_msg, query: string, qtype: count) {
if (|query| > 50) print fmt("Long query: %s", query);
}
references/standards.md (verbatim)
Standards and References - DNS Tunneling Detection
MITRE ATT&CK References
| Technique |
Name |
Description |
| T1071.004 |
Application Layer Protocol: DNS |
DNS-based C2 communication |
| T1048.003 |
Exfiltration Over Unencrypted Non-C2 Protocol |
Data theft via DNS |
| T1572 |
Protocol Tunneling |
IP-over-DNS tunneling |
| T1568.002 |
Domain Generation Algorithms |
Algorithmically generated domains |
| T1132.001 |
Data Encoding: Standard Encoding |
Base32/64 in DNS queries |
DNS Tunneling Detection Thresholds
| Indicator |
Threshold |
Rationale |
| Query length |
> 50 characters |
Normal queries average 20-30 chars |
| Subdomain label length |
> 30 characters |
Max label is 63; tunneling uses near-max |
| Subdomain entropy |
> 3.5 bits/char |
Base32/64 encoding produces high entropy |
| Unique subdomains per domain |
> 100/hour |
Legitimate domains have few unique subs |
| Query volume to single domain |
> 100/hour |
Sustained high volume indicates tunneling |
| TXT record query ratio |
> 50% to domain |
TXT queries carry more data |
| NULL record queries |
Any volume |
Rarely used legitimately |
| Tool |
Protocol |
Record Types |
Data Rate |
Detection Difficulty |
| iodine |
IP-over-DNS |
NULL, TXT, CNAME, A |
~100 Kbps |
Medium |
| dnscat2 |
C2 over DNS |
TXT, CNAME, MX |
~10 Kbps |
Medium |
| DNSExfiltrator |
Exfil over DNS |
TXT, A |
~5 Kbps |
Medium-Hard |
| Cobalt Strike DNS |
C2 |
A, TXT |
Variable |
Hard |
| dns2tcp |
TCP-over-DNS |
TXT, KEY |
~50 Kbps |
Medium |
| Heyoka |
DNS exfiltration |
All types |
Variable |
Hard |
Zeek Log Fields for DNS Analysis
| Field |
Description |
Tunnel Relevance |
| query |
Full DNS query name |
Length and entropy analysis |
| qtype_name |
Query record type |
TXT/NULL/CNAME anomalies |
| answers |
Response content |
Response size analysis |
| rcode_name |
Response code |
NXDOMAIN patterns |
| id.orig_h |
Source IP |
Source identification |
| AA |
Authoritative answer |
Non-authoritative responses |
| rejected |
Query rejected |
Filtering effectiveness |
references/workflows.md (verbatim)
Detailed Hunting Workflow - DNS Tunneling with Zeek
Phase 1: Query Length and Volume Analysis
Step 1.1 - Identify Domains with Long Queries
index=zeek sourcetype=bro_dns
| eval query_len=len(query)
| where query_len > 50
| rex field=query "\.(?<basedomain>[^.]+\.[^.]+)$"
| stats count avg(query_len) as avg_len max(query_len) as max_len dc(query) as unique_queries by id.orig_h basedomain
| where count > 50
| sort -avg_len
Step 1.2 - High Volume DNS to Single Domain
index=zeek sourcetype=bro_dns
| rex field=query "\.(?<basedomain>[^.]+\.[^.]+)$"
| bin _time span=1h
| stats count by id.orig_h basedomain _time
| where count > 100
| sort -count
Phase 2: Entropy Analysis
Step 2.1 - Shannon Entropy Calculation
import math
from collections import Counter
def shannon_entropy(text):
if not text:
return 0.0
counts = Counter(text)
length = len(text)
return -sum((c/length) * math.log2(c/length) for c in counts.values())
# Flag subdomains with entropy > 3.5
Step 2.2 - Splunk Entropy Approximation
index=zeek sourcetype=bro_dns
| rex field=query "^(?<subdomain>[^.]+)"
| where len(subdomain) > 20
| eval has_numbers=if(match(subdomain, "[0-9]"), 1, 0)
| eval has_mixed_case=if(match(subdomain, "[A-Z]") AND match(subdomain, "[a-z]"), 1, 0)
| stats count avg(len(subdomain)) as avg_sub_len sum(has_numbers) as numeric_count by id.orig_h basedomain
| eval numeric_ratio=numeric_count/count
| where avg_sub_len > 25 AND numeric_ratio > 0.3
Phase 3: Record Type Analysis
Step 3.1 - Unusual Record Types
index=zeek sourcetype=bro_dns
| where qtype_name IN ("TXT", "NULL", "CNAME", "MX", "KEY", "SRV")
| rex field=query "\.(?<basedomain>[^.]+\.[^.]+)$"
| stats count dc(query) as unique by id.orig_h basedomain qtype_name
| where count > 50
| sort -count
Phase 4: RITA Automated Analysis
# Full Zeek log import and DNS analysis
rita import /opt/zeek/logs/current dns_hunt
rita show-dns-tunneling dns_hunt
rita show-exploded-dns dns_hunt | sort -k2 -n -r | head -20
Phase 5: Correlation and Response
Step 5.1 - Map DNS Source to Endpoint
Correlate dns.log source IPs with DHCP logs or endpoint inventory to identify affected hosts and processes.
Step 5.2 - Response Actions
- DNS sinkhole the identified tunneling domain
- Block at DNS resolver and firewall
- Isolate source endpoint
- Capture memory and disk forensics
- Assess scope of data exfiltration
Back to mukul975/Anthropic-Cybersecurity-Skills (817 security skills) or Agent skills.