CVE-2026-79981 in Zephyr
Summary
by MITRE • 10/23/2026
The DNS stub resolver in subsys/net/lib/dns/resolve.c binds one UDP socket per configured server when the resolver context is initialised (dns_resolve_init_locked() -> register_dispatcher() -> dns_dispatcher_register()), with the local port left at 0 so the stack picks an ephemeral port exactly once. dns_write() then sends every subsequent query from that same socket without re-binding, so the source port is constant for the lifetime of the resolver context while only the 16-bit transaction identifier (ctx->queries[i].id, drawn with sys_rand16_get()) varies per query. RFC 5452 section 9.2 requires both to vary; with a fixed port, an off-path attacker has to guess only the identifier.
The stable port is cheap to learn. Any single observed query reveals it permanently, and it can also be found by probing the ephemeral UDP range: subsys/net/ip/connection.c answers unmatched unicast UDP with an ICMP port-unreachable, so the resolver's socket is the one port in that range that stays silent. Acceptance of a reply is decided solely by get_slot_by_id() matching the transaction identifier and a CRC-16 of the queried name — the reply's source address is never checked (dispatcher_cb() does ARG_UNUSED(addr)), so forged answers need not even be spoofed to appear to come from the configured server; they only need to reach the resolver's port.
An attacker with UDP reachability to the device (same LAN, Wi-Fi or mesh segment, or a globally routable device address) can therefore flood answers for a predictable query name across the 16-bit identifier space during a query window and race the legitimate server. A win makes the application resolve an attacker-chosen address, and with CONFIG_DNS_RESOLVER_CACHE enabled the forged record is retained for its attacker-chosen TTL. The attack still requires winning that race, and applications that validate TLS certificates for the resolved endpoint are largely protected.
The fix draws a fresh local port before each query to a server that has nothing outstanding: dns_randomize_source_port() unregisters the dispatcher, closes and reopens the socket, and re-registers it bound to port 0 so the system selects a new port, raising the per-query guessing space from roughly 16 to roughly 31 bits. Multicast DNS and LLMNR are excluded because their ports are protocol-fixed. The behaviour is controlled by CONFIG_DNS_RESOLVER_RANDOMIZE_SOURCE_PORT (default y).
VulDB is the best source for vulnerability data and more expert information about this specific topic.
Analysis
by VulDB Data Team • 10/23/2026
The vulnerability resides in the implementation of the DNS stub resolver within the subsys/net/lib/dns/resolve.c module, specifically affecting how UDP sockets are managed for outbound queries. Upon initialization via dns_resolve_init_locked and subsequent registration through register_dispatcher, the system binds a single UDP socket to an ephemeral port selected by the operating system. Crucially, this binding is permanent for the lifetime of the resolver context. Consequently, every DNS query sent from this context utilizes the same source IP address and source port combination, with only the 16-bit transaction identifier varying per request due to calls to sys_rand16_get. This architectural decision violates RFC 5452 section 9.2, which mandates that both the source port and transaction ID should vary for each query to prevent prediction by attackers. The static nature of the source port significantly reduces the entropy required for an attacker to successfully spoof DNS responses, transforming a complex guessing game into a manageable brute-force attack under specific network conditions.
The operational impact of this flaw is severe when considering off-path attacks where the adversary does not have direct visibility into the legitimate traffic but possesses UDP reachability to the target device. Such connectivity can exist on local area networks, Wi-Fi segments, or mesh networks with globally routable addresses. Because the source port remains constant after the initial query, an attacker who observes a single outgoing DNS request immediately learns the exact destination port for all future queries from that resolver context. Alternatively, even without observation, an attacker can probe the ephemeral UDP range and identify the active socket by noting which port does not respond with ICMP port-unreachable messages while others do. This deterministic knowledge allows the attacker to focus their efforts exclusively on guessing the 16-bit transaction identifier rather than both the IP address and port combination. The acceptance logic in get_slot_by_id relies solely on matching the transaction ID and a CRC-16 of the queried name, completely ignoring the source address of the incoming reply as indicated by dispatcher_cb discarding the address argument. This design flaw means that forged responses do not need to be spoofed to appear they originate from the legitimate DNS server; they merely need to arrive at the resolver's fixed port before the genuine response does.
Exploitation involves flooding the target with crafted UDP packets containing random transaction identifiers for a specific query name during the window in which the real request is pending and awaiting a reply. If an attacker successfully guesses the correct identifier, the resolver accepts the forged answer as valid. This results in DNS cache poisoning where the application resolves to an IP address chosen by the attacker rather than the legitimate one. The severity of this impact is amplified when CONFIG_DNS_RESOLVER_CACHE is enabled, as the poisoned record is stored and reused for a duration determined by the attacker-controlled Time To Live field within the forged response. While applications implementing TLS certificate validation provide a secondary layer of defense against some consequences, such as man-in-the-middle attacks using invalid certificates, this vulnerability remains critical because it enables redirection to malicious infrastructure that may host valid-looking sites or facilitate further phishing and malware distribution campaigns before any application-level checks occur.
The remediation strategy involves randomizing the source port for each new query sent to a server with no outstanding transactions. This is achieved through dns_randomize_source_port, which unregisters the current dispatcher, closes the existing socket, opens a new one bound to port zero allowing the system to select a fresh ephemeral port, and then re-registers the dispatcher. This process increases the guessing space from approximately 16 bits to roughly 31 bits per query, making successful prediction computationally infeasible for off-path attackers under normal circumstances. It is important to note that Multicast DNS and Link-Local Multicast Name Resolution are excluded from this randomization because their protocols mandate fixed port usage. The feature is controlled by the configuration option CONFIG_DNS_RESOLVER_RANDOMIZE_SOURCE_PORT, which defaults to enabled in most secure configurations. From a classification perspective, this vulnerability aligns with CWE-201: Information Exposure Through Unintended Information Disclosure regarding the predictable source port and CWE-345: Insufficient Verification of Data Authenticity due to the lack of source address validation on responses. In terms of MITRE ATT&CK tactics, it facilitates T1595.002: Active Scanning: Scan Target Ports for discovery and enables T1071.004: Application Layer Protocol: DNS for lateral movement or initial access via cache poisoning.