Skip to main content
CVE Vulnerability Database
Vulnerability Database/CVE-2026-49353

CVE-2026-49353: 9Router Authentication Bypass Vulnerability

CVE-2026-49353 is an authentication bypass flaw in 9Router that allows attackers to spoof headers and bypass local-only access controls. This post covers the technical details, affected versions, and mitigation strategies.

Published:

CVE-2026-49353 Overview

CVE-2026-49353 is an authentication bypass vulnerability in 9Router, an AI router and token saver application. The flaw affects versions 0.4.45 and earlier. The src/dashboardGuard.js local-only access gate relies on Host and Origin HTTP headers within the isLocalRequest() function to protect sensitive API endpoints. Attackers can spoof these headers in reverse proxy or tunnel deployments to reach protected paths including /api/mcp/*, /api/tunnel/*, and /api/cli-tools/*. Successful exploitation grants access to Model Context Protocol (MCP) child process stdin paths. The vulnerability is categorized under [CWE-290] Authentication Bypass by Spoofing.

Critical Impact

Header spoofing allows unauthenticated network attackers to bypass local-only restrictions and reach MCP child process stdin, tunnel management, and CLI tool endpoints.

Affected Products

  • 9Router versions 0.4.45 and earlier
  • Deployments using reverse proxies (nginx, Caddy, Traefik) fronting 9Router
  • Deployments exposing 9Router through tunnel services (Tailscale, Cloudflare Tunnel, ngrok)

Discovery Timeline

  • 2026-07-15 - CVE-2026-49353 published to NVD
  • 2026-07-16 - Last updated in NVD database

Technical Details for CVE-2026-49353

Vulnerability Analysis

The root defect resides in src/dashboardGuard.js, which enforces access control for administrative and internal API routes. The guard implements an isLocalRequest() predicate that inspects the incoming request's Host and Origin headers to decide whether the caller originates from the local machine. Endpoints under /api/mcp/*, /api/tunnel/*, and /api/cli-tools/* are gated behind this check.

Because HTTP headers are attacker-controlled inputs, the check trusts unauthenticated data. Any client that can reach the 9Router HTTP interface can supply Host: localhost or a matching Origin value and satisfy the guard. This is particularly exploitable in reverse proxy and tunnel deployments, where the network boundary is no longer the loopback interface.

Root Cause

The underlying weakness is [CWE-290] Authentication Bypass by Spoofing. The dashboard guard conflates header content with origin identity. It does not verify the request against the socket's actual remote address, mutual TLS, a shared secret, or a signed session token. Reverse proxies and tunnels forward client-supplied headers verbatim unless explicitly rewritten, so the loopback assumption breaks.

Attack Vector

An attacker sends HTTP requests to a publicly reachable 9Router instance and injects spoofed Host and Origin header values that match the local-only allowlist. The isLocalRequest() predicate returns true, and the request is routed to the protected handler. Reaching /api/mcp/* provides a path to MCP child process stdin, enabling instruction injection into downstream language model tooling. Reaching /api/tunnel/* and /api/cli-tools/* exposes management functionality intended only for the local operator.

javascript
// Security patch in src/dashboardGuard.js
// fix: deny-by-default API auth + safe SSE controller
   return token === await getCliToken();
 }
 
+// Public API paths — no auth required (LLM API has its own key auth inside handler).
+const PUBLIC_API_PATHS = [
+  "/api/health",
+  "/api/init",
+  "/api/locale",
+  "/api/auth/login",
+  "/api/auth/logout",
+  "/api/auth/status",
+  "/api/auth/oidc",
+  "/api/version",
+  "/api/settings/require-login",
+];
+
+// Public top-level prefixes (LLM API endpoints with their own API key auth).
+const PUBLIC_PREFIXES = ["/v1", "/v1beta"];
+
 // Always require JWT token regardless of requireLogin setting
 const ALWAYS_PROTECTED = [
   "/api/shutdown",
   "/api/settings/database",
+  "/api/version/shutdown",
+  "/api/version/update",
+  "/api/oauth/cursor/auto-import",
+  "/api/oauth/kiro/auto-import",
 ];

Source: GitHub commit bb86808. The patch replaces the header-based local-only heuristic with a deny-by-default allowlist and always-protected route enforcement.

Detection Methods for CVE-2026-49353

Indicators of Compromise

  • HTTP requests to /api/mcp/*, /api/tunnel/*, or /api/cli-tools/* where the socket remote address is non-loopback but the Host header is localhost or 127.0.0.1.
  • Requests bearing Origin: http://localhost or similar loopback values arriving through a reverse proxy or tunnel forwarder.
  • Unexpected MCP child process stdin activity or spawned CLI tool invocations without a corresponding authenticated dashboard session.

Detection Strategies

  • Enable proxy access logging that captures both the forwarded client IP and the raw Host/Origin headers, then alert on mismatches.
  • Correlate 9Router application logs with process telemetry to flag MCP child process launches that lack a preceding authenticated user action.
  • Baseline normal traffic patterns to /api/tunnel/* and /api/cli-tools/* and alert on requests from external source addresses.

Monitoring Recommendations

  • Monitor for the 9Router version string in deployed instances and flag any running at 0.4.45 or earlier.
  • Track outbound network activity from MCP child processes for signs of instruction injection or data exfiltration.
  • Review reverse proxy configurations for missing Host and Origin header sanitization.

How to Mitigate CVE-2026-49353

Immediate Actions Required

  • Upgrade 9Router to version 0.4.46 or later, which introduces the deny-by-default authentication model.
  • If immediate upgrade is not possible, restrict inbound access to 9Router at the network layer so only loopback or a trusted management VLAN can reach it.
  • Audit reverse proxy and tunnel configurations to strip or overwrite client-supplied Host and Origin headers before forwarding to 9Router.

Patch Information

The vendor published the fix in 9Router release v0.4.46. The remediation lands across two commits: commit 5e1c126 hardens the public API and local-only access gates, and commit bb86808 introduces deny-by-default API authentication and a safe Server-Sent Events (SSE) controller. Full details are documented in the GitHub Security Advisory GHSA-6g2f-w7g3-77vf.

Workarounds

  • Configure the reverse proxy to reject or rewrite requests where the client-supplied Host header does not match the proxy's expected upstream value.
  • Bind 9Router strictly to 127.0.0.1 and require authenticated SSH port forwarding or a mutually authenticated tunnel for remote access.
  • Place an authenticating reverse proxy in front of 9Router that enforces a session token or client certificate on /api/mcp/*, /api/tunnel/*, and /api/cli-tools/*.
bash
# Nginx configuration example: strip attacker-supplied Host/Origin
# and enforce upstream identity before proxying to 9Router
server {
    listen 443 ssl;
    server_name router.example.com;

    location / {
        proxy_set_header Host $proxy_host;
        proxy_set_header Origin "";
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_pass http://127.0.0.1:3000;
    }

    # Require client certificate for sensitive routes
    location ~ ^/api/(mcp|tunnel|cli-tools)/ {
        ssl_verify_client on;
        proxy_set_header Host $proxy_host;
        proxy_set_header Origin "";
        proxy_pass http://127.0.0.1:3000;
    }
}

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Default Legacy - Prefooter | Experience the World’s Most Advanced Cybersecurity Platform

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.