Executive brief
9router is an open-source AI router/proxy that manages requests to multiple LLM providers using stored API keys. The application's authentication guard incorrectly determines whether a request is "local" (and thus exempt from API key requirements) by checking the client-controlled Host header instead of the actual TCP peer IP address. Because 9router binds to all network interfaces (`0.0.0.0`) by default, a remote attacker can send a request with `Host: localhost` to bypass authentication entirely. This allows the attacker to relay AI API calls billed to the victim's paid accounts, or to trigger server-side requests to internal/cloud services (SSRF) with JSON responses reflected back.
Technical details
The vulnerability is a Header Spoofing / Authentication Bypass in 9router's `src/dashboardGuard.js` `isLocalRequest()` function. The function checks only the client-supplied `Host` header (via `request.headers.get("host")`) and an optional `Origin` header to decide if a request is local, and never consults the actual socket peer IP address. When `isLocalRequest()` returns true, the `canAccessPublicLlmApi()` gate bypasses all API-key authentication checks on `/v1`, `/v1beta`, `/api/v1`, and `/api/v1beta` endpoints. Combined with two configuration defaults—binding to `0.0.0.0` (DEFAULT_HOST in cli/cli.js) and the absence of `requireApiKey` in DEFAULT_SETTINGS (making handler-level key checks skip by default)—any remote attacker on the network can send `Host: localhost` to reach the handler logic and access configured providers' credentials. The `/v1/search` endpoint with the built-in `searxng` provider (marked `noAuth: true`) additionally allows the attacker to supply `provider_options.baseUrl` in the request body, which is used unsanitized in an outbound `fetch()` call, enabling SSRF. Attack vector is network with no authentication, no user interaction, and no special privileges required.
Affected products
- decolua 9router <0.5.2
Timeline
- 2026-07-07: disclosed: GitHub Advisory GHSA-86m2-fcxq-5q7c published
- 2026-08-28: advisory: Advisory updated with live confirmation and additional details
- 2026-08-28: patched: Fixed in version 0.5.2