Sep 22, 2026·7 min read·19 visits
A login lockout bypass in 9Router < 0.5.6 allows remote attackers to conduct unthrottled brute-force attacks against administrative credentials by spoofing the X-9r-Real-Ip HTTP header.
A rate limiting bypass vulnerability in 9Router versions before 0.5.6 allows unauthenticated remote attackers to circumvent the login progressive lockout mechanism. By manipulating the client-supplied X-9r-Real-Ip HTTP header, an attacker can rotate the tracking IP address, enabling unthrottled brute-force password guessing against the administrative interface.
9Router is an open-source AI router and token-saving proxy. To prevent brute-force attacks against its administrative control panel, the application integrates a rate-limiting and progressive lockout mechanism on its login endpoint. This mechanism tracks failed login attempts and locks out the corresponding client IP address after 5 consecutive failures.
The vulnerability, tracked as CVE-2026-56682 and GHSA-32gc-64m7-hj7v, exists because the login rate-limiting logic trusts client-supplied HTTP headers to identify the source of incoming requests. Specifically, the component relies on the X-9r-Real-Ip header to determine the remote client's identity without validating if the connection originates from a trusted reverse proxy.
Because this trust boundary is not properly enforced, an unauthenticated attacker can arbitrarily define this header value. By systematically rotating the value of X-9r-Real-Ip on each successive authentication request, the attacker can associate each attempt with a unique, virtual IP address. This bypasses the lockout mechanism entirely, allowing unthrottled dictionary attacks against the administrative credentials.
The core security flaw resides in the rate-limiting architecture implemented in src/lib/auth/loginLimiter.js. When a login request is made to POST /api/auth/login, the application attempts to retrieve the client's genuine IP address to track failed authentication attempts. The rate limiter is designed to track these failures in memory-based buckets mapped to the returned IP address.
To resolve the client IP address in reverse-proxy environments, the application retrieves the value of the X-9r-Real-Ip header. The system relies on a custom server wrapper (custom-server.js) to strip incoming proxy headers from untrusted connections and rewrite them based on the actual socket peer address. However, this implementation breaks down under two conditions.
First, if the Next.js application is deployed directly or accessed directly bypassing the custom-server.js wrapper (for example, by directly accessing the internal Next.js port 3000), the incoming HTTP headers are passed to the routing context completely unmodified. In this state, an attacker can directly inject and control the X-9r-Real-Ip header. Second, prior to version 0.5.6, the custom server's proxy validation checks were insufficient, trusting forwarding headers simply if they were present on the request, regardless of whether the physical TCP connection originated from a trusted local loopback address.
This structural trust flaw allows an attacker to manipulate the identifier used to resolve the rate-limiting bucket. Since the bucket tracking failure counts is keyed on this header value, rotating the IP address string on each request ensures that no individual tracking bucket ever reaches the threshold of 5 failed attempts required to trigger a progressive lockout.
An analysis of the fix in commit efd20be8d81ef2e256a7037f3aa78e6b567b5fd3 reveals how the trust boundary was re-established. The vulnerable wrapper implementation trusted any forwarding headers unconditionally. The patch introduces a strict Loopback Proxy Trust Model within custom-server.js.
The following code diff illustrates the exact changes made to resolve the validation gap:
// custom-server.js - Patch Analysis
const wrapped = (req, res) => {
// 1. Retrieve the actual physical remote IP address from the network socket
const socketIp = req.socket && req.socket.remoteAddress ? req.socket.remoteAddress : "";
const xff = req.headers["x-forwarded-for"];
const xRealIp = req.headers["x-real-ip"];
const viaProxy = !!(xff || xRealIp);
// 2. Validate if the TCP socket connection originates from a trusted local loopback interface
const isLoopbackProxy = socketIp === "127.0.0.1" || socketIp === "::1" || socketIp === "::ffff:127.0.0.1";
// 3. Resolve the proxy IP only if the connection is from localhost
const proxyIp = xRealIp || (xff ? String(xff).split(",")[0].trim() : "");
const ip = isLoopbackProxy && proxyIp ? proxyIp : socketIp;
// 4. Strip client-supplied tracking headers to prevent downstream contamination
delete req.headers["x-9r-real-ip"];
delete req.headers["x-forwarded-for"];
delete req.headers["x-9r-via-proxy"];
// 5. Explicitly set the sanitized IP address
req.headers["X-9r-Real-Ip"] = ip;
}The patched code ensures that forwarding headers are only parsed if the TCP peer address (socketIp) matches a validated loopback address (127.0.0.1, ::1, or the IPv4-mapped IPv6 representation ::ffff:127.0.0.1). If the request is received from an external network socket, the forwarding headers are completely ignored, and the actual socket connection IP is used as the client identifier. This prevents remote attackers from supplying arbitrary IP strings to rotate rate-limiting buckets.
Exploitation of this vulnerability requires network access to the 9Router administrative login endpoint. There are no prerequisite permissions or user roles required, as the bypass occurs during the initial authentication phase before any credentials are validated.
The attack vector can be visualized as a structured request pipeline where the client dynamically generates a random IPv4 address for the tracking header on each request. The following sequence diagram outlines this traffic flow:
Because the rate-limiting tracking is distributed across separate, distinct address keys, the login progressive lockout threshold is never reached for any single bucket. This allows the attacker to execute multi-threaded password brute-forcing operations until a valid credential matches, leading to a successful administrative session.
The security impact of CVE-2026-56682 is classified as Medium, with a CVSS v3.1 base score of 5.3. Although the vulnerability does not directly result in remote code execution (RCE) or immediate database compromise, it serves as a high-fidelity entry point for administrative takeover.
An attacker who successfully bypasses the lockout mechanism can conduct highly effective brute-force or dictionary attacks against the primary administrative login password. 9Router coordinates access to backend LLM providers and billing APIs; gaining access to the administrative dashboard grants the attacker control over configuration keys, token savings routes, and connected upstream API tokens.
Furthermore, the severity may escalate to High in deployments using default or weak passwords. In environments where the Next.js service is directly exposed on an unprotected public port, the mitigation provided in custom-server.js is rendered inactive, creating a persistent risk vector.
To fully mitigate the risks associated with CVE-2026-56682, administrators must deploy both software patches and structural network controls.
The primary remediation path is upgrading the 9Router deployment to version 0.5.6 or later. This release enforces the Loopback Proxy Trust Model, ignoring any forwarding headers received from non-loopback TCP sockets and stripping tracking headers from untrusted clients.
In scenarios where immediate patching is not feasible, the following workarounds should be applied:
127.0.0.1 or ::1). This prevents attackers from directly connecting to the Next.js application port (e.g., 3000) and bypassing the custom wrapper server.X-9r-Real-Ip, X-Forwarded-For, and X-Real-IP before forwarding traffic to the 9Router backend.CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
9Router decolua | < 0.5.6 | 0.5.6 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-307 / CWE-807 |
| Attack Vector | Network (Remote) |
| CVSS Score | 5.3 (Medium) |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
| Ransomware Use | No |
The application fails to properly enforce limits on how many times an entity can attempt to authenticate within a given timeframe.
CVE-2026-16454 is an object-level authorization bypass vulnerability (CWE-284, CWE-862) in Eclipse hawkBit versions 1.0.3 and prior. The flaw exists within the Direct Device Integration (DDI) REST controller (hawkbit-ddi-resource), where endpoints responsible for enumerating software module metadata and serving firmware binaries fail to verify whether the requested software module is assigned to the authenticated device. Consequently, any device with valid credentials (such as a TargetToken or GatewayToken) can enumerate software module identifiers and download arbitrary firmware artifacts across the entire tenant namespace.
CVE-2026-62284 (GHSA-5w5g-j6hw-6p8f) is a code injection and server-side request forgery vulnerability in the console-proxy component of the OSAC Operator. The RemoteConfigResolver parses Kubernetes Secrets containing kubeconfig files via clientcmd.RESTConfigFromKubeConfig without sanitizing execution-related fields. An attacker with RBAC permissions to create or modify labeled Secrets in monitored namespaces can supply a malicious exec credential plugin, resulting in arbitrary command execution within the console-proxy pod.
CVE-2026-62361 (GHSA-xgjr-7j9q-2h4r) is an authenticated SQL injection and authorization bypass vulnerability in listmonk, an open-source, self-hosted newsletter and mailing list manager written in Go. In versions prior to 6.2.0, the subscriber export endpoint (GET /api/subscribers/export) directly interpolates user-supplied SQL filter expressions into a PostgreSQL query without invoking the table allowlist validation function (validateQueryTables). Consequently, authenticated users possessing the subscribers:sql_query and subscribers:get_all permissions can bypass relational access controls, execute arbitrary subqueries against restricted tables such as users and settings, and modify database state via writable Common Table Expressions (CTEs).
CVE-2026-28385 (GHSA-3gq2-x4qg-p4g6) is a Server-Side Request Forgery (SSRF) vulnerability in Canonical LXD versions 4.12 through 6.9. An authenticated user possessing the can_create_images entitlement can force the LXD daemon to execute arbitrary outbound HTTP and HTTPS requests via the /1.0/images API endpoint. Due to the absence of egress destination validation, an attacker can probe host loopback services, RFC 1918 private network segments, and cloud Instance Metadata Services (IMDS) from the network position of the privileged LXD daemon.
Canonical LXD and LXC Incus contain an arbitrary file write vulnerability (CWE-73 / CWE-59) in the `/1.0/instances/<name>/exec` REST API endpoint. When an instance is unpacked from an image archive, top-level symbolic links are extracted directly onto the host filesystem within the instance storage directory. If a crafted image includes a symbolic link named `exec-output` pointing to an arbitrary host path, invoking the execution endpoint with `record-output: true` causes the privileged host daemon to follow the symlink and write command output files directly to the target host directory. An authenticated attacker with permissions to import images and execute commands can exploit this flaw to write controlled content to sensitive host directories such as `/etc/cron.d`, resulting in arbitrary code execution on the host as `root`.
CVE-2026-48749 (tracked as GHSA-vghh-5rfx-xhq8 for LXD and GHSA-2q3f-q5pq-g8wv for Incus) is a critical container escape vulnerability in the Canonical LXD and Linux Containers Incus system container managers. By importing a specially crafted container image archive containing duplicate tar member headers, an authenticated attacker can replace the instance rootfs directory with a symbolic link pointing to the underlying host root filesystem. When performing file operations on a stopped container, the privileged forkfile helper binary resolves the symbolic link without confinement and chroots directly into the host filesystem, granting arbitrary root-level file read and write access.