{"id":"CVE-2026-79981","kind":"vulnerability","aliases":["CVE-2026-79981"],"related":[],"title":"Zephyr DNS resolver reuses one UDP source port, enabling DNS spoofing","description":"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.\n\nThe 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.\n\nAn 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.\n\nThe 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).","summary":"The Zephyr DNS stub resolver sends every query from a single fixed UDP source port and accepts replies after checking only the 16-bit transaction ID, without checking the source address. An attacker on the same network segment or able to reach the device over UDP can flood forged answers and win the race to make the device resolve an attacker-chosen address.","severity":"medium","cvss":6.5,"epss":0.00195,"exploited":false,"exploited_at":null,"products":[],"ecosystems":[],"tags":["dns","embedded","iot","runtime","zephyr"],"not_affected_if":["dns-resolver-unused","mdns-or-llmnr-only","tls-cert-validation"],"how_to_check":"- Check whether the device firmware is built with Zephyr's DNS resolver (`CONFIG_DNS_RESOLVER`) and whether `CONFIG_DNS_RESOLVER_RANDOMIZE_SOURCE_PORT` is disabled or absent in the build configuration\n- Check the Zephyr version in use against the fixed release, if the sources name one: unknown","fix":"- Upgrade Zephyr to a release containing the fix that adds `dns_randomize_source_port()`: unknown\n- Keep `CONFIG_DNS_RESOLVER_RANDOMIZE_SOURCE_PORT` enabled (default `y`)\n- Validate TLS certificates for resolved endpoints so a forged address fails the handshake","status":"open","published_at":"2026-10-23T20:17:28+00:00","first_seen_at":"2026-10-23T21:57:34+00:00","triaged_at":"2026-10-25T03:47:10+00:00","updated_at":"2026-10-25T12:38:49+00:00","category":"runtime","confidence":0.7,"triage":{"at":"2026-10-25T03:47:10Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade Zephyr to a release containing the fix that adds `dns_randomize_source_port()`: unknown\n- Keep `CONFIG_DNS_RESOLVER_RANDOMIZE_SOURCE_PORT` enabled (default `y`)\n- Validate TLS certificates for resolved endpoints so a forged address fails the handshake","reach":"network","title":"Zephyr DNS resolver reuses one UDP source port, enabling DNS spoofing","impact":"medium","summary":"The Zephyr DNS stub resolver sends every query from a single fixed UDP source port and accepts replies after checking only the 16-bit transaction ID, without checking the source address. An attacker on the same network segment or able to reach the device over UDP can flood forged answers and win the race to make the device resolve an attacker-chosen address.","category":"runtime","nicknames":[],"who_cares":["zephyr","embedded","iot","dns"],"confidence":0.7,"needs_auth":"no","worst_case":"Attacker makes the device resolve a forged address, possibly cached for attacker-chosen TTL","analyst_note":"Confirm the fixed Zephyr release version and commit from the NVD entry or Zephyr advisory; sources give no fixed version.","how_to_check":"- Check whether the device firmware is built with Zephyr's DNS resolver (`CONFIG_DNS_RESOLVER`) and whether `CONFIG_DNS_RESOLVER_RANDOMIZE_SOURCE_PORT` is disabled or absent in the build configuration\n- Check the Zephyr version in use against the fixed release, if the sources name one: unknown","needs_analyst":true,"not_affected_if":["tls-cert-validation","mdns-or-llmnr-only","dns-resolver-unused"],"who_is_affected":"Embedded and IoT devices built on Zephyr that use the DNS stub resolver to look up hosts, especially on shared LAN, Wi-Fi or mesh segments or with globally routable addresses. Applications that validate TLS certificates for the resolved endpoint are largely protected, and multicast DNS and LLMNR are not affected by this change.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"medium"},"cost_usd":0.0008,"input_tokens":4070,"output_tokens":785,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-16':272C '-2026':13A,16A '-79981':14A,17A '0':121C,448C '16':45B,162C,349C,465C '31':468C '5452':179C '9.2':181C 'abl':62B 'accept':39B,253C 'across':347C 'addr':290C 'address':53B,86B,281C,337C,372C 'also':218C 'answer':72B,228C,293C,341C 'appear':300C 'applic':366C,399C 'arg':288C 'attack':55B,84B,194C,317C,370C,388C,392C 'attacker-chosen':83B,369C,387C 'behaviour':484C 'bind':93C,145C 'bit':46B,163C,350C,469C 'bound':445C 'cach':378C 'cb':286C 'certif':403C 'cheap':205C 'check':42B,50B,284C 'chosen':85B,371C,389C 'close':435C 'come':302C 'config':375C,488C 'configur':98C,305C 'constant':151C 'context':103C,158C 'control':486C 'crc':271C 'ctx':166C 'cve':12A,15A 'decid':258C 'default':494C 'devic':66B,80B,323C,336C 'dispatch':111C,113C,285C,434C 'dns':2A,10A,18B,25B,88C,106C,112C,131C,376C,428C,471C,489C 'draw':413C 'drawn':170C 'embed':19B 'enabl':9A,379C 'endpoint':407C 'ephemer':127C,224C 'even':296C 'everi':29B,135C 'exact':129C 'exclud':475C 'fi':328C 'fix':34B,188C,412C,482C 'flood':70B,340C 'forg':71B,292C,381C 'found':220C 'fresh':415C 'get':174C,261C 'global':334C 'guess':197C,461C 'icmp':234C 'id':48B,169C,264C 'identifi':165C,200C,268C,351C 'init':108C 'initialis':105C 'iot':20B 'lan':325C 'larg':409C 'learn':207C 'left':119C 'legitim':360C 'lifetim':154C 'llmnr':473C 'local':117C,416C 'lock':109C 'make':78B,364C 'match':265C 'mesh':330C 'multicast':470C 'name':276C,346C 'need':294C,309C 'network':59B 'never':283C 'new':454C 'noth':426C 'observ':210C 'off-path':191C 'one':5A,94C,245C 'outstand':427C 'path':193C 'per':97C,176C,459C 'per-queri':458C 'perman':214C 'pick':125C 'port':8A,37B,118C,128C,149C,189C,203C,236C,246C,315C,417C,431C,447C,455C,478C,493C 'port-unreach':235C 'predict':344C 'probe':222C 'protect':410C 'protocol':481C 'protocol-fix':480C 'queri':30B,137C,167C,177C,211C,275C,345C,355C,420C,460C 'race':76B,358C,397C 'rais':456C 'rand16':173C 'random':429C,491C 'rang':226C,249C 're':144C,442C 're-bind':143C 're-regist':441C 'reach':64B,311C 'reachabl':320C 'record':382C 'regist':110C,114C,443C 'reopen':437C 'repli':40B,256C,278C 'requir':182C,394C 'resolv':3A,27B,81B,90C,102C,107C,157C,240C,313C,367C,377C,406C,490C 'retain':384C 'reus':4A 'reveal':212C 'rfc':178C 'rough':464C,467C 'routabl':335C 'runtim':21B 'section':180C 'segment':60B,331C 'select':452C 'send':28B,134C 'server':99C,306C,361C,423C 'silent':252C 'singl':33B,209C 'slot':262C 'socket':96C,141C,242C,439C 'sole':259C 'sourc':7A,36B,52B,148C,280C,430C,492C 'space':352C,462C 'spoof':11A,298C 'stabl':202C 'stack':124C 'stay':251C 'still':393C 'stub':26B,89C 'subsequ':136C 'subsys/net/ip/connection.c':227C 'subsys/net/lib/dns/resolve.c':92C 'sys':172C 'system':451C 'therefor':339C 'tls':402C 'transact':47B,164C,267C 'ttl':390C 'udp':6A,35B,68B,95C,225C,231C,319C 'unicast':230C 'unmatch':229C 'unreach':237C 'unregist':432C 'unus':289C 'valid':401C 'vari':175C,185C 'wi':327C 'wi-fi':326C 'win':74B,363C,395C 'window':356C 'without':49B,142C 'write':132C 'y':495C 'zephyr':1A,22B,24B","nicknames":[],"mitigations":[],"who_affected":"Embedded and IoT devices built on Zephyr that use the DNS stub resolver to look up hosts, especially on shared LAN, Wi-Fi or mesh segments or with globally routable addresses. Applications that validate TLS certificates for the resolved endpoint are largely protected, and multicast DNS and LLMNR are not affected by this change.","tier":"C","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-23T20:17:28+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:H/A:N","cvss_by":"vulnerabilities@zephyrproject.org","poc_count":0,"sources":[{"source":"nvd","external_id":"CVE-2026-79981","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-79981","severity":null,"published_at":"2026-10-23T20:17:28+00:00","modified_at":"2026-10-23T20:17:28+00:00","first_seen_at":"2026-10-23T21:57:34+00:00","fetched_at":"2026-10-23T22:29:04+00:00","note":null},{"source":"epss","external_id":"CVE-2026-79981","url":"https://api.first.org/data/v1/epss?cve=CVE-2026-79981","severity":null,"published_at":null,"modified_at":null,"first_seen_at":"2026-10-25T12:38:49+00:00","fetched_at":"2026-10-25T12:38:49+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}