GHSA-g6g7-pvmx-m74p: 9router: Missing Authorization and OS Command Injection
Unauthenticated RCE via /api/tunnel/tailscale-install
Affected: 9router (npm package) — current master (v0.4.39).
Summary
POST /api/tunnel/tailscale-install accepts a JSON body with a sudoPassword field and pipes it, followed by the body of https://tailscale.com/install.sh, into a child process spawned as sudo -S sh. The route is not present in the dashboard middleware matcher in src/proxy.js, so the request reaches the handler without invoking dashboardGuard.proxy(). In deployments where the Node process runs as root (Docker images derived from node:* without a USER directive, npm i -g 9router invoked as root, or systemd units without User=), the spawned sh runs as root and executes the attacker-supplied bytes.
Details
1. Middleware matcher (src/proxy.js:3-15)
export const config = {
matcher: [
"/",
"/dashboard/:path*",
"/api/shutdown",
"/api/settings/:path*",
"/api/keys",
"/api/keys/:path*",
"/api/providers/client",
"/api/provider-nodes/validate",
"/api/cli-tools/:path*",
"/api/mcp/:path*",
],
};
Next.js invokes the middleware only for paths matching this list. Routes that are not listed — including the entire /api/tunnel/* family — do not invoke dashboardGuard.proxy(). No cookie, JWT, CLI token, or Host-header check is applied to them.
2. Route handler (src/app/api/tunnel/tailscale-install/route.js:18-67)
export async function POST(request) {
const body = await request.json().catch(() => ({}));
...
const sudoPassword =
body.sudoPassword || getCachedPassword() || await loadEncryptedPassword() || "";
...
const result = await installTailscale(sudoPassword, shortId, (msg) => {
send("progress", { message: msg });
});
...
}
body.sudoPassword comes from the request body and is passed to installTailscale, which dispatches to installTailscaleLinux on Linux.
3. Linux installation routine (src/lib/tunnel/tailscale.js:304-341)
async function installTailscaleLinux(sudoPassword, log) {
log("Downloading install script...");
return new Promise((resolve, reject) => {
Details
Original advisory: https://github.com/advisories/GHSA-g6g7-pvmx-m74p
Exploitation outlook
EPSS (FIRST.org) estimates each CVE’s probability of exploitation in the next 30 days — here is the CSIRTS.com read on those numbers.
- Moderate exploitation riskCVE-2026-598001.4% 30-day exploitation probability. Patch within normal cadence, watch for KEV listing. Riskier than 69% of all scored CVEs.
Referenced CVEs
| CVE | CSIRTS overview | External |
|---|---|---|
| CVE-2026-59800 | coverage & exploitation status | NVD · CVE.org |
Same CVEs, other sources
How other CERTs, PSIRTs and databases cover the vulnerabilities in this advisory.
More from GitHub Security Advisories
- criticalGHSA-g936-7jqj-mwv8: TSDProxy: Internal proxy auth token forwarded to backend services enables management API …2026-07-10
- highGHSA-fpg8-7664-jc5q: melange: Incomplete package integrity verification allows data section substitution2026-07-10
- mediumGHSA-48rx-c7pg-q66r: Excon does not redact additional sensitive/risky headers when following redirects2026-07-10
- highGHSA-h4g2-xfmw-q2c9: Clauster: Non-loopback deployments can serve the dashboard unauthenticated when auth.enab…2026-07-10
- mediumGHSA-rqq5-2gf9-4w4q: Secure Headers: CSP directive injection via sandbox, plugin_types, and report_to when giv…2026-07-10