Threat intelligence, Threat Research

9Router Tailscale Install Endpoint Unauthenticated OS Command Injection

by Security News

9Router Tailscale Install Endpoint
OS Command Injection (CVE-2026-59800)

OVERVIEW

SonicWall Capture Labs threat research team became aware of the threat CVE-2026-59800, assessed its impact, and developed mitigation measures. The flaw, also known as the 9Router Tailscale Install Endpoint Unauthenticated OS Command Injection, is a critical vulnerability affecting the 9Router AI request proxy (decolua/9router, distributed on npm as 9router) in all versions up to and including 0.4.39, which NVD expresses as any version before 0.4.44. It lets an unauthenticated attacker execute operating system commands on the server by sending a single HTTP POST to the tunnel installation endpoint, because a JSON field intended to carry a sudo password is written as the first line of standard input to a privileged shell. Classified under CWE-78 (OS Command Injection) and CWE-862 (Missing Authorization), and rated CVSS 9.8 (Critical) by NVD, it was reported by vcth4nh and Ductinn through GitHub Security Advisory GHSA-g6g7-pvmx-m74p. Its EPSS score is 1.34% (68th percentile). The Shadowserver Foundation first observed exploitation in the wild on July 4, 2026. Version 0.4.44 adds the missing route pattern to the authorization middleware and stops piping the installation script through standard input, so administrators should upgrade and stop running the service as root.

TECHNICAL OVERVIEW

9Router is an open-source Node.js proxy that routes AI coding assistant traffic from clients such as Claude Code, Codex, Cursor, and Copilot across more than forty upstream model providers. It is built on the Next.js 16 App Router, ships a self-hosted web dashboard, and is distributed through npm, Docker Hub, and GHCR. Created in January 2026, it has accumulated roughly 25,700 GitHub stars. Because the product brokers credentials for commercial model providers, a compromised instance also exposes the operator's upstream API keys.

figure1.png
Figure 1: 9Router tailscale-install unauthenticated command injection flow to a root reverse shell

Two independent defects combine to produce the vulnerability. The first is a missing authorization check. 9Router protects its dashboard and sensitive API routes with Next.js middleware in src/proxy.js, which delegates to a guard in src/dashboardGuard.js. Next.js only invokes middleware for paths listed in the exported matcher array, and in version 0.4.39 that array covers the dashboard, settings, keys, CLI tools, and MCP routes, but never the /api/tunnel family. The guard even maintains a LOCAL_ONLY_PATHS list restricting routes that spawn child processes to loopback callers, but none of it runs here, because the matcher was never updated when the tunnel routes were added.

figure2.png
Figure 2: Middleware matcher in proxy.js omits the /api/tunnel path family

The consequence is directly observable. A request to a matched route such as /api/keys returns HTTP 401, but a POST to /api/tunnel/tailscale-install reaches its handler with no credential of any kind. That handler parses the request body and takes sudoPassword from it, falling back to a cached value when the field is absent, and the only validation applied is a test that the value is not empty. When the field is missing entirely the endpoint answers HTTP 400 with "Sudo password is required", which confirms to an unauthenticated caller that the handler ran and tells them which field to populate.

figure3.png
Figure 3: The POST handler accepts body.sudoPassword and only checks that it is non-empty

The second defect is the command sink. On Linux the handler calls installTailscale, which delegates to installTailscaleLinux in src/lib/tunnel/tailscale.js. That function first fetches the vendor installation script with curl, and only if the download succeeds does it spawn a privileged shell with spawn("sudo", ["-S", "sh"]). It then writes the attacker-controlled sudoPassword value as the first line of that shell's standard input, and writes the downloaded script body immediately afterwards.

figure4.png
Figure 4: installTailscaleLinux writes sudoPassword as the first stdin line to sudo -S sh

The design assumes that sudo -S will consume that first line as a password, an assumption that holds only while sudo actually prompts. Sudo reads standard input for a password solely when it needs to authenticate, so if the process already runs as root, if the invoking account holds a NOPASSWD entry, or if a recent authentication timestamp is cached, sudo does not prompt at all. It immediately executes sh, which reads the same pipe as its script, and the supposed password becomes the first command rather than a credential.

The fix in version 0.4.44 addresses both defects and adds defense in depth. The middleware matcher gains /api/tunnel/:path*, so the guard now runs; the guard adds the tunnel API to its authenticated path list and the install route to the localhost-only list; and the sink itself is rewritten. The patched installTailscaleLinux rejects any sudoPassword containing a newline, then writes the installer to a temporary file and invokes sudo -S sh <path>. Because the shell now takes its program from a file, nothing placed on standard input is interpreted as a command.

figure5.png
Figure 5: Version 0.4.44 wires the guard to /api/tunnel and runs install.sh from a temp file

TRIGGERING THE VULNERABILITY

The following conditions must be met for successful exploitation of CVE-2026-59800:

  • Network-Reachable Deployment: The target must run the 9Router server so that its HTTP API is reachable from the attacker. The service binds all interfaces by default and listens on TCP 20128, though some builds in the 0.4.x line bind 20129, and the traffic is plaintext HTTP unless a proxy adds TLS.
  • Vulnerable Version: The 9Router build must be 0.4.39 or earlier according to the vendor advisory, which NVD expresses as any version before 0.4.44. Note that npm never published 0.4.44, and the registry jumps from 0.4.41 to 0.4.45, so 0.4.45 is the first fixed release available from npm.
  • Linux Host: The vulnerable sink is in the Linux installation path. The macOS branch with Homebrew present and the Windows branch do not pipe the value into a shell in the same way.
  • Non-Prompting Sudo: The server process must run in a context where sudo does not ask for a password, meaning it runs as root, the account has a NOPASSWD sudoers entry, or a recent sudo authentication timestamp is still valid. The official Docker image runs as root and meets this condition by default. Where sudo does prompt, the injected value is consumed as a password and the attack silently fails.
  • Outbound Access to tailscale.com: The handler fetches the vendor installation script before it spawns the shell, and a failed download rejects the request before the sink is reached. A target with no egress to tailscale.com therefore does not execute the payload.

EXPLOITATION

Exploiting CVE-2026-59800 requires no credentials, no session, and no special tooling. The entire attack is a single HTTP POST to /api/tunnel/tailscale-install carrying a JSON body whose sudoPassword value is the command to run. Because the middleware never inspects the route, the request carries no authorization of any kind, and that absence is itself a useful detection signal. Appending ; exit 0 or & exit 0 to the payload terminates the shell before the genuine installation script arrives on the pipe, which both prevents Tailscale from actually installing and returns the HTTP response immediately rather than holding the connection open.

figure6.png
Figure 6: Single unauthenticated POST carrying the sudoPassword command injection payload

The endpoint replies with HTTP 200 and a text/event-stream body rather than JSON, emitting progress events as the handler works. A successful injection produces the sequence "Downloading install script...", then "Running install script...", then a done event, which is the same sequence a legitimate installation produces. A rejected empty-body probe instead returns HTTP 400 with a JSON content type, and a failed injection against a prompting sudo returns an error event carrying "Wrong sudo password". On a vulnerable instance running as root, the injected command executes with full root privilege, which is sufficient to establish an interactive reverse shell on the target host.

Video Demonstration

Key Payload Components
ComponentValuePurpose
Target EndpointPOST /api/tunnel/tailscale-installRoute absent from the middleware matcher, so no authorization runs
Transportplaintext HTTP on TCP 20128Default listener bound to all interfaces, 20129 in some builds
Injection ParametersudoPassword in the JSON bodyWritten as the first line of stdin to the privileged shell
Required HeadersContent-Type: application/json onlyNo cookie, bearer token, or x-9r-cli-token needed
Execution Primitivespawn("sudo", ["-S", "sh"])Non-prompting sudo executes sh, which reads the payload as a command
Terminator; exit 0 or & exit 0Ends the shell before install.sh arrives and frees the HTTP response
Server ResponseHTTP 200 with text/event-streamProgress events match a legitimate installation attempt
Resulting Privilegeroot on the target hostAlso exposes the upstream model provider API keys the proxy stores

SONICWALL PROTECTIONS

To ensure SonicWall customers are prepared for any exploitation that may occur due to this vulnerability, the following signature has been released:

Signature IDSignature Name
IPS: 224019Router tailscale-install OS Command Injection

REMEDIATION RECOMMENDATIONS

The risks posed by CVE-2026-59800 can be mitigated or eliminated with the following measures:

  • Upgrade 9Router: Move to 0.4.44 or later, or 0.4.45 or later from npm, where the authorization middleware covers the tunnel routes and the installer no longer runs from standard input. This is the direct fix and should be applied to every deployment.
  • Stop Running the Service as Root: Run 9Router under a dedicated unprivileged account with no NOPASSWD sudoers entry. Removing the non-prompting sudo condition breaks the exploit even on an unpatched build, and it limits the blast radius of any future flaw in the same code path.
  • Restrict Network Exposure: Firewall the 9Router listener port so the dashboard and API are reachable only from trusted administrative hosts, and place the service behind an authenticated reverse proxy with TLS rather than exposing it directly or through a public tunnel.
  • Rotate Upstream Provider Credentials: Treat the model provider API keys stored by any exposed instance as compromised, and reissue them after patching, because command execution on the host grants access to the credential store the proxy maintains.
  • Monitor for Exploitation Artifacts: Inspect access logs for POST /api/tunnel/tailscale-install requests, especially those carrying shell metacharacters or command tokens inside the sudoPassword value and arriving without any session cookie, and alert on unexpected outbound connections from the 9Router host shortly after such a request.
  • Deploy IPS Signatures: Apply updated signature coverage at the perimeter and on any segment that can reach the 9Router listener, and confirm that the sensor inspects HTTP on TCP 20128 and 20129, because a parser bound only to standard web ports will not evaluate this traffic.
  • Segment the Network: Isolate application servers from sensitive internal resources and implement egress filtering to detect unauthorized outbound connections.

RELEVANT LINKS

ATTRIBUTION

The vulnerability was reported by vcth4nh and Ductinn, and disclosed through the 9Router project as GitHub Security Advisory GHSA-g6g7-pvmx-m74p, published by the maintainer decolua on May 29, 2026. The fix shipped in 9Router 0.4.44.

Third-party vulnerability database mirrors:

Share This Article

An Article By

Security News

The SonicWall Capture Labs Threat Research Team gathers, analyzes and vets cross-vector threat information from the SonicWall Capture Threat network, consisting of global devices and resources, including more than 1 million security sensors in nearly 200 countries and territories. The research team identifies, analyzes, and mitigates critical vulnerabilities and malware daily through in-depth research, which drives protection for all SonicWall customers. In addition to safeguarding networks globally, the research team supports the larger threat intelligence community by releasing weekly deep technical analyses of the most critical threats to small businesses, providing critical knowledge that defenders need to protect their networks.

Related Articles

  • DbGate JSON Script Runner Unauthenticated Remote Code Execution
    Read More
  • rclone Remote-Control API Unauthenticated Command Execution
    Read More