SOP-32: DNS Exfiltration & Tunnelling Detection

·

DNS exfiltration exploits the fact that DNS traffic is rarely blocked and rarely inspected at depth. Attackers encode stolen data into DNS query subdomains — a 63-character label limit per subdomain and up to 255 characters total per FQDN allows ~200 bytes per query, enough to slowly exfiltrate gigabytes when queries are sent at scale. Tools like dnscat2 create full bidirectional covert channels over DNS, while iodine tunnels IPv4 over DNS TXT records. Detection requires volume analytics, label-length statistics, and entropy scoring, not simple signature matching.

Key data sources: DnsEvents (Azure DNS Analytics / DNS connector), DeviceNetworkEvents (MDE DNS query telemetry), CommonSecurityLog (firewall/proxy DNS logs), ASimDnsActivityLogs (ASIM-normalized DNS schema).

☰ Table of Contents

01 Background 02 Identification 03 Containment 04 Eradication 05 Escalation
🛠

01 — BACKGROUND

TECHNIQUE OVERVIEW
01
Technique Overview — DNS Exfil & Tunnelling
MITRE T1048.003 / T1071.004

DNS tunnelling exploits the universally permitted DNS protocol to create covert data channels that bypass firewalls, DLP, and proxy inspection. dnscat2 is the most common open-source tool — it encodes arbitrary data (shell commands, file bytes, C2 traffic) into DNS query/response pairs using TXT, MX, CNAME, and A record types. iodine specifically tunnels IPv4 packets over DNS TXT records, providing a full IP stack over DNS. Custom exfil scripts encode data as sequential long subdomain labels pointed at attacker-controlled authoritative nameservers, which decode and reassemble the stream.

FieldValue
MITRE TechniquesT1048.003 — Exfiltration Over Alternative Protocol: Unencrypted
T1071.004 — Application Layer Protocol: DNS
Kill-chain PhaseExfiltration / Command & Control
Primary Log SourceDnsEvents, ASimDnsActivityLogs, DeviceNetworkEvents
Secondary Log SourcesCommonSecurityLog (Palo Alto / Cisco DNS syslog), AzureFirewallNetworkRule, DnsInventory
Known Toolsdnscat2, iodine, DNScat, NSDNS, dns2tcp, cobalt-strike DNS C2, custom base64 subdomain scripts
Default SeveritySEV2 on detection; escalates to SEV1 if data volume exceeds 1 MB or confirmed C2 tunnel
Key MetricLabel length >40 chars, query rate >100/min from single host, TXT record ratio >20% of total DNS from host, NXDOMAIN rate >30%

DNS exfil is slow but undetected for months. The average dwell time before DNS tunnel detection is 47 days (SANS 2024). A host querying 500+ unique FQDNs per hour under a single second-level domain is almost certainly tunnelling — normal user endpoints query 5–20 unique domains per hour.

🔍

02 — IDENTIFICATION

2 INDICATORS
▶ Investigation Workflow
I1
High-Volume TXT Queries & Long Subdomain Label Detection
T1048.003
WHY CHECK

DNS TXT records are the preferred exfil record type because they support arbitrary binary data up to 255 bytes per string. dnscat2 defaults to TXT queries. Legitimate applications generate very few TXT queries — SPF/DKIM lookups account for nearly all legitimate TXT traffic and originate from mail servers, not endpoints. An endpoint generating >20 TXT queries per minute to a single domain is a near-certain tunnel indicator. Long base64-encoded subdomain labels (40+ chars) are the signature of encoded data chunks — legitimate hostnames are human-readable and short.

📍 Portal Navigation — Microsoft Sentinel Logs
portal.azure.com› Microsoft Sentinel› Logs› DnsEvents / ASimDnsActivityLogs
  • 1Run the KQL query below. Set time range to Last 1 hour for active investigation; extend to Last 7 days to establish baseline and identify slow-burn exfil.
  • 2For each flagged host, examine the Name (queried FQDN) field. Count the length of the leftmost label (subdomain before the first dot) — labels exceeding 40 characters with base64 character patterns ([A-Za-z0-9+/=]) indicate encoded data.
  • 3Identify the second-level domain (SLD) being queried — e.g. a1b2c3def456.exfiltrate[.]com. Pivot to Threat Intelligence in Sentinel to check if this domain is a known dnscat2/DNS tunnel domain.
  • 4Cross-reference ClientIP in DeviceNetworkEvents to identify the process making DNS requests — nslookup.exe, powershell.exe, or an unusual process name making DNS calls is highly suspicious.
  • 5Check if the authoritative nameserver for the target SLD resolves to a VPS/cloud provider (DigitalOcean, Linode, Vultr, Hetzner AS) — attacker-operated authoritative NS servers are the exfil receiving endpoint.
IndicatorMeaningSeverity
>50 TXT queries/min from single hostActive dnscat2 or iodine tunnel — data being exfiltrated or C2 channel activeHIGH
Subdomain label length >40 charsBase64/hex encoded data chunk — classic DNS exfil encoding patternHIGH
>200 unique subdomains to single SLD/hrSequential chunk transmission — reassembly at attacker nameserver indicates active exfilHIGH
MX/NULL/CNAME query flood to single domaindnscat2 multi-record-type tunnel — tool rotates record types to evade single-type filtersMEDIUM
DNS queries from non-browser/non-OS processMalware or script making direct DNS calls — confirm via DeviceNetworkEvents InitiatingProcessMEDIUM
DETECTS: High-volume TXT record queries and abnormally long subdomain labels from individual endpoints — primary indicators of DNS tunnelling and data exfiltration
KQL — DNS Tunnel Detection via TXT Volume & Label Length
DnsEvents | where TimeGenerated > ago(1h) | where QueryType == "TXT" or strlen(Name) > 100 | extend SubLabel = tostring(split(Name, ".")[0]) | extend LabelLen = strlen(SubLabel) | summarize TotalQueries = count(), TXTQueries = countif(QueryType == "TXT"), LongLabelQueries = countif(LabelLen > 40), UniqueDomains = dcount(Name), SampleLabels = make_set(SubLabel, 5) by ClientIP, bin(TimeGenerated, 1h) | where TXTQueries > 20 or LongLabelQueries > 10 or UniqueDomains > 200 | order by TotalQueries desc
DETECTS: Process-level DNS exfiltration — identifies the specific process making suspicious DNS calls, corroborating tunnel activity seen in DnsEvents
KQL — Process-Level DNS Exfil Correlation (MDE)
DeviceNetworkEvents | where Timestamp > ago(1h) | where Protocol == "Dns" | extend LabelLen = strlen(tostring(split(RemoteUrl, ".")[0])) | where LabelLen > 40 or RemotePort == 53 and InitiatingProcessFileName !in~ ("svchost.exe","dns.exe","chrome.exe","msedge.exe","firefox.exe","outlook.exe","teams.exe") | project Timestamp, DeviceName, InitiatingProcessFileName, InitiatingProcessCommandLine, RemoteUrl, RemoteIP, RemotePort, LabelLen | order by Timestamp desc
SPL — DNS TXT Volume & Long Label Detection
index=dns sourcetype=dns_events | eval label=mvindex(split(query,".") ,0) | eval label_len=len(label) | stats count as total_queries, count(eval(qtype="TXT")) as txt_queries, count(eval(label_len>40)) as long_label_queries, dc(query) as unique_domains by src_ip, span(_time, 1h) | where txt_queries>20 OR long_label_queries>10 OR unique_domains>200 | sort -total_queries

To confirm dnscat2 specifically: look for queries using sequential numeric prefixes on long encoded labels (e.g., 01.a1b2c3..., 02.a1b2c3...) — dnscat2 sequences chunks numerically. Also check if query sizes increase then reset cyclically — this is the session/stream boundary signature.

I2
NXDOMAIN Spike & DNS C2 Beacon Pattern
T1071.004
WHY CHECK

A high NXDOMAIN (Non-Existent Domain) rate from a single host indicates either DNS DGA (Domain Generation Algorithm) malware polling for the active C2 domain, or a misconfigured DNS tunnel where encoded query strings don't resolve. DGA malware generates hundreds of random-looking domain names per hour — only one will be registered by the attacker on any given day. The massive NXDOMAIN ratio (often >95%) is a distinctive DGA fingerprint. Separately, DNS C2 beaconing produces a periodic, low-rate DNS query pattern to a single domain at fixed intervals — the jitter-free regularity distinguishes it from human browsing.

📍 Portal Navigation — Sentinel Logs
portal.azure.com› Microsoft Sentinel› Logs› DnsEvents
  • 1Run the NXDOMAIN ratio query below to identify hosts with >30% NXDOMAIN rate — set time range to Last 4 hours. Normal endpoints have <5% NXDOMAIN rate.
  • 2For hosts with high NXDOMAIN rate, inspect the failing domain names for DGA characteristics: high character entropy, no vowels or consonant clusters, 8–20 character length, random-appearing TLD pattern.
  • 3For C2 beacon detection, run the periodic query analysis — look for hosts making DNS requests to the same domain at suspiciously regular intervals (every 30s, 60s, 300s). Real browsing is irregular; C2 beacons have <5% jitter.
  • 4Pivot identified suspicious domains to Sentinel → Threat Intelligence → TI Workbook and search against TAXII feed indicators. Also submit to VirusTotal / any-run for rapid classification.
IndicatorMeaningSeverity
NXDOMAIN rate >30% from single hostDGA malware polling for active C2 — host is likely compromised and beaconingHIGH
>100 NXDOMAIN/hr to distinct random-looking FQDNsActive DGA generation — malware cycling through domains to find C2HIGH
Periodic DNS query to same domain (fixed interval)DNS C2 beacon — regular heartbeat indicates implant checking for commandsHIGH
Low-TTL (1–5s) responses from unknown domainFast-flux DNS — attacker rotating IPs rapidly to evade blocklistsMEDIUM
DNS queries outside business hours from serverOff-hours C2 activity — malware beaconing when less likely to be noticedMEDIUM
DETECTS: Hosts with abnormally high NXDOMAIN rates — indicates DGA malware scanning for active C2 domain or misconfigured DNS tunnel
KQL — NXDOMAIN Ratio Anomaly Detection
DnsEvents | where TimeGenerated > ago(4h) | summarize TotalQueries = count(), NXDomainCount = countif(ResultCode == 3), UniqueNXDomains = dcountif(Name, ResultCode == 3) by ClientIP, bin(TimeGenerated, 1h) | extend NXDomainRatio = round(todouble(NXDomainCount) / TotalQueries, 2) | where NXDomainRatio > 0.30 and TotalQueries > 50 | project TimeGenerated, ClientIP, TotalQueries, NXDomainCount, UniqueNXDomains, NXDomainRatio | order by NXDomainRatio desc
DETECTS: Periodic DNS beaconing — identifies regular fixed-interval DNS queries to same domain characteristic of C2 implant heartbeat
KQL — DNS C2 Beacon Periodicity Detection
DnsEvents | where TimeGenerated > ago(6h) | where ResultCode == 0 | summarize QueryTimes = make_list(TimeGenerated) by ClientIP, Name | where array_length(QueryTimes) > 5 | extend Intervals = series_fir( series_diff(array_sort_asc(QueryTimes)), dynamic([1]), true, false) | extend IntervalStdDev = stdev(Intervals) | extend IntervalMean = avg(Intervals) | where IntervalMean < 600 and IntervalStdDev < 30 | project ClientIP, Name, QueryCount=array_length(QueryTimes), IntervalMean, IntervalStdDev | order by IntervalStdDev asc
SPL — NXDOMAIN Ratio & Beacon Analysis
index=dns sourcetype=dns_events | stats count as total, count(eval(rcode=3)) as nxdomain by src_ip, span(_time, 1h) | eval nxdomain_ratio=round(nxdomain/total,2) | where nxdomain_ratio>0.30 AND total>50 | sort -nxdomain_ratio
🚫

03 — CONTAINMENT

DECISION + ACTIONS
C1
Containment Verdict & Action Decision
IMMEDIATE
▲ Containment Decision Matrix
CONFIRMEDLong-label TXT queries confirmed + process identified — isolate endpoint via MDE (Assets → Devices → [Device] → Actions → Isolate Device), block the exfil domain at DNS resolver and firewall, and capture PCAP of DNS traffic before isolation.
SUSPECTEDHigh NXDOMAIN ratio or volume anomaly but no process attribution yet — apply DNS sinkhole for flagged domain(s), increase DNS logging verbosity (enable full query logging on corporate resolvers), and continue investigation without isolating endpoint.
FALSE POSITIVELong labels explained by CDN/CNAME chain, NXDOMAIN spike from misconfigured app or stale cached lookup — document in ticket, tune detection threshold, close alert. No containment needed.
  • 1DNS sinkhole: Add the exfil domain(s) to your DNS block list / sinkhole immediately — this cuts the exfil channel without alerting the attacker to the investigation (endpoint stays online but channel dies silently).
  • 2Firewall block: Add the attacker-controlled authoritative NS IP and any resolved IPs to the network firewall deny list on port 53 UDP/TCP. Also block any backup C2 domains identified in the NXDOMAIN analysis.
  • 3PCAP capture: Before full isolation, capture DNS traffic from the affected host for at minimum 10 minutes. This preserves evidence of the tunnel and may reveal the destination IP of the authoritative NS controlling the exfil.
  • 4Endpoint isolation: Only isolate via MDE after PCAP capture. Isolation will cut the DNS channel anyway — PCAP first preserves the live evidence trail.
⚖

04 — ERADICATION

CLEANUP & RECOVERY
E1
Tunnel Removal & Data Loss Assessment
ERADICATE
  • 1Identify and terminate tunnel process: From DeviceNetworkEvents attribution, identify the initiating process. Use MDE Live Response to confirm the process is running and collect the binary before terminating: get <binary_path> → run antimalware-scan.exe.
  • 2Estimate data volume exfiltrated: Sum the total bytes in DNS query names over the tunnel timeframe — each character in a subdomain label is ~1 byte. DNS labels are max 63 bytes; queries max 255 bytes total. Calculate: total_query_chars × 0.75 (base64 decode ratio) = approximate raw bytes exfiltrated. Report this figure to the data owner.
  • 3Identify data type exfiltrated: If you can decode the base64 labels from captured DNS queries, do so to determine the content category (credentials, files, database records). This drives notification obligations.
  • 4Re-image if dnscat2/iodine confirmed: These tools are typically delivered as implants with persistence mechanisms. Full OS re-image is recommended unless forensic memory analysis confirms no persistence (scheduled task, registry run key, service).
  • 5DNS resolver hardening: Enable Response Policy Zones (RPZ) on internal DNS servers to block known tunnel domains. Consider deploying DNS Security Extensions (DNSSEC) validation and Cisco Umbrella / Cloudflare Gateway for outbound DNS filtering.

DNS exfil volume calculation is approximate — the actual bytes exfiltrated may be 2–3× higher if the attacker used compression before base64 encoding. Treat the calculated figure as a minimum for data loss reporting, not a ceiling.

△

05 — ESCALATION

THRESHOLDS & CONTACTS
ES1
Escalation Thresholds & Notification
ESCALATE
ConditionActionSeverity
Confirmed DNS tunnel + process identifiedEscalate to SOC Lead — initiate full IR playbook; notify data owner if PII/PHI scope suspectedSEV2
Estimated exfil >1 MB or PII/PHI confirmedEscalate to CISO + Legal — potential data breach notification obligation (GDPR 72h, NZ Privacy Act)SEV1
DGA malware confirmed (high NXDOMAIN + process)Escalate to Threat Intel team — DGA seed/algorithm analysis for broader campaign scopeSEV2
Multiple hosts tunnelling to same domainEscalate immediately — indicates enterprise-wide compromise; initiate containment across all identified hostsSEV1
DNS exfil from server (not endpoint)Escalate to SOC Lead — server compromise implies higher data sensitivity; may indicate post-exploitation lateral movementSEV1

Always submit identified exfil domains and authoritative NS IPs to your MISP instance and threat intel sharing communities (ISACs). Other organisations may be targeted by the same infrastructure — coordinated takedown requests to the hosting provider are more effective than single-org reports.


CyberHawk Threat Intel

CyberHawk Threat Intel

Built by Rudra Verma | Senior Cyber Security Architect & Researcher

▶ YouTube 🎥 TikTok 𝕏 Twitter / X 📤 Telegram
Web App (Free) All SOPs Courses

"They can't exploit you if you are the Exploit."

Authorized security research & penetration testing only. Unauthorized use is illegal.