Sep 2, 2026·6 min read·21 visits
The fast-uri package redundantly decodes host components twice across the parsing and recomposition lifecycle. Attackers can leverage nested percent-encoded host strings to bypass security boundary checks, leading to unauthenticated SSRF to internal network nodes and cloud metadata endpoints.
A double-decoding vulnerability in the fast-uri package allows unauthenticated remote attackers to bypass host-policy validation and conduct Server-Side Request Forgery (SSRF) attacks by submitting nested percent-encoded URI strings.
The high-performance, dependency-free URI parsing library fast-uri is heavily utilized within the Node.js ecosystem (such as in the Fastify framework) for routing, URL normalization, and address resolution. In modern microservice architectures, application gateways and reverse proxies frequently rely on URI parsers to inspect and validate destination hostnames before forwarding outbound network requests.
This vulnerability is characterized as an incomplete fix for a precursor flaw (CVE-2026-6322) and belongs to the double-decoding vulnerability class (CWE-174). Due to incorrect processing of percent-encoded hostnames, the library decodes the host component twice during its parsing and serialization lifecycle.
This behavior directly breaches RFC 3986, Section 2.4, which explicitly states that a URI processor must not decode the same string multiple times. The resulting parser differential permits attackers to craft obfuscated URIs that pass application-level validation blocklists but serialize into restricted destinations when handed to backend HTTP network clients.
The technical breakdown of the flaw involves two distinct phases inside the fast-uri lifecycle: parsing and recomposition.
In the initial parser phase, handled by the parseWithStatus function in index.js, the library processes the raw host string. It attempts to extract and normalize the hostname, applying the legacy global JavaScript function unescape() to decode percent-encoded sequences. If an input string contains double-encoded characters, such as %256c (where %25 decodes to the percent symbol % and 6c represents the letter l), the parser decodes only the outer sequence. The resulting intermediate host saved in memory is %6c.
When the application executes security validations against the parsed object, it inspects this intermediate string. Because %6c does not match restricted host literal strings like localhost or local IP segments, the validation logic flags the URL as safe. This mismatch constitutes a critical parser differential.
During the subsequent recomposition, normalization, or resolution phase in lib/utils.js, the recomposeAuthority function is invoked to format the URI for outgoing requests. Inside this function, fast-uri historically called unescape() on the stored hostname a second time. This second pass decodes %6c into the literal character l. When the completed URI is serialized and handed to a network client (such as standard Node.js http, undici, or axios), the client routes the request to the fully decoded, restricted host, bypassing the validation layer.
The maintainers patched the vulnerability across versions 2.4.5, 3.1.6, and 4.1.3. The key change replaces raw unescape() calls during parsing and recomposition with a safe helper function named normalizePercentEncoding.
In the parsing logic of index.js, the unescaping logic was modified as follows:
@@ -460,15 +468,15 @@ function parseWithStatus (uri, opts) {
if (parsed.scheme !== undefined) {
parsed.scheme = unescape(parsed.scheme)
}
if (parsed.host !== undefined) {
- parsed.host = reescapeHostDelimiters(unescape(parsed.host), isIP)
+ parsed.host = reescapeHostDelimiters(normalizePercentEncoding(parsed.host, true), isIP)
}
}Similarly, within the serialization phase inside lib/utils.js, the secondary unescape() call was replaced:
@@ -539,8 +548,8 @@ function recomposeAuthority (component) {
if (component.host !== undefined) {
- let host = unescape(component.host)
+ // Decode only unreserved bytes, once. In particular, keep %25 encoded so
+ // it cannot become the introducer for a second escape during recomposition.
+ let host = normalizePercentEncoding(component.host, true)
if (!isIPv4(host)) {The newly introduced normalizePercentEncoding helper solves the root cause by strictly limiting decoding to safe, unreserved ASCII characters (alphanumerics, hyphen, period, underscore, tilde) while explicitly preserving the percent sign representation %25 in its encoded form. By keeping %25 intact, nested percent-encoded sequences are prevented from becoming active percent characters during downstream serialization passes, neutralising the recursive decoding exploit path.
Exploitation of CVE-2026-75899 requires no authentication and can be executed via three main payload techniques targeting different internal infrastructure topologies.
The first vector targets loopback hostnames. An attacker targets an internal administrative portal by crafting a nested URL:
http://%256c%256f%2563%2561%256c%2568%256f%2573%2574/private-api
When parsed, the host is resolved to %6c%6f%63%61%6c%68%6f%73%74. If validation filters block string-exact matches for localhost, this check passes. Upon serialization, the application makes a request to localhost/private-api.
The second vector evades IP-based restriction lists by obfuscating separator characters. The IP address loopback 127.0.0.1 contains dot separator characters. Since the dot character evaluates to %2e when percent encoded, double-encoding results in %252e or %252E. The attacker submits the following payload:
//127%252e0%252e0%252e1/admin
The validation logic analyzes 127%2e0%2e0%2e1 as a benign remote string. The recomposition layer decodes this back into 127.0.0.1, initiating an unauthorized local network request.
The third vector targets cloud metadata endpoints to extract authentication credentials in AWS, Azure, or GCP environments. The cloud metadata IP address 169.254.169.254 is obfuscated via nested percent encoding:
//169%252E254%252E169%252E254/latest/meta-data/
The double-decoded request routes directly to the Link-Local Instance Metadata Service (IMDS), allowing the remote attacker to extract temporary cloud role credentials.
The impact of this vulnerability is critical for applications that process untrusted user-supplied URIs and implement internal security boundaries. A successful exploitation allows complete bypass of network restrictions, enabling Server-Side Request Forgery (SSRF).
If the host application executes with elevated network privileges or resides inside an isolated virtual private cloud (VPC), the attacker can access sensitive local systems. This includes internal databases, microservice administration endpoints, key-value stores, and credential configuration dashboards.
The CVSS v3.1 score of 7.5 reflects high impact on integrity. Because fast-uri does not directly execute code itself, the confidentiality and availability scores are technically defined as none within the scope of this isolated library. However, the downstream architectural impact of SSRF can result in complete system compromise or data exfiltration depending on the capabilities of the reached internal endpoints.
The primary remediation step is upgrading the fast-uri dependency to a secure, patched version. The specific secure versions correspond to the version branch maintained by the project.
For projects utilizing the 4.x branch, upgrade to 4.1.3 or later:
npm install fast-uri@4.1.3
For projects utilizing the 3.x branch, upgrade to 3.1.6 or later:
npm install fast-uri@3.1.6
For projects utilizing the 2.x branch, upgrade to 2.4.5 or later:
npm install fast-uri@2.4.5
In scenarios where immediate patching is unfeasible, implement a temporary input validation wrapper that rejects incoming URLs containing percent signs (%) in the host block. This effectively blocks nested percent encodings and mitigates double-decoding attempts before the string is passed to fast-uri parsing functions.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
fast-uri Fastify / OpenJS Foundation | >= 2.4.1 < 2.4.5 | 2.4.5 |
fast-uri Fastify / OpenJS Foundation | >= 3.1.2 < 3.1.6 | 3.1.6 |
fast-uri Fastify / OpenJS Foundation | >= 4.0.0 < 4.1.3 | 4.1.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-174 / CWE-918 |
| Attack Vector | Network (AV:N) |
| CVSS v3.1 Score | 7.5 (High) |
| EPSS Score | 0.00234 |
| Impact Type | Integrity (SSRF / Host Bypass) |
| Exploit Status | poc |
| CISA KEV Status | Not Listed |
The application decodes the same input data multiple times, which can allow malicious input to bypass validation checks that only look at the first level of decoding.
Open WebUI from version 0.9.0 to 0.11.1 is vulnerable to a state desynchronization and privilege persistence flaw. When an administrator is demoted to a standard user via Single Sign-On (SSO) role synchronization, the local database is updated, but their active Socket.IO connection is not invalidated. Because the WebSocket handlers authorize operations using the cached role in the socket context, the demoted user retains administrative read and write access to all collaborative notes.
An authenticated denial of service vulnerability exists in Open WebUI versions 0.10.0 through 0.11.0. An attacker can update a folder's parent identifier to establish cyclic folder references, causing recursive tree-walking operations to execute infinitely, leading to CPU exhaustion and localized application denial of service.
Open WebUI is a self-hosted AI platform. Versions 0.9.0 through 0.11.0 contain a denial-of-service vulnerability where an authenticated user can inject non-numeric values into calendar event alert metadata. The shared scheduler process fails to validate the type, leading to an unhandled TypeError that halts the execution of instance-wide alerts, suppressing notifications for all users.
CVE-2026-87011 is a critical vulnerability in Open WebUI versions 0.9.0 through 0.11.0. It allows unauthenticated remote attackers to trigger a Denial of Service (DoS) by sending crafted tokens to the back-channel logout endpoint, causing synchronous network calls that block the single-worker ASGI event loop.
A high-severity Denial of Service (DoS) vulnerability in the S3 compatibility layer of rclone allows unauthenticated remote attackers (or authenticated attackers depending on configuration) to trigger rapid memory exhaustion and process termination. The flaw lies in the handling of S3 multipart uploads, where rclone eagerly allocates buffers based on untrusted size headers and fails to prevent integer overflows in its concurrent request admission control.
CVE-2026-88046 (also tracked via GHSA-38xv-hf3p-h7mq) is a directory traversal and root confinement escape vulnerability residing in the core listing and transfer logic of rclone. Prior to version 1.75.1, raw relative parent-directory sequences returned by flat-keyspace source backends are trusted and processed without proper sanitization, enabling writes outside the designated target root or bucket.