{
  "variant_id": "9router-cve-2026-49352-variant-default-password",
  "created_at": "2026-07-03T14:42:00Z",
  "variant_summary": "Bypass of the CVE-2026-49352 JWT-secret fix via a sibling hardcoded default auth credential: the dashboard login password '123456' (INITIAL_PASSWORD fallback) in src/app/api/auth/login/route.js. On a fresh install (no saved password hash) an unauthenticated remote attacker POSTs {\"password\":\"123456\"} to /api/auth/login, receives a valid auth_token cookie, and gains full /dashboard + protected-API access. Confirmed on the JWT-fixed v0.4.44 (bypass of the fix) and on the latest v0.4.80 (its 'remote default-password guard' is UI-only and bypassed by direct cookie use).",
  "relation": "newer_version_sibling",
  "origin_kind": "pruva_variant",
  "repository": "https://github.com/decolua/9router",
  "submitted_target": {
    "target_kind": "git_tag",
    "commit_sha": "cebc72e343dca5aad69b2828cb0d0f2e54b168d",
    "version": "0.4.41",
    "ref": "v0.4.41",
    "display": "decolua/9router @ v0.4.41 (cebc72e34) — vulnerable to CVE-2026-49352 (hardcoded JWT secret)"
  },
  "variant_target": {
    "target_kind": "git_tag",
    "commit_sha": "9e87935c0e53f46d6ae04fbec656fc4d971547d7",
    "version": "0.4.44",
    "ref": "v0.4.44",
    "display": "decolua/9router @ v0.4.44 (9e87935c0) — JWT-secret fixed; variant (default-password bypass) CONFIRMED here"
  },
  "same_root_cause_confidence": "high",
  "same_surface_confidence": "high",
  "claimed_surface": "api_remote",
  "validated_surface": "api_remote",
  "required_entrypoint_kind": "api_remote",
  "required_entrypoint_detail": "HTTP POST /api/auth/login with JSON {\"password\":\"123456\"} against a real 9router Next.js server on a fresh install (no saved password hash, JWT_SECRET unset); then reuse the issued auth_token cookie on GET /dashboard and protected GET /api/*.",
  "attacker_controlled_input": "POST body {\"password\":\"123456\"} using the hardcoded INITIAL_PASSWORD fallback literal; for the latest version a non-loopback Host header simulates a remote client and the issued cookie is used directly (bypassing the UI-only mustChangePassword guard).",
  "trigger_path": "POST /api/auth/login -> src/app/api/auth/login/route.js POST() : initialPassword = process.env.INITIAL_PASSWORD || '123456'; isValid = password === initialPassword (no storedHash) -> setDashboardAuthCookie() issues valid auth_token JWT -> src/dashboardGuard.js proxy() verifyDashboardAuthToken(cookie) accepts -> NextResponse.next() (200) for /dashboard and PROTECTED_API_PATHS.",
  "observed_impact_class": "authz_bypass",
  "exploitability_confidence": "high",
  "evidence_scope": "production_path",
  "runtime_manifest_present": true,
  "end_to_end_target_reached": true,
  "inferred": false,
  "file_path": "src/app/api/auth/login/route.js",
  "line_start": 37,
  "line_end": 38,
  "secondary_anchors": [
    {
      "file_path": "src/app/api/auth/login/route.js",
      "line_start": 43,
      "line_end": 43
    },
    {
      "file_path": "src/dashboardGuard.js",
      "line_start": 68,
      "line_end": 69
    },
    {
      "file_path": "src/lib/auth/dashboardSession.js",
      "line_start": 7,
      "line_end": 19
    },
    {
      "file_path": "src/app/api/auth/login/route.js",
      "line_start": 61,
      "line_end": 64
    },
    {
      "file_path": "src/app/login/page.js",
      "line_start": 78,
      "line_end": 78
    }
  ],
  "review_scope_paths": [
    "src/app/api/auth/login/route.js",
    "src/dashboardGuard.js",
    "src/lib/auth/dashboardSession.js",
    "src/app/login/page.js",
    "src/proxy.js"
  ],
  "artifact_refs": {
    "variant_manifest": "bundle/vuln_variant/variant_manifest.json",
    "validation_verdict": "bundle/vuln_variant/validation_verdict.json",
    "runtime_manifest": "bundle/vuln_variant/runtime_manifest.json",
    "repro_log": "bundle/logs/vuln_variant/reproduction_steps.log",
    "root_cause_equivalence": "bundle/vuln_variant/root_cause_equivalence.json",
    "reproducer": [
      "bundle/vuln_variant/reproduction_steps.sh"
    ]
  },
  "notes": "Bypass confirmed on the JWT-fixed target v0.4.44 (commit 9e87935c0). Also confirmed on the latest v0.4.80 (commit 515e2cc4300ace55650ae366414cd51ef3d675df): the v0.4.80 'remote default-password guard' sets mustChangePassword only as a UI hint (consumed solely by src/app/login/page.js line 78); the dashboardGuard middleware never enforces it, so using the issued auth_token cookie directly yields 200 on /dashboard and /api/keys for a simulated remote client. Precondition: fresh install with no saved password hash (default npx/docker state). The login does not auto-save a hash, so the default password persists until a manual change. See source_identity.json for exact tested commits."
}
