Skip to content

[Bug]: Sandbox browser bridge is started without auth token, allowing cross-session local takeover #11023

@coygeek

Description

@coygeek

CVSS Assessment

Metric Value
Score 9.2 / 10.0
Severity Critical
Vector CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:L

CVSS v3.1 Calculator

Summary

The sandbox browser bridge supports bearer-token protection, but the primary sandbox startup path calls it without an authToken. This creates an unauthenticated per-session browser control API, allowing any local process to interact with another session’s sandboxed browser state.

Affected Code

File: src/browser/bridge-server.ts:33

const authToken = params.authToken?.trim();
if (authToken) {
  app.use((req, res, next) => {
    const auth = String(req.headers.authorization ?? "").trim();
    if (auth === `Bearer ${authToken}`) {
      return next();
    }
    res.status(401).send("Unauthorized");
  });
}

Sandbox path starts bridge without passing authToken:

File: src/agents/sandbox/browser.ts:192

return await startBrowserBridgeServer({
  resolved: buildSandboxBrowserResolvedConfig({
    controlPort: 0,
    cdpPort: mappedCdp,
    headless: params.cfg.browser.headless,
    evaluateEnabled: params.evaluateEnabled ?? DEFAULT_BROWSER_EVALUATE_ENABLED,
  }),
  onEnsureAttachTarget,
});

Unauthenticated routes include sensitive actions:

File: src/browser/routes/agent.act.ts:447

app.post("/download", async (req, res) => {
  // ...
  const out = toStringOrEmpty(body.path);
  // ...
  const result = await pw.downloadViaPlaywright({
    cdpUrl: profileCtx.profile.cdpUrl,
    targetId: tab.targetId,
    ref,
    path: out,

Attack Surface

How is this reached?

  • Network (HTTP/WebSocket endpoint, API call)
  • Adjacent Network (same LAN, requires network proximity)
  • Local (local file, CLI argument, environment variable)
  • Physical (requires physical access to machine)

Authentication required?

  • None (unauthenticated/public access)
  • Low (any authenticated user)
  • High (admin/privileged user only)

Entry point: Sandbox browser bridge URL (bridgeUrl) returned by ensureSandboxBrowser() and served by startBrowserBridgeServer().

Exploit Conditions

Complexity:

  • Low (no special conditions, works reliably)
  • High (requires race condition, specific config, or timing)

User interaction:

  • None (automatic, no victim action needed)
  • Required (victim must click, visit, or perform action)

Prerequisites: Sandbox browser mode enabled and a session has started a bridge (which binds a local port). Attacker only needs local process execution on the same machine.

Impact Assessment

Scope:

  • Unchanged (impact limited to vulnerable component)
  • Changed (can affect other components, escape sandbox)

What can an attacker do?

Impact Type Level Description
Confidentiality High Read cookies/storage/network response bodies from another session’s sandbox browser context
Integrity High Drive browser actions and alter browser state for another session; leverage file-writing browser actions (e.g., download path control)
Availability Low Interrupt or reset active browser workflows for other sessions

Steps to Reproduce

  1. Enable sandbox browser mode and trigger a session that initializes ensureSandboxBrowser().
  2. Obtain the session bridge URL (e.g., from runtime metadata/state where bridgeUrl is recorded).
  3. Call bridge endpoints without credentials:
    • curl <bridgeUrl>/profiles
    • curl -X POST <bridgeUrl>/start
    • curl -X POST <bridgeUrl>/tabs/open -H 'Content-Type: application/json' -d '{"url":"https://example.com"}'
  4. Access sensitive state without credentials:
    • curl "<bridgeUrl>/cookies?targetId=<returnedTargetId>"
  5. Observe that no Authorization header is required despite bridge support for bearer auth.

Recommended Fix

  • Generate a cryptographically strong per-bridge token when creating sandbox bridges.
  • Pass that token via authToken to startBrowserBridgeServer(...) and enforce it for all requests.
  • Scope token disclosure only to the owning session context, and rotate token when bridge is recreated.
  • Optionally reject bridge startup if auth token setup fails.

References

  • CWE: CWE-862 - Missing Authorization
  • CWE: CWE-269 - Improper Privilege Management

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    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