Skip to main content

9router CVE-2026-49353

HIGH
Authentication Bypass by Spoofing (CWE-290)
2026-07-02 https://github.com/decolua/9router GHSA-6g2f-w7g3-77vf
7.5
CVSS 3.1 · Vendor: https://github.com/decolua/9router
Share

Severity by source

Vendor (https://github.com/decolua/9router) PRIMARY
7.5 HIGH
AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:H/A:N
vuln.today AI
9.0 CRITICAL

Network-reachable but AC:H for the required tunnel/proxy exposure plus deterministic-token guessing; PR:N since no legitimate account is needed; S:C and C/I/A:H reflect code execution escaping into a spawned child process.

3.1 AV:N/AC:H/PR:N/UI:N/S:C/C:H/I:H/A:H
4.0 AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/decolua/9router).

CVSS VectorVendor: https://github.com/decolua/9router

CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:H/A:N
Attack Vector
Network
Attack Complexity
High
Privileges Required
None
User Interaction
None
Scope
Changed
Confidentiality
Low
Integrity
High
Availability
None

Lifecycle Timeline

1
Analysis Generated
Jul 02, 2026 - 21:51 vuln.today

DescriptionCVE.org

Summary

The fix for CVE-2026-46339 (unauthenticated RCE via unprotected MCP plugin routes) introduced a local-only access gate in src/dashboardGuard.js that restricts spawn-capable routes (/api/mcp/*, /api/tunnel/*, /api/cli-tools/*) to loopback requests. The gate determines "local" by inspecting the Host and Origin HTTP headers rather than the TCP source address. When 9router is deployed behind a reverse proxy, tunnel (Cloudflare Tunnel, Tailscale - both natively supported), or is subject to DNS rebinding, these headers are attacker-controlled, allowing the local-only gate to be bypassed.

A second factor (CLI token or JWT cookie) is required by canAccessLocalOnlyRoute(), but the CLI token is a deterministic HMAC of the machine ID (getConsistentMachineId), which is stable and predictable on cloud VMs. If the attacker can obtain or guess the machine ID (e.g., via another information disclosure, or on shared-tenant infrastructure), the full chain to MCP child process stdin injection is reachable.

This is a variant / incomplete fix of CVE-2026-46339 - the same attack surface (remote → MCP child process stdin) remains reachable under specific but realistic deployment configurations.

Root Cause

isLocalRequest() at src/dashboardGuard.js:93-101:

javascript
function isLocalRequest(request) {
  if (!isLoopbackHostname(request.headers.get("host"))) return false;
  const origin = request.headers.get("origin");
  if (origin) {
    try {
      if (!isLoopbackHostname(new URL(origin).hostname)) return false;
    } catch { return false; }
  }
  return true;
}

This function trusts Host and Origin headers as proof of local origin. Both are attacker-controlled in any proxied deployment. The LOOPBACK_HOSTS set (localhost, 127.0.0.1, ::1) is checked against these headers, not against the actual connection source IP.

Attack Scenario

Scenario 1: Cloudflare Tunnel / Tailscale Funnel

9router natively supports Cloudflare Tunnel and Tailscale (see LOCAL_ONLY_PATHS entries for /api/tunnel/*). When exposed via tunnel:

  1. Attacker sends request to https://<tunnel-domain>/api/mcp/<plugin>/sse
  2. Sets Host: localhost:3000 and Origin: http://localhost:3000
  3. isLocalRequest() returns true
  4. canAccessLocalOnlyRoute() then requires CLI token or (local + JWT)
  5. CLI token is getConsistentMachineId("9r-cli-auth") - a deterministic HMAC of the machine's hardware/OS identifiers

Scenario 2: DNS Rebinding

  1. Attacker controls evil.com DNS, initially resolving to attacker IP
  2. Victim's browser navigates to evil.com (or via iframe/redirect)
  3. DNS rebinding switches evil.com127.0.0.1
  4. Subsequent fetch to evil.com:3000/api/mcp/<plugin>/message reaches 9router
  5. Host header is evil.com:3000 - this is blocked by the current check (not in LOOPBACK_HOSTS)
  6. However, if the attacker uses localhost:3000 as the request host via CORS or service worker tricks, and the browser sends Host: localhost:3000, the gate opens

Exploitation (when CLI token is obtained)

Once past the gate, the attacker can:

  1. GET /api/mcp/<plugin>/sse - establish SSE session, get sessionId
  2. POST /api/mcp/<plugin>/message - send arbitrary JSON-RPC to the child process stdin
  3. The child process is one of: npx, node, python, python3, uvx, bunx, bun
  4. Depending on the MCP plugin implementation, this can achieve arbitrary code execution on the host

Steps to Reproduce

  1. Deploy 9router behind a reverse proxy or tunnel
  2. From a remote host, send:
http
GET /api/mcp/browser/sse HTTP/1.1
Host: localhost:3000
Origin: http://localhost:3000
x-9r-cli-token: <machine-id-derived-token>
  1. Observe: SSE connection established, endpoint event received with message URL
  2. POST arbitrary JSON-RPC to the message endpoint

Impact

An attacker who can reach a proxied/tunneled 9router instance and obtain the deterministic CLI token can bypass the local-only restriction and interact with MCP child processes (node, python, npx, etc.) via stdin. This achieves the same impact as CVE-2026-46339: remote code execution on the host.

The severity is reduced from CVE-2026-46339's CVSS 10.0 because:

  • Requires proxied/tunneled deployment (not default localhost-only)
  • Requires obtaining the CLI token (deterministic but not trivially guessable without another primitive)

Remediation

  1. Check actual source IP, not headers. Use request.ip, request.socket.remoteAddress, or a trusted X-Forwarded-For header with known proxy configuration instead of Host/Origin for the local-only gate.
  2. Make CLI token non-deterministic. Generate a random token on first run and persist it, rather than deriving from machine ID. Machine IDs are often predictable or discoverable on cloud infrastructure.
  3. Bind MCP routes to loopback at the network layer. If MCP is local-only by design, the server should bind those routes to 127.0.0.1 only, not rely on middleware header checks.

Credit: @snailsploit

AnalysisAI

Authentication-bypass leading to remote code execution in 9router (npm package 9router) lets attackers reach spawn-capable MCP routes that were meant to be loopback-only. This is an incomplete fix for CVE-2026-46339: the local-only gate in src/dashboardGuard.js decides 'local' from attacker-controllable Host and Origin headers instead of the TCP source address, so any proxied or tunneled (Cloudflare Tunnel / Tailscale) deployment can be tricked into treating remote requests as local. …

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

Free forever · No credit card required

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Recon
Identify tunnel/proxy-exposed 9router
Delivery
Obtain or guess machine-ID CLI token
Exploit
Send /api/mcp/<plugin>/sse with forged Host/Origin headers
Install
Bypass local-only gate, capture sessionId
C2
POST crafted JSON-RPC to message endpoint
Execute
Inject into MCP child process stdin
Impact
Execute code on host

Vulnerability AssessmentAI

Exploitation Exploitation requires that 9router be deployed behind a reverse proxy, Cloudflare Tunnel, or Tailscale (i.e., reachable beyond loopback) - the default localhost-only deployment is NOT vulnerable to remote abuse. … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment The vendor CVSS 3.1 score of 7.5 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:C/C:L/I:H/A:N) captures the tension well: the vector is network-reachable with no privileges, but AC:H reflects two real hurdles that materially limit exploitation - the instance must be deployed behind a reverse proxy or tunnel (not the default localhost-only posture), and the attacker must obtain or guess the CLI token. … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario An operator exposes 9router through a Cloudflare Tunnel for remote access. A remote attacker who has separately learned or guessed the target's machine ID sends GET https://<tunnel-domain>/api/mcp/browser/sse with forged headers Host: localhost:3000 and Origin: http://localhost:3000 plus the derived x-9r-cli-token, causing isLocalRequest() to return true and passing the second-factor check. …
Remediation Upstream fix available (PR/commit); released patched version not independently confirmed - apply the fixes in commits 5e1c1261368e06dced1cbc650684561b2c8844db and bb86808582067e4fc6f004508a919efb9970d1d5 referenced by advisory GHSA-6g2f-w7g3-77vf, or update to the vendor release incorporating them once identified. … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

Within 24 hours: Identify all production 9router deployments and classify by network configuration (direct, proxied, or tunneled). …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

More in Python

View all
CVE-2025-24016 CRITICAL POC
9.9 Feb 10

Wazuh SIEM platform versions 4.4.0 through 4.9.0 contain an unsafe deserialization vulnerability in the DistributedAPI t

CVE-2025-27520 CRITICAL POC
9.8 Apr 04

BentoML version 1.4.2 and earlier contains an unauthenticated remote code execution vulnerability through insecure deser

CVE-2025-2945 CRITICAL POC
9.9 Apr 03

pgAdmin 4 contains critical remote code execution vulnerabilities in the Query Tool download and Cloud Deployment endpoi

CVE-2013-5093 MEDIUM POC
6.8 Sep 27

The renderLocalView function in render/views.py in graphite-web in Graphite 0.9.5 through 0.9.10 uses the pickle Python

CVE-2025-32375 CRITICAL POC
9.8 Apr 09

BentoML is a Python library for building online serving systems optimized for AI apps and model inference. Rated critica

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2024-21644 HIGH POC
7.5 Jan 08

pyLoad download manager version prior to 0.5.0b3.dev77 exposes the Flask SECRET_KEY through an unauthenticated endpoint.

CVE-2017-9462 HIGH POC
8.8 Jun 06

In Mercurial before 4.1.3, "hg serve --stdio" allows remote authenticated users to launch the Python debugger, and conse

CVE-2026-39987 CRITICAL POC
9.3 Apr 08

Unauthenticated remote code execution in Marimo ≤0.20.4 allows attackers to execute arbitrary system commands via the `/

CVE-2024-21645 MEDIUM POC
5.3 Jan 08

pyLoad is the free and open-source Download Manager written in pure Python. Rated medium severity (CVSS 5.3), this vulne

CVE-2026-33017 CRITICAL POC
9.3 Mar 17

Langflow (a visual LLM pipeline builder) contains a critical unauthenticated code execution vulnerability (CVE-2026-3301

CVE-2026-55255 HIGH POC
8.4 Jun 19

Cross-user flow execution in Langflow (< 1.9.1) lets any authenticated API-key holder run another user's flow by passing

Share

CVE-2026-49353 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy