Potential DNS Tunneling via Long and Unique Subdomains

Identifies a client generating many unique, unusually long DNS query names to the same registered domain within a five-minute window. Malware DNS tunnels and DNS command-and-control commonly encode data in lengthy subdomain portions under one apex domain.

Elastic rule (View on GitHub)

  1[metadata]
  2creation_date = "2026/08/20"
  3integration = ["network_traffic", "fortinet_fortigate", "zeek"]
  4maturity = "production"
  5updated_date = "2026/08/26"
  6
  7[rule]
  8author = ["Elastic"]
  9description = """
 10Identifies a client generating many unique, unusually long DNS query names to the same registered domain within a
 11five-minute window. Malware DNS tunnels and DNS command-and-control commonly encode data in lengthy subdomain portions
 12under one apex domain.
 13"""
 14false_positives = [
 15    """
 16    CDN, cloud load-balancer, software-update, and telemetry hostnames can be long and change often. Recursive
 17    resolvers, forwarders, NAT gateways, and localhost DNS listeners can also combine queries from many endpoints under
 18    one client address. Validate the apex domain and whether the source is an endpoint before treating the activity as
 19    tunneling.
 20    """,
 21]
 22from = "now-9m"
 23language = "esql"
 24license = "Elastic License v2"
 25name = "Potential DNS Tunneling via Long and Unique Subdomains"
 26note = """## Triage and analysis
 27
 28### Investigating Potential DNS Tunneling via Long and Unique Subdomains
 29
 30DNS tunneling encodes data in query labels and often produces many unique, unusually long subdomains under a single apex
 31domain. This rule aggregates network DNS telemetry for that behavioral pattern without relying on threat intelligence
 32feeds or machine learning jobs.
 33
 34Compare overlapping apex domains against the machine learning **DNS Tunneling** rule when that job is enabled.
 35
 36### Possible investigation steps
 37
 38- Review `Esql.dns_registered_domain`, `Esql.count_distinct_names`, `Esql.unique_name_ratio`,
 39  `Esql.max_subdomain_length`, and sample values in `Esql.sample_names`.
 40- Inspect `Esql.dns_question_type_values`. TXT, NULL, CNAME, or MX bursts increase confidence; A/AAAA-only activity can
 41  still be tunneling and should not be dismissed on type alone.
 42- Use `Esql.first_seen`, `Esql.last_seen`, `Esql.dataset_values`, and `Esql.observer_name_values` to establish the event
 43  span and identify the integrations and sensors that contributed to the alert.
 44- Confirm whether `Esql.client_ip` is a workstation, server, recursive resolver, forwarder, NAT address, or localhost
 45  DNS service. Resolver and localhost sources merge many clients and are a common false-positive pattern.
 46- Review `Esql.destination_ip_values` to identify the resolver or authoritative destination observed by the sensor.
 47- Pivot on the same client and apex domain in raw DNS events and look for follow-on process, file, or additional C2
 48  activity.
 49
 50### False positive analysis
 51
 52- CDN, cloud load-balancer, certificate, and software-update services often create long hostnames. Confirm the apex
 53  domain reputation and whether the requesting host role normally uses that provider.
 54- Security or network appliances performing DNS-based reachability or reputation checks can resemble tunneling. Exclude
 55  confirmed appliance addresses after validation.
 56- Do not create a global resolver exception until the originating endpoint is known; a shared `Esql.client_ip` can hide
 57  a single infected host behind legitimate bulk lookups.
 58
 59### Response and remediation
 60
 61- Block the apex domain or forwarding from the affected host at recursive resolvers if malicious activity is confirmed.
 62- Isolate the source host and inspect for tunneling tools or malware initiating the queries.
 63- Add temporary blocks for the apex domain while scoping additional hosts querying the same name.
 64"""
 65references = [
 66    "https://unit42.paloaltonetworks.com/dns-tunneling-how-dns-can-be-abused-by-malicious-actors/",
 67]
 68risk_score = 47
 69rule_id = "89ed957d-609b-4b00-b8c6-a5cbd187632c"
 70setup = """## Setup
 71
 72This rule requires DNS transaction events from one of the following sources:
 73
 74- Elastic Network Packet Capture (`network_traffic.dns`) in `logs-network_traffic.dns-*`
 75- Fortinet FortiGate DNS logs (`fortinet_fortigate.log`) in `logs-fortinet_fortigate.log-*`
 76- Elastic Zeek (`zeek.dns`) in `logs-zeek.dns-*`
 77- Legacy Packetbeat DNS events in `packetbeat-*` with `event.dataset` set to `dns`
 78
 79Place the sensor where it observes endpoint-to-resolver DNS traffic. If the sensor is upstream of a recursive resolver,
 80or if the captured client is a localhost listener such as `127.0.0.1`, `Esql.client_ip` may identify shared DNS
 81infrastructure instead of the originating endpoint.
 82
 83DNS-over-HTTPS (DoH), DNS-over-TLS (DoT), and other encrypted DNS traffic are not visible to packet capture unless the
 84sensor receives decrypted DNS telemetry or equivalent resolver logs mapped to ECS.
 85"""
 86severity = "medium"
 87tags = [
 88    "Domain: Network",
 89    "Use Case: Threat Detection",
 90    "Use Case: Network Security Monitoring",
 91    "Rule Type: ESQL",
 92    "Tactic: Command and Control",
 93    "Tactic: Exfiltration",
 94    "Data Source: Network Packet Capture",
 95    "Data Source: Fortinet",
 96    "Data Source: Network Traffic",
 97    "Data Source: Zeek",
 98    "Resources: Investigation Guide",
 99]
100timestamp_override = "event.ingested"
101type = "esql"
102
103query = '''
104from logs-network_traffic.dns-*, logs-fortinet_fortigate.log-*, logs-zeek.dns-*, packetbeat-*
105| where
106    (
107      data_stream.dataset in ("network_traffic.dns", "fortinet_fortigate.log", "zeek.dns")
108      or event.dataset == "dns"
109    )
110    and dns.question.name is not null
111    and dns.question.registered_domain is not null
112| eval
113    Esql.client_ip = COALESCE(client.ip, source.ip),
114    Esql.dataset = COALESCE(data_stream.dataset, event.dataset),
115    Esql.dns_question_name = TO_LOWER(dns.question.name),
116    Esql.dns_registered_domain = TO_LOWER(dns.question.registered_domain),
117    Esql.dns_question_type = TO_LOWER(dns.question.type),
118    Esql.subdomain_length = LENGTH(Esql.dns_question_name) - LENGTH(Esql.dns_registered_domain) - 1
119| where
120    Esql.client_ip is not null
121    and Esql.subdomain_length >= 50
122    and (Esql.dns_question_type is null or Esql.dns_question_type != "ptr")
123    and not ENDS_WITH(Esql.dns_question_name, ".arpa")
124| eval Esql.time_window = DATE_TRUNC(5 minutes, @timestamp)
125| stats
126    Esql.count_queries = COUNT(*),
127    Esql.count_distinct_names = COUNT_DISTINCT(Esql.dns_question_name),
128    Esql.max_subdomain_length = MAX(Esql.subdomain_length),
129    Esql.avg_subdomain_length = AVG(Esql.subdomain_length),
130    Esql.dns_question_type_values = MV_SLICE(VALUES(Esql.dns_question_type), 0, 9),
131    Esql.destination_ip_values = MV_SLICE(VALUES(destination.ip), 0, 4),
132    Esql.sample_names = MV_SLICE(VALUES(Esql.dns_question_name), 0, 4),
133    Esql.dataset_values = MV_SLICE(VALUES(Esql.dataset), 0, 9),
134    Esql.observer_name_values = MV_SLICE(VALUES(observer.name), 0, 19),
135    Esql.first_seen = MIN(@timestamp),
136    Esql.last_seen = MAX(@timestamp)
137  by Esql.time_window, Esql.client_ip, Esql.dns_registered_domain
138| where Esql.count_queries >= 25 and Esql.count_distinct_names >= 15
139| eval Esql.unique_name_ratio = TO_DOUBLE(Esql.count_distinct_names) / Esql.count_queries
140| keep Esql.*
141'''
142
143
144[[rule.threat]]
145framework = "MITRE ATT&CK"
146[[rule.threat.technique]]
147id = "T1071"
148name = "Application Layer Protocol"
149reference = "https://attack.mitre.org/techniques/T1071/"
150[[rule.threat.technique.subtechnique]]
151id = "T1071.004"
152name = "DNS"
153reference = "https://attack.mitre.org/techniques/T1071/004/"
154
155
156[[rule.threat.technique]]
157id = "T1572"
158name = "Protocol Tunneling"
159reference = "https://attack.mitre.org/techniques/T1572/"
160
161
162[rule.threat.tactic]
163id = "TA0011"
164name = "Command and Control"
165reference = "https://attack.mitre.org/tactics/TA0011/"
166[[rule.threat]]
167framework = "MITRE ATT&CK"
168[[rule.threat.technique]]
169id = "T1048"
170name = "Exfiltration Over Alternative Protocol"
171reference = "https://attack.mitre.org/techniques/T1048/"
172[[rule.threat.technique.subtechnique]]
173id = "T1048.003"
174name = "Exfiltration Over Unencrypted Non-C2 Protocol"
175reference = "https://attack.mitre.org/techniques/T1048/003/"
176
177
178
179[rule.threat.tactic]
180id = "TA0010"
181name = "Exfiltration"
182reference = "https://attack.mitre.org/tactics/TA0010/"
183
184[rule.investigation_fields]
185field_names = [
186    "Esql.client_ip",
187    "Esql.dns_registered_domain",
188    "Esql.time_window",
189    "Esql.count_queries",
190    "Esql.count_distinct_names",
191    "Esql.unique_name_ratio",
192    "Esql.max_subdomain_length",
193    "Esql.avg_subdomain_length",
194    "Esql.dns_question_type_values",
195    "Esql.destination_ip_values",
196    "Esql.sample_names",
197    "Esql.dataset_values",
198    "Esql.observer_name_values",
199    "Esql.first_seen",
200    "Esql.last_seen",
201]

Triage and analysis

Investigating Potential DNS Tunneling via Long and Unique Subdomains

DNS tunneling encodes data in query labels and often produces many unique, unusually long subdomains under a single apex domain. This rule aggregates network DNS telemetry for that behavioral pattern without relying on threat intelligence feeds or machine learning jobs.

Compare overlapping apex domains against the machine learning DNS Tunneling rule when that job is enabled.

Possible investigation steps

  • Review Esql.dns_registered_domain, Esql.count_distinct_names, Esql.unique_name_ratio, Esql.max_subdomain_length, and sample values in Esql.sample_names.
  • Inspect Esql.dns_question_type_values. TXT, NULL, CNAME, or MX bursts increase confidence; A/AAAA-only activity can still be tunneling and should not be dismissed on type alone.
  • Use Esql.first_seen, Esql.last_seen, Esql.dataset_values, and Esql.observer_name_values to establish the event span and identify the integrations and sensors that contributed to the alert.
  • Confirm whether Esql.client_ip is a workstation, server, recursive resolver, forwarder, NAT address, or localhost DNS service. Resolver and localhost sources merge many clients and are a common false-positive pattern.
  • Review Esql.destination_ip_values to identify the resolver or authoritative destination observed by the sensor.
  • Pivot on the same client and apex domain in raw DNS events and look for follow-on process, file, or additional C2 activity.

False positive analysis

  • CDN, cloud load-balancer, certificate, and software-update services often create long hostnames. Confirm the apex domain reputation and whether the requesting host role normally uses that provider.
  • Security or network appliances performing DNS-based reachability or reputation checks can resemble tunneling. Exclude confirmed appliance addresses after validation.
  • Do not create a global resolver exception until the originating endpoint is known; a shared Esql.client_ip can hide a single infected host behind legitimate bulk lookups.

Response and remediation

  • Block the apex domain or forwarding from the affected host at recursive resolvers if malicious activity is confirmed.
  • Isolate the source host and inspect for tunneling tools or malware initiating the queries.
  • Add temporary blocks for the apex domain while scoping additional hosts querying the same name.

References

Related rules

to-top