Skip to content

[Bug]: Browser control server vulnerable to DNS rebinding attacks #4949

@coygeek

Description

@coygeek

Summary

Severity: P0/Critical (Score: 115/150)
CWE: CWE-350 - Reliance on Reverse DNS Resolution for a Security-Critical Action
OWASP: A05:2021 - Security Misconfiguration
File: src/browser/server.ts:33

The browser control server binds to 127.0.0.1 but lacks Host header validation, making it vulnerable to DNS rebinding attacks. Combined with the evaluate endpoint (#4950), this allows remote arbitrary JavaScript execution in controlled browser tabs.

Why this is critical: DNS rebinding turns a "localhost-only" server into a remotely exploitable one. The attack requires only that a victim visits a malicious website while the gateway is running. The attacker's page initially resolves to their server, then rebinds DNS to 127.0.0.1 after the browser caches the connection. All subsequent requests go to the victim's localhost but with the attacker's origin. This is a well-known attack class with proven exploits against development servers, IDEs, and local services.

Triage Assessment

Factor Value Score
Reachability Requires victim to visit malicious site 25/40
Impact Remote code execution via evaluate endpoint 45/50
Exploitability Requires DNS rebinding setup 20/30
Verification file:line ✓, code ✓, attack steps ✓ 25/30
Total 115/150

Steps to reproduce

  1. Start the gateway with browser control enabled
  2. Host a malicious website that:
    • Initially resolves to attacker's IP
    • After TTL expires, rebinds DNS to 127.0.0.1
  3. Have victim visit malicious site
  4. After DNS rebind, site makes POST to http://malicious.com:PORT/act with {kind: "evaluate", fn: "malicious code"}
  5. JavaScript executes in victim's browser-controlled tabs

Expected behavior

The browser control server should validate the Host header to reject requests from rebinding attacks, or require authentication tokens.

Actual behavior

The server accepts requests from any origin as long as they reach 127.0.0.1, enabling DNS rebinding bypass.

Affected code location:

Browser Server (src/browser/server.ts:33):

const server = await new Promise<Server>((resolve, reject) => {
  const s = app.listen(port, "127.0.0.1", () => resolve(s));
  s.once("error", reject);
});
// No Host header validation
// No authentication mechanism

Environment

  • Version: latest (main branch)
  • OS: Any
  • Install method: Any

Impact

  • Remote code execution: Attacker can execute arbitrary JS in browser tabs
  • Credential theft: Access cookies, localStorage of controlled pages
  • Session hijacking: Impersonate user on any site open in controlled browser
  • Network pivoting: Use browser as proxy to internal network

Recommended fix

Add Host header validation:

app.use((req, res, next) => {
  const host = req.headers.host;
  const allowedHosts = ['localhost', '127.0.0.1', `localhost:${port}`, `127.0.0.1:${port}`];
  
  if (!host || !allowedHosts.some(h => host === h || host.startsWith(h + ':'))) {
    res.status(403).json({ error: 'Invalid Host header - possible DNS rebinding attack' });
    return;
  }
  next();
});

Or require authentication token:

const BROWSER_CONTROL_TOKEN = process.env.BROWSER_CONTROL_TOKEN || crypto.randomUUID();
app.use((req, res, next) => {
  if (req.headers['x-browser-control-token'] !== BROWSER_CONTROL_TOKEN) {
    res.status(401).json({ error: 'Invalid or missing authentication token' });
    return;
  }
  next();
});

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingstaleMarked as stale due to inactivity

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions