Potential DNS Exfiltration via Excessive Chunked Queries

edit
IMPORTANT: This documentation is no longer updated. Refer to Elastic's version policy and the latest documentation.

Potential DNS Exfiltration via Excessive Chunked Queries

edit

Identifies potential DNS exfiltration on Windows hosts by detecting a high volume of DNS queries whose subdomain labels follow a chunked encoding pattern (index-payload.base_domain). Attackers split stolen data across many DNS queries to evade volume-based detection; this rule aggregates queries per process, base domain, and five-minute window and flags sessions with many distinct chunk indices and sufficiently long encoded payloads.

Rule type: esql

Rule indices: None

Severity: medium

Risk score: 47

Runs every: 5m

Searches indices from: now-9m (Date Math format, see also Additional look-back time)

Maximum alerts per execution: 100

References:

Tags:

  • Domain: Endpoint
  • OS: Windows
  • Use Case: Threat Detection
  • Tactic: Exfiltration
  • Resources: Investigation Guide
  • Data Source: Elastic Defend
  • Data Source: Crowdstrike
  • Data Source: Sysmon

Version: 1

Rule authors:

  • Elastic

Rule license: Elastic License v2

Investigation guide

edit

Triage and analysis

Investigating Potential DNS Exfiltration via Excessive Chunked Queries

DNS tunneling and exfiltration often encode data in subdomain labels using a chunk index prefix (for example, 42-<base64-or-hex-payload>.attacker.example). A large number of distinct chunk indices to the same base domain from one process within a short window strongly suggests staged data transfer rather than normal resolution behavior.

Possible investigation steps

  • Review Esql.base_domain, Esql.unique_chunks, and Esql.max_index on the alert to gauge exfil volume and whether chunk indices form a contiguous or near-contiguous sequence.
  • Identify process.name and process.executable (from related network events on the same host) and inspect the process tree for scripting runtimes, LOLBins, or unsigned binaries.
  • Pivot on host.id for other DNS, network, or exfiltration alerts in the past 48 hours.
  • Inspect sample dns.question.name values for the session to confirm encoded payload subdomains and estimate data volume (Esql.avg_payload_len × Esql.unique_chunks).
  • Check whether the base domain is newly observed, lacks business justification, or resolves to infrastructure outside approved DNS allowlists.

False positive analysis

  • Legitimate software that encodes telemetry or session tokens in DNS labels is rare; validate against known vendor behavior before closing.
  • Security scanners or research tools that generate synthetic chunked DNS labels may match; confirm process identity and organizational ownership.

Response and remediation

  • If confirmed malicious: isolate the host, block the Esql.base_domain at DNS and egress controls, and preserve DNS/network logs for scoping.
  • Hunt for the same Esql.base_domain and process hash across other hosts and users.
  • Reset credentials and review data accessible to the involved user or process if exfiltration is confirmed.

Setup

edit

Setup

This rule is designed for data generated by Elastic Defend, which provides native endpoint detection and response, along with event enrichments designed to work with our detection rules.

Setup instructions: https://ela.st/install-elastic-defend

Additional data sources

This rule also supports the following third-party data sources. For setup instructions, refer to the links below:

Rule query

edit
FROM logs-crowdstrike.fdr*, logs-endpoint.events.network-*, logs-windows.sysmon_operational-*
| WHERE host.os.type == "windows"
    AND event.category == "network"
    AND event.action IN ("lookup_requested", "DNSEvent (DNS query)", "DnsRequest")
    AND process.name != "svchost.exe"
    AND dns.question.name RLIKE """[0-9]{1,5}-[A-Za-z0-9+/=]{15,63}\..+"""
| GROK dns.question.name "%{INT:chunk_index}-%{DATA:chunk_payload}\\.%{GREEDYDATA:Esql.base_domain}"
| WHERE chunk_index IS NOT NULL
| EVAL payload_len = LENGTH(chunk_payload)
| STATS
    Esql.occurrences = COUNT(*),
    Esql.unique_chunks = COUNT_DISTINCT(chunk_index),
    Esql.max_index = MAX(TO_INTEGER(chunk_index)),
    Esql.avg_payload_len = AVG(payload_len)
  BY process.name, Esql.base_domain, user.id, user.name, host.id, host.name, data_stream.namespace, DATE_TRUNC(5 minutes, @timestamp)
| WHERE Esql.occurrences >= 30
    AND Esql.unique_chunks >= 30
    AND Esql.avg_payload_len >= 20
| SORT Esql.unique_chunks DESC
| LIMIT 20
| KEEP host.id, host.name, process.name, user.id, user.name, data_stream.namespace, Esql.*

Framework: MITRE ATT&CKTM