GHSA-vwgx-h8w3-xwj7: DNS resolver reuses a single UDP source port for all queries, easing off-path answer spoofing

Published Oct 23, 2026
·
Updated

The DNS stub resolver in subsys/net/lib/dns/resolve.c binds one UDP socket per configured server when the resolver context is initialised (dnsresolveinitlocked() -> registerdispatcher() -> dnsdispatcherregister()), with the local port left at 0 so the stack picks an ephemeral port exactly once. dnswrite() 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 sysrand16get()) 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 getslotbyid() matching the transaction identifier and a CRC-16 of the queried name — the reply's source address is never checked (dispatchercb() does ARGUNUSED(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 CONFIGDNSRESOLVERCACHE 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: dnsrandomizesourceport() 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 CONFIGDNSRESOLVERRANDOMIZESOURCEPORT (default y).

Affected Software

1 affected component
Zephyr Zephyr RTOS DNS resolver

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Configuration

    Enable CONFIG_DNS_RESOLVER_RANDOMIZE_SOURCE_PORT so the resolver draws a fresh local UDP source port before each query to a server with no outstanding queries.

    DNS stub resolver CONFIG_DNS_RESOLVER_RANDOMIZE_SOURCE_PORT = y

Event History

Oct 23, 2026
CVE Published
via OSV·07:56 PM
Data Sourced
via OSV·07:56 PM
DescriptionSeverityWeakness

Frequently Asked Questions

1

What deployments are exposed to this issue?

Deployments using the Zephyr DNS stub resolver are exposed when it has initialized a resolver context with configured DNS servers. The resolver creates one UDP socket per configured server and keeps the same ephemeral source port for the lifetime of that context.

2

What does an attacker need to spoof a DNS response?

An off-path attacker needs to determine the resolver's stable UDP source port and guess the 16-bit transaction identifier for an active query. The port can be learned from a single observed query or inferred by probing the ephemeral UDP range for the port that does not return an ICMP port-unreachable response.

3

Does the resolver validate that a reply came from the configured DNS server?

No. Reply acceptance is described as relying only on a matching transaction identifier and CRC-16 of the queried name; the reply source address is not checked.

4

Why does the fixed source port materially reduce spoofing difficulty?

The transaction identifier is the only per-query value that varies. RFC 5452 section 9.2 requires both the transaction identifier and source port to vary, so a known fixed port removes one value an attacker would otherwise need to guess.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203