THREAT-DNSTunneling-Exfiltration Elastic Security · Elastic

Detect Data Exfiltration via DNS Tunneling Tools in Elastic Security

Adversaries who cannot or will not exfiltrate over HTTP/S turn to DNS tunneling — encoding stolen data into the subdomain labels of outbound DNS queries and relying on an adversary-controlled authoritative nameserver to reassemble it. Because DNS is almost never blocked at the perimeter and is rarely inspected payload-by-payload, tools such as iodine, dnscat2, dns2tcp, DNSExfiltrator, and Cobalt Strike's DNS beacon can move stolen files, credential dumps, and keystrokes out of even tightly firewalled networks a few dozen bytes at a time. OilRig/APT34 has repeatedly used custom DNS tunneling malware (Helminth, DNSpionage) against Middle Eastern targets, and DNS-based exfiltration is a standard feature of numerous commodity C2 frameworks. The technique is distinct from generic 'Exfiltration Over C2 Channel' (T1041) because the DNS tunnel is frequently a dedicated side-channel independent of the primary C2 protocol, and from the base T1048.003 record because tunneling tools have identifiable protocol fingerprints (query volume, label entropy, label-length skew) beyond simple unencrypted-protocol abuse.

MITRE ATT&CK

Tactic
Exfiltration

Elastic Detection Query

Elastic Security (Elastic)
eql
dns where event.type == "start" and length(dns.question.subdomain) >= 20
high severity medium confidence

Elastic EQL filter for candidate DNS tunneling queries using ECS dns.question.subdomain (the portion of the query name below the registered/apex domain). EQL has no aggregation functions, so this rule identifies individual long-label candidate events; pair it with a companion Kibana Threshold rule bucketing on dns.question.registered_domain with a count aggregation (>=50) and a cardinality aggregation on dns.question.name (>=40) to reproduce the full volumetric scoring model used in the kql/spl versions of this detection.

Data Sources

Elastic Defend (DNS network events)Packetbeat DNS moduleElastic Security Threshold rule (companion aggregation, required for full parity)

Required Tables

logs-endpoint.events.network-*packetbeat-*

False Positives & Tuning

  • CDN/cloud edge hostnames with legitimately long, high-entropy subdomain labels
  • DNS-based service discovery or load-balancing systems issuing high query volumes
  • Security telemetry or threat-intel feeds that piggyback data over DNS queries

Other platforms for THREAT-DNSTunneling-Exfiltration


Testing Methodology

Validate this detection against 2 adversary techniques from Atomic Red Team. Each test below lists the behaviour to exercise and the telemetry you should expect to see. Executable commands and cleanup steps are available with Pro.

  1. Test 1Simulated DNS Tunneling via Base32-Encoded Subdomain Query Burst

    Expected signal: Sysmon Event ID 22 (DNS Query): 40 events with QueryName first-labels of 25-30 characters (Base64-derived), resolving against the internal test apex domain, initiated by powershell.exe. The queries will fail to resolve (no authoritative server configured) but the query attempt itself generates the required telemetry.

  2. Test 2High-Frequency Long-Label DNS Query Burst via nslookup

    Expected signal: Sysmon Event ID 22: 25 DNS query events for nslookup.exe with 28-character random alphanumeric first labels at ~2-second intervals against the same test apex domain. Windows DNS Client operational log may also capture the resolution attempts.


Response Playbook

Triage

  1. Identify the apex domain receiving the tunneled queries and check its registration date, registrar, and WHOIS privacy status — DNS tunneling C2 domains are frequently registered within the past 30-90 days.
  2. Pull the full set of first-labels observed for the apex domain and attempt Base32/Base64 decoding — successful decoding into readable text or binary headers (e.g. ZIP magic bytes 'PK') confirms active data exfiltration rather than a false positive.
  3. Identify the querying process — dnscat2, iodine, and dns2tcp typically run as unsigned or renamed binaries; Cobalt Strike DNS beacons run inside an injected or masqueraded process. Check process hash and signature.
  4. Estimate data volume: rough capacity is ~30-60 bytes of payload per query for a maximally-packed label. Multiply QueryCount by this estimate to gauge how much data may already have left the network.
  5. If a DNS server query log or packet capture is available, check the actual record type requested (TXT/NULL requests carry larger response payloads and are a stronger tunneling indicator than this endpoint telemetry alone can provide).
  6. Correlate with DeviceFileEvents/Sysmon Event ID 11 in the preceding hour for archive creation or bulk file reads, indicating a collect-then-tunnel pattern.

Containment

  1. Sinkhole or block the offending apex domain at the internal DNS resolver and firewall — this is the single most effective containment step since the tunnel cannot function without resolution to the adversary's authoritative server.
  2. Isolate the endpoint via EDR network isolation; DNS tunneling clients often persist and will resume once network access is restored, so isolation should precede remediation.
  3. If the tunneling client uses a fixed authoritative nameserver IP (rather than the victim's configured resolver), consider blocking direct UDP/TCP 53 to non-authorized DNS servers organization-wide as an emergency control.
  4. Rotate credentials for any account active on the host during the tunneling window, since DNS exfiltration is frequently used to move credential dumps.
  5. Preserve a packet capture of the DNS traffic before blocking — decoded tunnel content is often the best evidence of what was actually stolen.

Evidence Collection

  1. Full DNS query log (Sysmon Event ID 22 or DNS server query log) for the apex domain across the full activity window, not just the alerting window — tunnels often run for days before crossing volume thresholds.
  2. Process memory dump of the tunneling client if still running — iodine/dnscat2 keep the reconstructed data buffer in memory prior to writing to disk.
  3. Any locally staged archive or output file the tunneling tool assembled data from or into (check %TEMP%, /tmp, and the tool's working directory).
  4. Command-line arguments and any configuration file for the tunneling binary — most tools take the C2 domain and tunnel password as CLI flags or a config file.
  5. Network flow records or a firewall/DNS-server log capturing the actual record type (TXT/NULL) requested, if available, since endpoint telemetry alone cannot confirm this.

Escalation Criteria

  • !Decoded label content confirms exfiltration of credentials, source code, or regulated data (PII, PHI, PCI) — escalate immediately to incident response and legal/privacy.
  • !The apex domain or tunneling toolset matches known APT infrastructure (e.g. OilRig/APT34 DNSpionage/Helminth patterns) in threat intelligence.
  • !Tunneling activity is observed from a server, domain controller, or privileged workstation rather than a standard user endpoint.
  • !Tunneling has been active for more than 24 hours before detection, indicating a substantial volume of data may already be off-network.
  • !The same apex domain or tunneling signature appears across multiple hosts, indicating lateral spread or a shared C2 infrastructure compromise.

Investigation Guide

Related Techniques

Forensic Artifacts

  • >Sysmon Event ID 22 (DNS Query) — full query history including QueryName and initiating process; note it does not capture DNS record type, so it cannot by itself confirm TXT/NULL usage.
  • >Windows DNS Client cache — ipconfig /displaydns captures recently resolved names before they expire from cache.
  • >Local DNS resolver / firewall query logs — often retain longer history than endpoint logs, can reveal the full tunnel lifetime, and are typically the only source that records the actual query type requested.
  • >Prefetch and process execution artifacts for the tunneling client binary (iodine, dnscat2 Ruby/PowerShell client, dns2tcp).
  • >Any dropped configuration file specifying the tunnel password, apex domain, and encoding scheme used by the client.

Tuning Guidance

Baseline your environment's normal long-subdomain-label traffic before enabling this in blocking mode — CDNs (Akamai, CloudFront, Fastly), collaboration platforms (Teams, Zoom media relays), and some enterprise VPN/SASE providers use legitimately long, high-entropy labels. Build an allowlist of apex domains for these known-good high-entropy label sources rather than excluding by label length alone. Start MinQueriesPerApex at 50 and raise it in environments with chatty DNS-based service discovery (Consul, some Kubernetes CNI DNS). Neither DeviceDnsEvents nor Sysmon Event ID 22 exposes DNS record type, so this model cannot key on TXT/NULL skew the way a resolver-side log could; if your DNS resolver or firewall logs expose query type, layer a supplemental rule on that source for higher fidelity. If your DNS resolver logs are available, correlating resolver-side query volume against the same apex domain across your whole fleet (not just per-host) can reveal low-and-slow tunnels that stay under per-host thresholds.


Hunting Queries

Hunts for automated, sub-5-second-interval DNS query bursts with long first labels — the timing signature of a scripted tunneling client rather than human or application-driven DNS traffic, which is bursty but irregular.

Hunting — KQL
kql
// Hunt for DNS query bursts with regular inter-query timing consistent with automated tunneling
DeviceDnsEvents
| where Timestamp > ago(7d)
| extend Labels = split(QueryName, ".")
| extend FirstLabel = tostring(Labels[0])
| where strlen(FirstLabel) >= 20
| sort by DeviceName, InitiatingProcessFileName, Timestamp asc
| serialize
| extend PrevTimestamp = prev(Timestamp), PrevDevice = prev(DeviceName), PrevProcess = prev(InitiatingProcessFileName)
| where DeviceName == PrevDevice and InitiatingProcessFileName == PrevProcess
| extend GapSeconds = datetime_diff('second', Timestamp, PrevTimestamp)
| where GapSeconds between (0 .. 5) // sub-5-second automated pacing
| summarize BurstQueries = count(), AvgGapSeconds = avg(GapSeconds) by DeviceName, InitiatingProcessFileName, bin(Timestamp, 1h)
| where BurstQueries > 30
| sort by BurstQueries desc
Hunting — SPL
spl
index=wineventlog sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=22
| eval FirstLabel=mvindex(split(QueryName, "."), 0)
| where len(FirstLabel) >= 20
| sort 0 host, Image, _time
| streamstats current=f last(_time) as PrevTime by host, Image
| eval GapSeconds=_time-PrevTime
| where GapSeconds>=0 AND GapSeconds<=5
| bucket _time span=1h
| stats count as BurstQueries, avg(GapSeconds) as AvgGapSeconds by host, Image, _time
| where BurstQueries>30
| sort - BurstQueries

Hunts for a single process resolving an abnormally high number of distinct apex domains in a short window, a pattern seen in DGA-backed or multi-domain DNS tunneling infrastructure used to evade single-domain blocklisting.

Hunting — KQL
kql
// Hunt for a single process resolving an unusually large number of distinct apex domains via DNS in a short window (domain-rotation tunneling C2)
DeviceDnsEvents
| where Timestamp > ago(24h)
| extend Labels = split(QueryName, ".")
| extend ApexDomain = strcat(tostring(Labels[array_length(Labels) - 2]), ".", tostring(Labels[array_length(Labels) - 1]))
| summarize UniqueApexDomains = dcount(ApexDomain), TotalQueries = count() by DeviceName, InitiatingProcessFileName, bin(Timestamp, 1h)
| where UniqueApexDomains > 15 and TotalQueries > 50
| sort by UniqueApexDomains desc
Hunting — SPL
spl
index=wineventlog sourcetype="XmlWinEventLog:Microsoft-Windows-Sysmon/Operational" EventCode=22
| eval Labels=split(QueryName, ".")
| eval LabelCount=mvcount(Labels)
| eval ApexDomain=mvindex(Labels, LabelCount-2) . "." . mvindex(Labels, LabelCount-1)
| bucket _time span=1h
| stats dc(ApexDomain) as UniqueApexDomains, count as TotalQueries by host, Image, _time
| where UniqueApexDomains>15 AND TotalQueries>50
| sort - UniqueApexDomains

Atomic Red Team Tests

Test 1 Simulated DNS Tunneling via Base32-Encoded Subdomain Query Burst
windows

Simulates the query pattern of a DNS tunneling client (iodine/dnscat2-style) by Base32-encoding a chunk of synthetic sensitive data into DNS subdomain labels and issuing a rapid burst of queries to a single test apex domain. Replicates the high-volume, high-label-cardinality, packed-label-length signature the detection targets, without requiring installation of an actual tunneling tool.

Command

powershell
$Data = [System.Text.Encoding]::UTF8.GetBytes("synthetic-sensitive-export-chunk-$(Get-Random)")
for ($i = 0; $i -lt 40; $i++) {
  $Chunk = [System.Text.Encoding]::UTF8.GetBytes("chunk${i}-$(Get-Random -Maximum 99999)")
  $Label = [Convert]::ToBase64String($Chunk).TrimEnd('=').Replace('+','a').Replace('/','b').Substring(0, [Math]::Min(30, [Convert]::ToBase64String($Chunk).Length))
  Resolve-DnsName -Name "$Label.df00tech-dnstunnel-test.internal" -Type A -ErrorAction SilentlyContinue | Out-Null
  Start-Sleep -Milliseconds 150
}
Write-Host 'DNS tunneling simulation complete: 40 encoded-label queries generated'

Expected Telemetry

Sysmon Event ID 22 (DNS Query): 40 events with QueryName first-labels of 25-30 characters (Base64-derived), resolving against the internal test apex domain, initiated by powershell.exe. The queries will fail to resolve (no authoritative server configured) but the query attempt itself generates the required telemetry.

Expected Detection

KQL/SPL DNS tunneling detection: QueryCount=40 exceeds MinQueriesPerApex threshold when lowered for lab testing (or extend the window/threshold for a realistic 50+ query run); UniqueLabels approaches 40 (near 1:1 cardinality); AvgLabelLen is 25-30 characters, at or above the MinAvgLabelLen=28 threshold. TunnelScore should reach 2-3.

Test 2 High-Frequency Long-Label DNS Query Burst via nslookup
windows

Generates a rapid, evenly-paced burst of DNS queries with long, high-entropy first labels using nslookup, simulating the automated timing signature (sub-5-second, regular interval) that the hunting query targets. Representative of dnscat2 and dns2tcp client polling behavior.

Command

powershell
1..25 | ForEach-Object {
  $Label = -join ((48..57) + (97..122) | Get-Random -Count 28 | ForEach-Object {[char]$_})
  nslookup "$Label.df00tech-dnstunnel-test.internal" 2>$null | Out-Null
  Start-Sleep -Seconds 2
}
Write-Host 'nslookup burst complete: 25 queries at 2-second intervals'

Expected Telemetry

Sysmon Event ID 22: 25 DNS query events for nslookup.exe with 28-character random alphanumeric first labels at ~2-second intervals against the same test apex domain. Windows DNS Client operational log may also capture the resolution attempts.

Expected Detection

The automated-pacing hunting query flags this burst — GapSeconds consistently ~2 seconds falls within the sub-5-second detection window, and BurstQueries=25 approaches the >30 threshold (adjust threshold down for lab validation runs).

Related Detections