Analyzes DNS query logs to detect data exfiltration via DNS tunneling, DGA domain communication, and covert C2 channels using entropy analysis, query volume anomalies, and subdomain length detection in SIEM platforms. Use when SOC teams need to identify DNS-based threats that bypass traditional network security controls.
Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Analyzes DNS query logs to detect data exfiltration via DNS tunneling, DGA domain communication, and covert C2 channels using entropy analysis, query volume anomalies, and subdomain length detection in SIEM platforms. Use when SOC teams need to identify DNS-based threats that bypass traditional network security controls.
SOC teams suspect data exfiltration through DNS tunneling to bypass firewall/proxy controls
Threat intelligence indicates adversaries using DNS-based C2 channels (e.g., Cobalt Strike DNS beacon)
UEBA detects anomalous DNS query volumes from specific hosts
Malware analysis reveals DNS-over-HTTPS (DoH) or DNS tunneling capabilities
Do not use for standard DNS troubleshooting or availability monitoring — this skill focuses on security-relevant DNS abuse detection.
Detection Gaps & Validation
DoH/DoT blind spot: if the endpoint resolves over DNS-over-HTTPS (1.1.1.1, 8.8.8.8:443) or DNS-over-TLS (port 853), the stream:dns/Zeek tap never sees the query and every entropy/length search above returns nothing. Confirm coverage by joining index=proxy OR index=firewall dest_port IN (443,853) against the known public-resolver IP list before declaring a host clean.
Index/sourcetype mismatch:index=dns sourcetype="stream:dns" silently returns zero results if logs land under sourcetype=bro:dns:json, XmlWinEventLog:Microsoft-Windows-DNS-Server/Analytical, or a different index. Run | tstats count where index=* by sourcetype and verify query, query_type, src_ip are actually populated, not null.
CIM normalization gap: if you pivot via the Network_Resolution data model, an unaccelerated or unmapped sourcetype makes | tstats ... from datamodel=Network_Resolution undercount — check Network_Resolution.query is mapped and acceleration is current.
Validate the search fires: replay a known tunnel (iodine, dnscat2) or dig a 60+ char base32 subdomain in a lab, confirm the long-subdomain and entropy searches alert, then time-box a 7-day backtest.
FP tuning: allowlist legitimate long-label/high-entropy domains — CDN/cloud hostnames (*.cloudfront.net, *.akamaiedge.net), AV/telemetry (*.mcafee.com, *.windows.com), and DKIM/SPF TXT lookups — via a lookup rather than raising the entropy threshold, which blinds you to slow-drip exfil.
Prerequisites
DNS query logging enabled (Windows DNS Server, Bind, Infoblox, or Cisco Umbrella)
DNS logs ingested into SIEM (Splunk with Stream:DNS, dns sourcetype, or Zeek DNS logs)
Passive DNS data for historical domain resolution analysis
Baseline of normal DNS behavior (query volume, domain distribution, TXT record frequency)
Python with math and collections libraries for entropy calculation
Workflow
Step 1: Detect DNS Tunneling via Subdomain Length Analysis
DNS tunneling encodes data in subdomain labels, creating unusually long queries:
index=dns sourcetype="stream:dns" query_type IN ("A", "AAAA", "TXT", "CNAME", "MX")
| eval domain_parts = split(query, ".")
| eval subdomain = mvindex(domain_parts, 0, mvcount(domain_parts)-3)
| eval subdomain_str = mvjoin(subdomain, ".")
| eval subdomain_len = len(subdomain_str)
| eval tld = mvindex(domain_parts, -1)
| eval registered_domain = mvindex(domain_parts, -2).".".tld
| where subdomain_len > 50
| stats count AS queries, dc(query) AS unique_queries,
avg(subdomain_len) AS avg_subdomain_len,
max(subdomain_len) AS max_subdomain_len,
values(src_ip) AS sources
by registered_domain
| where queries > 20
| sort - avg_subdomain_len
| table registered_domain, queries, unique_queries, avg_subdomain_len, max_subdomain_len, sources
Python-based Shannon Entropy Calculation for DNS queries:
import math
from collections import Counter
defshannon_entropy(text):
"""Calculate Shannon entropy of a string"""ifnot text:
return0
counter = Counter(text.lower())
length = len(text)
entropy = -sum(
(count / length) * math.log2(count / length)
for count in counter.values()
)
returnround(entropy, 4)
# Test with examples
normal_domain = "google"# Low entropy
dga_domain = "x8kj2m9p4qw7n"# High entropy
tunnel_subdomain = "aGVsbG8gd29ybGQ.evil.com"# Base64 encoded dataprint(f"Normal: {shannon_entropy(normal_domain)}") # ~2.25print(f"DGA: {shannon_entropy(dga_domain)}") # ~3.70print(f"Tunnel: {shannon_entropy(tunnel_subdomain)}") # ~3.50# Threshold: entropy > 3.5 for subdomain = likely tunneling/DGA
Splunk implementation of entropy scoring:
index=dns sourcetype="stream:dns"
| eval domain_parts = split(query, ".")
| eval check_string = mvindex(domain_parts, 0)
| eval check_len = len(check_string)
| where check_len > 8
| eval chars = split(check_string, "")
| stats count AS total_chars, dc(chars) AS unique_chars by query, src_ip, check_string, check_len
| eval entropy_estimate = log(unique_chars, 2) * (unique_chars / check_len)
| where entropy_estimate > 3.5
| stats count AS high_entropy_queries, dc(query) AS unique_queries by src_ip
| where high_entropy_queries > 50
| sort - high_entropy_queries
Step 3: Detect Anomalous DNS Query Volume
Identify hosts generating abnormal DNS traffic:
index=dns sourcetype="stream:dns" earliest=-24h
| bin _time span=1h
| stats count AS queries, dc(query) AS unique_domains by src_ip, _time
| eventstats avg(queries) AS avg_queries, stdev(queries) AS stdev_queries by src_ip
| eval z_score = (queries - avg_queries) / stdev_queries
| where z_score > 3 OR queries > 5000
| sort - z_score
| table _time, src_ip, queries, unique_domains, avg_queries, z_score
Detect TXT record abuse (common tunneling method):
index=dns sourcetype="stream:dns" query_type="TXT"
| stats count AS txt_queries, dc(query) AS unique_txt_domains,
values(query) AS domains by src_ip
| where txt_queries > 100
| eval suspicion = case(
txt_queries > 1000, "CRITICAL — Likely DNS tunneling",
txt_queries > 500, "HIGH — Possible DNS tunneling",
txt_queries > 100, "MEDIUM — Unusual TXT volume"
)
| sort - txt_queries
| table src_ip, txt_queries, unique_txt_domains, suspicion
Step 4: Detect Known DNS Tunneling Tools
Search for signatures of common DNS tunneling tools:
index=dns sourcetype="stream:dns"
| eval query_lower = lower(query)
| where (
match(query_lower, "\.dnscat\.") OR
match(query_lower, "\.dns2tcp\.") OR
match(query_lower, "\.iodine\.") OR
match(query_lower, "\.dnscapy\.") OR
match(query_lower, "\.cobalt.*\.beacon") OR
query_type="NULL" OR
(query_type="TXT" AND len(query) > 100)
)
| stats count by src_ip, query, query_type
| sort - count
Detect DNS over HTTPS (DoH) bypassing local DNS:
index=proxy OR index=firewall
dest IN ("1.1.1.1", "1.0.0.1", "8.8.8.8", "8.8.4.4",
"9.9.9.9", "149.112.112.112", "208.67.222.222")
dest_port=443
| stats sum(bytes_out) AS total_bytes, count AS connections by src_ip, dest
| where connections > 100 OR total_bytes > 10485760
| eval alert = "Possible DoH bypass — DNS queries sent over HTTPS to public resolver"
| sort - total_bytes
Step 5: Correlate DNS Findings with Endpoint Data
Cross-reference suspicious DNS with process data:
index=dns src_ip="192.168.1.105" query="*.evil-tunnel.com" earliest=-24h
| stats count AS dns_queries, earliest(_time) AS first_query, latest(_time) AS last_query
by src_ip, query
| join src_ip [
search index=sysmon EventCode=3 DestinationPort=53 Computer="WORKSTATION-042"
| stats count AS connections, values(Image) AS processes by SourceIp
| rename SourceIp AS src_ip
]
| table src_ip, query, dns_queries, first_query, last_query, processes
Step 6: Calculate Data Exfiltration Volume Estimate