CVE-2026-62312 Overview
CVE-2026-62312 is a command injection vulnerability in 9Router, an AI router and token saver. Versions prior to 0.5.2 allow a remote authenticated attacker to achieve arbitrary code execution on the host operating system. The flaw combines a Host header bypass of localhost-only routes with unvalidated Model Context Protocol (MCP) plugin arguments passed directly to child_process.spawn(). Malicious custom plugins can execute arbitrary operating system commands through the /api/mcp/<name>/sse endpoint. The issue is classified as [CWE-78] OS Command Injection and is fixed in version 0.5.2.
Critical Impact
Authenticated remote attackers can execute arbitrary commands on the host, resulting in full compromise of confidentiality, integrity, and availability.
Affected Products
- 9Router versions prior to 0.5.2
- MCP plugin routes exposed via /api/mcp/<name>/sse
- Deployments behind reverse proxies where loopback trust is misapplied
Discovery Timeline
- 2026-07-15 - CVE-2026-62312 published to NVD
- 2026-07-16 - Last updated in NVD database
Technical Details for CVE-2026-62312
Vulnerability Analysis
The vulnerability chains two defects. First, 9Router treats requests arriving on a loopback socket as trusted local traffic. When the application sits behind a reverse proxy, the proxy hop terminates on 127.0.0.1, so any remote request inherits local trust. This bypasses the Host header check meant to restrict sensitive routes to localhost.
Second, the MCP plugin subsystem forwards user-supplied plugin arguments to Node.js child_process.spawn() without validation or sanitization. An authenticated attacker who reaches /api/mcp/<name>/sse can register or invoke a custom plugin whose args array contains attacker-controlled commands. The combined effect converts an authenticated web request into arbitrary shell execution on the host.
Root Cause
The root cause is dual. The reverse-proxy trust boundary was implicit rather than explicit — the code assumed a loopback peer IP proved locality. The MCP plugin executor also lacked an allowlist or argument validator for spawned processes, violating the principle that data from HTTP requests must never flow directly into process arguments.
Attack Vector
An attacker authenticates to the 9Router dashboard, then sends a crafted request through the reverse proxy to a route protected only by localhost checks. Because the loopback socket is trusted, access is granted. The attacker then registers or invokes a custom MCP plugin whose spawn arguments include the desired command payload, and the server executes it.
// Security patch in custom-server.js
if (!handler) return origCreate(...args);
const wrapped = (req, res) => {
const ip = req.socket && req.socket.remoteAddress ? req.socket.remoteAddress : "";
+ // Forwarding headers present = request arrived via a reverse proxy; loopback
+ // socket is the proxy hop, not the end-user, so it must not be trusted as local.
+ const viaProxy = !!(req.headers["x-forwarded-for"] || req.headers["x-real-ip"]);
delete req.headers["x-9r-real-ip"];
delete req.headers["x-forwarded-for"];
+ delete req.headers["x-9r-via-proxy"];
req.headers["x-9r-real-ip"] = ip;
+ if (viaProxy) req.headers["x-9r-via-proxy"] = "1";
return handler(req, res);
};
return origCreate(...rest, wrapped);
Source: GitHub Commit da667836
// Security patch in src/dashboardGuard.js
export function isLocalRequest(request) {
+ // Stamped by custom-server.js when forwarding headers exist: request came through
+ // a reverse proxy, so the loopback socket is the proxy hop, not the end-user.
+ if (request.headers.get("x-9r-via-proxy")) return false;
// Trusted peer IP from TCP socket (custom-server.js); unspoofable. Primary anchor for "local".
const realIp = request.headers.get("x-9r-real-ip");
if (realIp) {
Source: GitHub Commit da667836
Detection Methods for CVE-2026-62312
Indicators of Compromise
- HTTP requests to /api/mcp/<name>/sse originating from external IPs while carrying x-forwarded-for or x-real-ip headers.
- MCP plugin registrations containing shell metacharacters, absolute binary paths, or command flags in the args field.
- Unexpected child processes spawned by the 9Router Node.js process (for example sh, bash, cmd.exe, powershell).
- Outbound network connections initiated by processes descending from the 9Router runtime.
Detection Strategies
- Correlate reverse proxy access logs against 9Router application logs to identify remote requests reaching localhost-only routes.
- Alert on any process ancestry where the 9Router Node.js runtime spawns interactive shells or download utilities such as curl, wget, or certutil.
- Inspect MCP plugin configuration files and API calls for arguments that deviate from expected binary paths or option patterns.
Monitoring Recommendations
- Enable verbose logging on the reverse proxy layer, including original client IP, request path, and Host header.
- Monitor for anomalous authenticated API traffic patterns targeting MCP endpoints.
- Baseline the expected child processes of the 9Router service and alert on deviations.
How to Mitigate CVE-2026-62312
Immediate Actions Required
- Upgrade 9Router to version 0.5.2 or later without delay.
- Audit existing MCP plugin definitions for suspicious args values and remove any unrecognized custom plugins.
- Rotate any credentials, API tokens, or secrets accessible to the 9Router host process.
- Review authentication logs to identify accounts that accessed /api/mcp/* routes prior to patching.
Patch Information
The fix is available in 9Router 0.5.2. See the GitHub Release v0.5.2 and the GitHub Security Advisory GHSA-63p9-g54h-prrp. The patch stamps requests arriving via a reverse proxy with an x-9r-via-proxy marker, and isLocalRequest() refuses to treat such requests as local.
Workarounds
- Restrict access to the 9Router dashboard and MCP endpoints using network-level controls, allowing only trusted management subnets.
- Disable custom MCP plugin registration if the feature is not required in the deployment.
- Terminate TLS at the reverse proxy and drop any client-supplied x-9r-via-proxy, x-9r-real-ip, and x-forwarded-for headers before forwarding.
- Run the 9Router process under a low-privilege service account with restricted filesystem and network egress.
# Configuration example - strip spoofable headers at the reverse proxy (nginx)
location / {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header x-9r-via-proxy "";
proxy_set_header x-9r-real-ip "";
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.

