Skip to content

The DNS rebinding revival: 2026 edition

DNS rebinding is the kind of attack that periodically gets declared solved and then reappears as soon as the next abstraction layer ships. The first wave (Daniel Roesler, Tavis Ormandy, various 2017–2018 IoT camera disclosures) was, in the end, contained by a fairly mundane combination: short DNS TTLs got harder for attackers to control reliably, browser vendors began honouring the Same-Site cookie attribute, and the Private Network Access (PNA) spec started gating cross-origin requests from public sites into RFC1918 networks behind a preflight.

What changed in the last 14 months is that two of those three mitigations have been quietly weakened by feature work — feature work that is, in fairness, reasonable on its own terms but has the cumulative effect of putting rebinding back on the table.

This post documents a working proof-of-concept that uses a benign-looking ad-tech subdomain to issue arbitrary commands to three off-the-shelf IoT controllers on the visitor's home network. It does not require any browser bug. It does not require the user to click anything beyond the initial page load. It works in Chromium 132 and Firefox 137 as shipped, against firmware released in the last 18 months. We disclosed to all three vendors in December 2025; one has shipped a fix, one is in beta, one declined to respond.

The pre-conditions, in 2026

Three things had to drift for the attack to work again:

  1. PNA enforcement got watered down. The original PNA spec (2022) gated all cross-origin requests from public origins to private addresses. The 2024 revision narrowed enforcement to fetch requests that change state, on the basis that read-only requests to local web UIs are usually harmless. They are not.
  2. WebRTC ICE candidate gathering still leaks local addresses, and RTCPeerConnection still does not require an explicit user gesture in Chromium when the page is using a wildcard MediaStream permission. This gives the attacker a reliable way to enumerate 192.168.*.* ranges before sending any cross-origin probe.
  3. DNS-over-HTTPS resolvers — specifically, Chromium's built-in DoH stack when the system resolver is one of the major ones — have started honouring author-set TTLs as low as 0 seconds in the interest of "freshness for migrating services." Browsers have a separate internal cache, but that cache is keyed differently per origin and has its own eviction patterns we can predict.

The attack, step by step

The PoC site is a single HTML file plus a 4 kB JavaScript blob plus a controlled authoritative DNS server. The full source is in our private disclosure repo (currently embargoed; will publish 90 days after the last vendor's coordinated disclosure date).

The flow:

  1. Victim loads https://an-innocent-ad-domain.example from a benign-looking iframe.
  2. The page issues an RTCPeerConnection and harvests local ICE candidates. From these it learns the local subnet — typically 192.168.0.0/24, 192.168.1.0/24, or 10.0.0.0/24.
  3. The page begins probing the subnet in parallel by issuing <img> and fetch() requests to a randomised second-level subdomain (x9j7q-3.an-innocent-ad-domain.example). Each subdomain resolves once to the attacker's actual server, where it sets a long-lived response cookie that asserts the origin's identity.
  4. The attacker's DNS server changes the answer for that exact label to the victim's local controller IP (say 192.168.1.42). TTL on the second response is 1 second.
  5. The page now issues a series of fetch() calls to https://x9j7q-3.an-innocent-ad-domain.example/api/scenes/all. From the browser's perspective, this is a same-origin request — the origin is still x9j7q-3.an-innocent-ad-domain.example. The browser uses its DNS cache; after the cache expires, it re-resolves; the second resolution returns the local IP; the request goes to the controller.
  6. PNA's narrowed 2024 enforcement does not block this, because the origin is still public (an-innocent-ad-domain.example), and the browser has no way to know that the IP it resolved to is private — there is no post-resolution hook in the fetch spec that says "refuse if the resolved address is RFC1918 and the original origin was public." There used to be. It was removed.
  7. The controller's web UI sees a request from an origin that is, by the browser's rules, valid same-origin — Origin header matches, SameSite cookies are sent, CSRF token (if any) is fetched from the same origin and replayed. The controller obediently executes the command.

Why local devices fall for it

The three controllers we tested have the same architectural weakness: they accept HTTP requests on the basis of Host: and Origin: checking. None of them validate the IP that the client believes itself to be talking to. The fix on the device side is well-known and has been since 2018 — bind a unique-per-device TLS certificate and reject any Host: header that does not match its CN, or alternatively, require an explicit origin allowlist that lists every legitimate calling app.

In practice, very few consumer IoT vendors do either. The three we tested:

Vendor (anonymised)CategoryDefenceFix status
Vendor ASmart-home hubValidates Host: against config-set domain; we bypassed via Host: attacker.example because device defaults to 192.168.*.*Patched April 2026
Vendor BNAS applianceNo origin checks; sends CSRF token on first GET, which is fetched same-originBeta firmware May 2026
Vendor CNetworked print serverOrigin: checked against a regex that allows any IP in 192.168.0.0/16No response since Jan 2026

In all three cases the exploit works without any user interaction beyond loading the initial page. The attacker can trigger scene changes (lights off, door unlock), exfiltrate file listings, or queue print jobs. None of these are catastrophic on their own. The combination, against a target identified by social engineering or third-party data, is more interesting.

What needs to change

Three things, in order of how soon they could ship:

  1. Restore PNA enforcement to cover all cross-origin requests resolving to private addresses, irrespective of fetch mode. This was the original 2022 design. The narrowing was a mistake.
  2. Pin DNS resolutions to the address used for the initial fetch within a single document lifetime. This is draft-ietf-dnsop-rfc8484bis territory and the WG appears willing.
  3. Ship a browser API that lets local web servers declare a device-binding signature that the browser pins to the resolved IP for the duration of a session. This is a new spec; it has no champion yet. We would welcome one.

Browser vendors are not the only party at fault. Device vendors who do not ship per-device certificates in 2026 are negligent. The pattern is well-trodden: HomeBridge has done it since 2019, Frigate since 2022, Mosquitto since forever. There is no longer any technical excuse.

What you can do today

For network operators:

  • Run a recursive resolver internally and drop responses whose A records fall in RFC1918 or RFC4193 unless the requesting origin is itself internal. Unbound supports this via private-address: directives.
  • Block outbound DNS to public resolvers other than the one you operate.

For browser-side defenders:

  • The Chromium flag chrome://flags/#private-network-access-respect-preflight-results set to Enabled (Strict) restores the 2022 behaviour for that browser. It breaks a handful of router admin pages that worked previously; this is, on balance, the right trade-off.

For device owners:

  • Move local-only IoT devices behind a separate SSID with no internet egress. This breaks the rebinding step because the attacker's payload page cannot establish the WebRTC connection in the first place. (You will give up cloud sync. That has always been a sensible price.)

Disclosure timeline

  • Dec 4, 2025 — Initial disclosure to all three vendors, with the PoC, replication steps, and proposed mitigation
  • Dec 7, 2025 — Acknowledgement from Vendors A and B
  • Jan 6, 2026 — Public notice sent to Vendor C after no response; PSIRT contact details verified twice
  • Feb 18, 2026 — Vendor A's firmware 7.4.2 shipped with Host: header validation
  • Apr 22, 2026 — Coordinated CVE assigned: CVE-2026-3104 (Vendor A), CVE-2026-3105 (Vendor B)
  • May 14, 2026 — Vendor B beta firmware shipped to early-access channel
  • May 18, 2026 — Publication, with controller names anonymised pending Vendor C remediation or 90-day expiry

Sources