GHSA-7cfm-pqrj-xgq7 9router: Login brute-force protection bypass via spoofed X-Forwarded-For header
SummaryThe 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`:The returned value is used as the key for the in-memory rate-limit state:The login route uses this value when checking and recording failed login attempts: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.PoCStep 1 — Baseline: rate limiter triggers when the client identity is stableSend repeated failed login attempts with the same `X-Forwarded-For` value: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 requestSend failed login attempts while changing the `X-Forwarded-For` value for every request:Observed response for every request:The counter resets to the initial state on every request, and the lockout is never triggered.Step 3 — Impact amplifier: default dashboard passwordIf 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:Observed response on a default installation: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 ScenarioA remote attacker identifies a publicly reachable 9router dashboard.The attacker sends repeated login attempts to `/api/auth/login`.For each attempt, the attacker changes the `X-Forwarded-For` header value.9router treats each request as a different client and assigns a fresh rate-limit bucket.The attacker can continue brute-force attempts without triggering the configured lockout.If the instance uses a weak or default dashboard password, the attacker can gain administrative access.ImpactA 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.
CVSS v3.1 score: 7.3