# WeKnora has DNS Rebinding Vulnerability in web_fetch Tool that Allows SSRF to Internal Resources

WeKnora's web_fetch tool validates URLs once but Chromedp re-resolves DNS, so a rebinding domain bypasses the private-IP check and reaches internal services.

- Payout: Research
- Severity: high
- Program: WeKnora
- Found by: aleister1102, Haruna38
- Disclosed: 2026-03-06
- CVE: CVE-2026-30858
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-h6gw-8f77-mmmp
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-h6gw-8f77-mmmp
- Tags: area:ai domain:ai domain:web impact:data-leak impact:internal-access kind:research platform:ghsa sev:high tech:go tech:llm technique:race-window vuln:access-control vuln:ssrf

**Target:** web_fetch agent tool / headless Chromedp fetcher
**Root cause:** URL validation resolves the hostname once in validateParams(), but the original hostname is passed to fetchWithChromedp() where Chromedp performs an independent DNS lookup with no pinning, creating a TOCTOU gap.
**Steps:**
1. Deploy a rebinding DNS server returning `1.1.1.1` for the first query and `127.0.0.1` for subsequent queries with TTL 0 (`--ip1 1.1.1.1 --ip2 0.0.0.0 --first-response-count 1 --reset-time 0`).
2. Run a local HTTP server on the backend host, e.g. `python -m http.server 8888`.
3. Point the attacker domain at the rebinding DNS server.
4. Enable web search on an agent and prompt it to fetch `http://attacker.example.com`.
5. First DNS query during validateParams resolves to 1.1.1.1 and passes; the Chromedp navigation re-resolves to 127.0.0.1 and fetches the internal server.
6. Reset the sequence via reset.domain.com and retry since the race is not fully reliable.
**How found:** Code reading of the fetch chain: noticed validateParams() resolves the host once but the unchanged hostname flows to fetchHTMLContent()/fetchWithChromedp(), where chromedp.Navigate performs its own DNS lookup — a classic TOCTOU/pinning gap.
**Exploitation:** Attacker controls a rebinding domain and induces the agent (via prompt) to fetch it. Validation sees a public IP; Chromedp's independent resolution hits localhost/internal IPs, returning internal content to the attacker through the agent's fetch output.
**Escalation:** In cloud environments the same primitive could reach metadata endpoints such as AWS IMDSv1 to obtain credentials and secrets.
**Insight:** When a fetcher validates a URL then hands the raw hostname to a headless browser, the browser's resolver is a second, unpinned lookup — test any validate-then-navigate chain with a rebinding domain rather than assuming the check covers the fetch.

## Proof of concept (copied exactly from the original report)

**DNS rebinding server systemd config with alternating IPs**

```
ExecStart=/root/.proto/shims/python -u /root/Repos/dns-rebinding-server/server.py --token aleister1102 --domain aleister.ninja --port 53 --global-tracking --ip1 1.1.1.1 --ip2 0.0.0.0 --first-response-count 1 --reset-time 0
```

**Local HTTP server setup for SSRF target**

```
python -m http.server 8888
```

