Potential DNS Rebinding from Public to Private Address

Last updated 2 days ago on 2026-08-25
Created 7 days ago on 2026-08-20

About

Identifies a client resolving the same public registered domain to both a public IP address and a private, loopback, link-local, unique-local IPv6, or shared address. This includes both address classes being observed at the same timestamp, and a public answer followed within five minutes by a private answer where the minimum TTL across all answer records in the private-answer events is 60 seconds or less. Either pattern is consistent with DNS rebinding that pivots browser or application trust to internal resources.
Tags
Domain: NetworkUse Case: Threat DetectionUse Case: Network Security MonitoringTactic: Initial AccessRule Type: ESQLData Source: Network Packet CaptureData Source: Network TrafficData Source: ZeekLanguage: esql
Severity
medium
Risk Score
47
MITRE ATT&CK™

Initial Access (TA0001)(external, opens in a new tab or window)

False Positive Examples
Split-horizon DNS, VPN transitions, service discovery, failover, hairpin NAT, and dual-stack names that publish a public A record with a unique-local AAAA record can legitimately produce public and private answers for the same name. Recursive resolvers, DNS forwarders, and localhost listeners can also aggregate many endpoints under one client address. Security products may sinkhole suspicious domains to loopback or private addresses with short TTLs. Confirm the domain, resolver placement, and client identity before adding an exception, and scope exceptions by registered domain or client rather than globally.
License
Elastic License v2(external, opens in a new tab or window)

Definition

Integration Pack
Prebuilt Security Detection Rules
Related Integrations

network_traffic(external, opens in a new tab or window)

zeek(external, opens in a new tab or window)

Query
text code block:
from logs-network_traffic.dns-*, logs-zeek.dns-*, packetbeat-* | where ( data_stream.dataset in ("network_traffic.dns", "zeek.dns") or event.dataset == "dns" ) and dns.question.name is not null and dns.question.registered_domain is not null and dns.resolved_ip is not null and TO_UPPER(dns.response_code) == "NOERROR" and TO_UPPER(dns.question.type) in ("A", "AAAA") | eval Esql.client_ip = COALESCE(client.ip, source.ip), Esql.dataset = COALESCE(data_stream.dataset, event.dataset) | where Esql.client_ip is not null | mv_expand dns.resolved_ip | eval Esql.is_private = CIDR_MATCH( dns.resolved_ip, "0.0.0.0/32", "10.0.0.0/8", "100.64.0.0/10", "127.0.0.0/8", "169.254.0.0/16", "172.16.0.0/12", "192.168.0.0/16", "::1/128", "fc00::/7", "fe80::/10" ) | eval Esql.private_time = CASE(Esql.is_private, @timestamp, null), Esql.public_time = CASE(not Esql.is_private, @timestamp, null), Esql.private_event_ttl = CASE(Esql.is_private, MV_MIN(dns.answers.ttl), null), Esql.private_ip = CASE(Esql.is_private, dns.resolved_ip, null), Esql.public_ip = CASE(not Esql.is_private, dns.resolved_ip, null) | stats Esql.resolved_ip_observation_count = COUNT(*), Esql.resolved_ip_count = COUNT_DISTINCT(dns.resolved_ip), Esql.first_public_answer = MIN(Esql.public_time), Esql.first_private_answer = MIN(Esql.private_time), Esql.min_private_event_ttl = MIN(Esql.private_event_ttl), Esql.public_ips = MV_SLICE(VALUES(Esql.public_ip), 0, 100), Esql.private_ips = MV_SLICE(VALUES(Esql.private_ip), 0, 100), Esql.dataset_values = MV_SLICE(VALUES(Esql.dataset), 0, 10), Esql.observer_name_values = MV_SLICE(VALUES(observer.name), 0, 20) by Esql.client_ip, dns.question.name, dns.question.registered_domain | eval Esql.same_timestamp = Esql.first_public_answer == Esql.first_private_answer, Esql.transition_seconds = DATE_DIFF("seconds", Esql.first_public_answer, Esql.first_private_answer) | where Esql.first_public_answer is not null and Esql.first_private_answer is not null and ( Esql.same_timestamp or ( Esql.first_public_answer < Esql.first_private_answer and Esql.min_private_event_ttl is not null and Esql.min_private_event_ttl <= 60 and Esql.transition_seconds <= 300 ) ) | keep Esql.*, dns.*

Install detection rules in Elastic Security

Detect Potential DNS Rebinding from Public to Private Address in the Elastic Security detection engine by installing this rule into your Elastic Stack.

To setup this rule, check out the installation guide for Prebuilt Security Detection Rules(external, opens in a new tab or window).