Blog

DNS Rebinding: How a Browser Becomes a Weapon Against Your Internal Network.

DNS-Rebinding
Uncategorized

DNS Rebinding: How a Browser Becomes a Weapon Against Your Internal Network.

Imagine an employee clicks a link — an ordinary-looking website, no warnings, no malware alerts. But at that moment, their browser is already scanning the office’s internal network and sending data to an attacker. The router, internal services, admin panels — everything the browser can reach.

This is DNS rebinding. The attack has been known since 1996, it still works today, and most administrators never think about it.

HOW THE BROWSER PROTECTS INTERNAL RESOURCES — AND WHY IT BREAKS DOWN

Browsers follow the same-origin policy: JavaScript on evil.com cannot read responses from bank.com. This is the basic defense against cross-site attacks.

But this rule has a blind spot. The same-origin policy relies on the domain name, not the IP address. The browser doesn’t track what IP address is behind a domain — what matters is that the request goes to the same hostname.

DNS rebinding exploits exactly this. The attacker controls the domain evil.com and its DNS server. Everything starts when the victim opens a page on evil.com.

ANATOMY OF THE ATTACK, STEP BY STEP

Step 1. The victim opens http://evil.com. The browser makes a DNS query and receives the attacker’s real IP, say 1.2.3.4. The page loads. It contains JavaScript.

Step 2. The DNS record for evil.com has TTL = 1 second. TTL (Time To Live) is how long the browser caches a DNS response — normally minutes or hours. Here it is one second. The browser almost immediately forgets the old IP and goes back to DNS on the next request.

Step 3. The JavaScript on the page waits a few seconds, then makes a new request to evil.com — for example, fetch('http://evil.com/api/data').

Step 4. The browser makes another DNS query for evil.com. This time the attacker’s DNS server responds with a different IP — say, 192.168.1.1. That’s the victim’s router on their local network.

Step 5. The browser reasons: same domain — evil.com, same-origin is not violated. The request goes to 192.168.1.1 — directly to the victim’s router. JavaScript reads the response and sends it to the attacker.

The victim knows nothing about any of this.

WHAT THE ATTACKER CAN DO

After a successful rebind, the victim’s browser is a proxy into their network. Through JavaScript, the attacker can:

Scan the internal network — iterate through addresses 192.168.1.1192.168.1.254, check open ports, identify running services.

Attack the router — most home and office routers have a web interface on port 80 or 8080. No password or default credentials means full access.

Attack internal services — server management panels, Grafana, Jenkins, Kubernetes dashboard, Elasticsearch without authentication, internal APIs. Everything reachable from the victim’s machine is reachable by the attacker.

Attack localhost — if something is running on 127.0.0.1 on the victim’s machine (a dev server, local database, REST API), rebinding works there too. The attacker simply rebinds to 127.0.0.1 instead of a private range.

REAL-WORLD CASES

In 2018, researcher Brannon Dorsey demonstrated DNS rebinding against Google Home and Chromecast — the devices leaked geolocation data and could be controlled through any browser that opened a malicious page. At the same time, Tavis Ormandy of Google Project Zero demonstrated the same attack against BitTorrent clients uTorrent and Transmission, and Blizzard games. Google patched the vulnerability several months after the publication.

Also in 2018, NCC Group released Singularity — an open source framework for automating DNS rebinding attacks. A live demo is available at rebind.it. The attack no longer required specialist knowledge.

Almost all browsers are vulnerable. In October 2025, Chrome 142 added Local Network Access (LNA) — sites must now request user permission before making any request to the local network or loopback. Silent rebinding without user interaction no longer works in Chrome 142+. Firefox is rolling out LNA gradually — enabled by default in Nightly, deploying to users with Enhanced Tracking Protection Strict starting Firefox 147. Safari has not implemented LNA.

HOW TO PROTECT YOURSELF

Chrome 142 LNA partially closes the problem on the browser side, but relying on it alone is not enough: Firefox and Safari lag behind, users can click “Allow” on the prompt, and a server cannot know which browser will make a request. A short TTL is a legitimate DNS configuration that cannot be prohibited. Solid protection is built on infrastructure, independent of the browser.

Layer 1 — DNS resolver with rebinding protection

This layer protects client machines and office networks — where a browser is actually used and can become a proxy for an attacker. A server has no browser, so this layer is not needed on the server itself. But for your workstation or corporate network — it matters.

For the server itself, good practice is to configure encrypted DNS through systemd-resolved. This does not protect against rebinding, but it encrypts the server’s DNS traffic — your provider cannot see which domains the machine resolves, and it protects against DNS response spoofing in transit:

[Resolve]
DNS=9.9.9.9#dns.quad9.net
DNSOverTLS=yes

Here 9.9.9.9 is Quad9’s IP address, and #dns.quad9.net is the hostname for TLS certificate verification. DNSOverTLS=yes enables encryption of DNS queries. After editing:

sudo systemctl restart systemd-resolved

Against rebinding specifically, dnsmasq with the stop-dns-rebind option is what helps. This is configured on the router (if it supports dnsmasq, e.g. OpenWRT) — and protects all devices on the network at once:

# /etc/dnsmasq.conf
stop-dns-rebind
rebind-localhost-ok

stop-dns-rebind makes dnsmasq drop responses where a public domain resolves to an RFC1918 address (192.168.x.x, 10.x.x.x, 172.16-31.x.x) or 127.x.x.x. rebind-localhost-ok is an exception for 127.0.0.1: some legitimate services on the router use loopback internally and would break without this line.

Layer 2 — Host header validation in nginx

This is the key protection for any service. If nginx only accepts requests from known hostnames, rebinding will not work — because the request will arrive with a Host: evil.com header, not the expected hostname.

server {
    listen 80 default_server;
    # Default block — drops everything unknown
    return 444;
}

server {
    listen 80;
    server_name myserver.example.com;
    # Only this host is processed
}

return 444 is nginx-specific: the server closes the connection without sending a single byte. The attacker does not even receive an error status — the connection is simply dropped.

For internal services where requests need to be accepted by both hostname and IP — you can explicitly list the allowed values:

server {
    listen 8080;
    server_name internal.company.local 192.168.1.50;

    if ($host !~* ^(internal\.company\.local|192\.168\.1\.50)$) {
        return 444;
    }
}

$host is the nginx variable containing the value of the incoming request’s Host header. The condition !~* means “does not match, case-insensitively.” If the Host header is neither internal.company.local nor 192.168.1.50 — the connection is closed. Any rebinding with a foreign domain gets 444.

Layer 3 — Authentication on all internal services

Rebinding only works if the service responds to requests without verification. Grafana without a password, Jenkins without authorization, Elasticsearch on 0.0.0.0 — these are all targets.

The rule is simple: any service with a web interface must require authentication, even if it is “internal” and “not accessible from outside.”

Layer 4 — Network segmentation

Services that should not be accessible from a browser should not be accessible from workstations at all. VLANs, nftables, firewall rules at the network level.

nft add rule inet filter forward \
    ip saddr 192.168.10.0/24 \
    ip daddr 192.168.20.0/24 \
    tcp dport { 8080, 9090, 5601 } drop

The rule reads: any traffic from workstations (192.168.10.0/24) to servers (192.168.20.0/24) on ports 8080 (management panels), 9090 (Prometheus) and 5601 (Kibana) — drop. Even if a workstation browser is used as a proxy through rebinding, it physically cannot reach these services. Replace the IP ranges with your own.

AUDIT YOUR INFRASTRUCTURE

Step 1. Check what is listening on all interfaces — these are services accessible from the network, not just from localhost:

ss -tlnp | grep -v 127.0.0.1

Each line in the output is a port open to network connections. Check manually that each one requires authentication:

curl http://localhost:3000   # Grafana — should ask for login
curl http://localhost:9200   # Elasticsearch — should not respond without auth
curl http://localhost:8080   # Jenkins and other panels

Step 2. Check nginx for a block that accepts requests with any Host header:

grep -r "default_server\|server_name _" /etc/nginx/

The command looks for two patterns. default_server — the block nginx uses when no server_name matched the request’s Host header. server_name _ — an explicit catch-all that accepts any hostname.

If the command finds something — that is not yet a problem. What matters is what the file does with the request. Check whether it is active:

ls /etc/nginx/sites-enabled/

If the found file is in sites-enabled — open it and look at the location / block. Dangerous: it has try_files or proxy_pass — the server serves content to any Host. Safe: it has return 444 — the server closes the connection without a response.

If there is no explicit default_server at all — nginx sends requests with an unknown Host to the first active block it finds. This also needs to be closed. Create a new file and paste the following config into it:

sudo nano /etc/nginx/sites-available/default-catch-all.conf
server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name _;
    return 444;
}

Save the file (Ctrl+O, then Ctrl+X in nano), activate it with a symlink and reload nginx:

sudo ln -s /etc/nginx/sites-available/default-catch-all.conf /etc/nginx/sites-enabled/
sudo nginx -t && sudo systemctl reload nginx

Verify the protection works — a request with a foreign Host should return an empty response:

curl -v -H "Host: evil.com" http://localhost/
# Expected result: Empty reply from server

Online testing tool: rebind.it — the Singularity demo site, shows whether your browser is vulnerable to rebinding right now.

CONCLUSION

DNS rebinding is an attack that has been known since 1996 and is not going away, because it is rooted in an architectural constraint, not a bug. The browser cannot be blamed — it works exactly according to the specification.

The protection is straightforward: dnsmasq with stop-dns-rebind on the router, Host header validation in nginx, mandatory authentication on all internal services. Three steps, each taking minutes.

“Grafana has no password because it’s internal” is not an argument. Your colleague’s browser may already be a proxy for an attacker.

Leave your thought here

Your email address will not be published. Required fields are marked *