CSIRTS // UNIFIED SECURITY ADVISORY FEEDSYS ● ONLINE · POWERED BY INTELFUSIONS.COM

GHSA-7cfm-pqrj-xgq7: 9router: Login brute-force protection bypass via spoofed X-Forwarded-For header

highCVSS 7.3CVE-2026-55501
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: 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: const attempts = new Map(); // ip -> { fails, lockUntil, lockLevel, lastFailAt } The login route uses this value when checking and recording failed login attempts: 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 lim

Details

Source
GitHub Security Advisories (INTL · database · site)
Severity
high — CVSS 7.3
Published
2026-07-06
Last updated
2026-07-06
Exploitation
Not in CISA KEV at last sync

Original advisory: https://github.com/advisories/GHSA-7cfm-pqrj-xgq7

Referenced CVEs

CVECSIRTS overviewExternal
CVE-2026-55501coverage & exploitation statusNVD · CVE.org

Same CVEs, other sources

How other CERTs, PSIRTs and databases cover the vulnerabilities in this advisory.

More from GitHub Security Advisories