Aug 27, 2026·7 min read·33 visits
A parser differential between WebOb and Python's urljoin on modern runtimes allows unauthenticated attackers to bypass open redirect protections using leading spaces or control characters.
An open redirect vulnerability exists in WebOb before version 1.8.11 due to a parser differential between WebOb's validation logic and Python's standard urllib.parse.urljoin() function. Under Python 3.10+, the urljoin function strips leading and trailing space characters and C0 control characters, which allowed specially crafted inputs to bypass WebOb's prefix checks while still resolving as off-host redirects.
WebOb is an essential Python library providing wrappers for WSGI request and response objects, serving as a foundational dependency for web frameworks like Pyramid and various Pylons-based applications. Due to its role in parsing HTTP parameters and constructing HTTP response headers, WebOb is positioned at a critical junction of the application's attack surface. Specifically, components responsible for generating redirections, such as the Location header handler and redirection exception classes, rely on parsing and resolving URLs safely.
CVE-2026-54770 represents a recurring design vulnerability in WebOb's validation logic, categorized under CWE-601 (URL Redirection to Untrusted Site). When applications accept user-supplied URLs to redirect users after specific actions, they must ensure that the destination remains within the boundaries of the trusted host. If an attacker can manipulate the destination to point to an arbitrary external domain, the application becomes an open redirector.
This flaw arises from a parser differential between WebOb's validation routines and the underlying URL parsing library in Python 3.10+. Because these two systems interpret special characters differently, validation checks can be bypassed, resulting in unauthenticated remote users redirecting victims to arbitrary external domains. The scope of this vulnerability extends beyond simple phishing, potentially facilitating OAuth flow exploits and token theft in single-sign-on (SSO) environments.
The root cause of CVE-2026-54770 lies in a parser differential between WebOb's preliminary sanitization checks and the standard behavior of urllib.parse.urljoin() in Python 3.10 and above. To prevent protocol-relative redirects (e.g., //evil.com), WebOb attempts to detect if a redirection path begins with double slashes. If detected, the prefix is neutralized to prevent user agents from interpreting the target as an off-host authority.
However, in modern Python runtimes (3.10+), the standard library's urllib.parse.urljoin() has been updated to align with the WHATWG URL specification. This specification dictates that a parser must strip leading and trailing C0 control characters (ASCII 0x00 through 0x1F) as well as the standard space character (ASCII 0x20) before performing reference resolution. Additionally, tabs and newline characters are removed from anywhere within the URL string.
This creates a classic validation-execution gap. When an attacker prefixes a protocol-relative URL with a space or a control character, such as " //evil.com", WebOb's validation logic executes first on the raw string. Since the string starts with a space rather than "//", the neutralization routine is bypassed. Subsequently, the string is passed to urllib.parse.urljoin(), which strips the leading space, leaving a valid protocol-relative URL. The library then resolves this relative URL against the local origin, producing an absolute redirect to http://evil.com.
To address this persistent validation gap, WebOb completely refactored its URL resolution mechanism in version 1.8.11. The maintainers determined that standardizing on Python's urllib.parse.urljoin() was fundamentally unsafe because the standard library's parsing rules are coupled with runtime-dependent behaviors and browser-emulation stripping routines.
Instead of patching around individual characters, WebOb implemented a custom, strict RFC 3986-compliant reference resolution parser in src/webob/util.py. The new function, _split_uri_reference, parses URLs without stripping leading or trailing spaces or C0 control characters, ensuring that validation and execution parse the exact same string:
def _split_uri_reference(uri):
# Split a URI reference into its five components.
# Unlike urllib.parse.urlsplit(), no characters are ever removed
# from the reference: ASCII tab/CR/LF and leading or trailing C0
# control and space characters are treated like any other character.
scheme = authority = query = fragment = None
rest, sep, token = uri.partition("#")
if sep:
fragment = token
rest, sep, token = rest.partition("?")
if sep:
query = token
token, sep, candidate = rest.partition(":")
if sep and _URI_SCHEME_RE.match(token):
scheme = token
rest = candidate
if rest.startswith("//"):
end = rest.find("/", 2)
if end == -1:
authority, rest = rest[2:], ""
else:
authority, rest = rest[2:end], rest[end:]
return scheme, authority, rest, query, fragmentAdditionally, the patch consolidated the redirection paths within webob.exc._HTTPMove (which covers classes like HTTPFound). Historically, these exceptions directly invoked urljoin() without routing through the safe _make_location_absolute() path. The updated logic forces all redirections to use the strict validation routine, blocking variants of the bypass.
Exploitation of CVE-2026-54770 requires no authentication and can be performed remotely via standard HTTP requests. The primary prerequisite is that the target application must expose an endpoint that dynamically redirects users based on input parameter values, such as a "next" or "return_to" parameter.
An attacker crafts a URL targeting this endpoint, injecting a payload that includes a leading space or control character. For instance, using the URL-encoded representation of a space (%20), the parameter is defined as "%20//attacker-controlled.com". When the backend application initiates the redirection exception, WebOb handles the path resolution.
When the victim accesses the crafted link and performs the expected action, their browser receives a 302 Found response containing a Location header pointing directly to the malicious domain. Because the initial domain in the link was trusted, the victim has no indication of the impending redirection, making this an extremely effective vector for credential harvesting.
The security impact of CVE-2026-54770 is primarily rated as medium, with a CVSS v3.1 score of 6.1. Although the vulnerability does not allow remote code execution or direct data compromise on the server, its role as an initial access vector is significant.
Open redirects are frequently chained with other vulnerabilities to bypass security controls. In OAuth 2.0 and OpenID Connect (OIDC) implementations, client applications register trusted redirect URIs. If a registered URI is vulnerable to an open redirect, an attacker can construct a flow that diverts the authorization code or implicit token to an external server. This results in complete account takeovers on the target application.
Furthermore, because the redirect originates from a trusted host, security filters, email scanners, and users are highly likely to trust the initial link. This trust is exploited during spearphishing campaigns to deliver malware or present highly convincing spoofed login interfaces to targets, directly impacting the integrity of user credentials.
The definitive remediation for CVE-2026-54770 is to upgrade the WebOb library to version 1.8.11 or later. This version completely replaces the vulnerable parsing dependencies with a strict, consistent internal parser that removes the parser differential.
If patching is not immediately feasible due to legacy system constraints, organizations can implement defense-in-depth measures at the application layer or via Web Application Firewalls (WAFs). A custom middleware or WAF rule can be applied to inspect all redirect targets and block strings containing leading spaces or C0 control characters. For example, the following regular expression can be used to identify anomalous redirection patterns:
^\s+[\/\\]{2,}
Additionally, developers should avoid blindly passing user input to redirection mechanisms. Implementing an allowlist of permitted destination domains or path prefixes is the most robust application-layer defense. If only local redirects are allowed, the application should strictly enforce that the path begins with a single slash "/" and is not followed by a second slash, space, or control character.
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:C/C:L/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
WebOb Pylons Project | < 1.8.11 | 1.8.11 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-601 |
| Attack Vector | Network |
| CVSS v3.1 | 6.1 (Medium) |
| EPSS Score | 0.00339 (0.34%) |
| Exploit Status | Proof-of-Concept |
| KEV Status | Not Listed |
| Impact | Open Redirect / Phishing |
A web application accepts a user-controlled input that specifies a link to an external site, and uses that input in a redirect. This simplifies phishing attacks or facilitates credential theft.
A heap-based buffer overflow vulnerability in Pillow PyPI binary wheels (versions 11.1.0 to 12.x) allows remote code execution or application crash via crafted JPEG 2000 files processed by the bundled libopenjp2 library.
CVE-2026-109618 (dubbed PHP2Shell) is a critical vulnerability in the PHP engine core resulting from improper out-of-memory exception handling via longjmp(). When request allocation fails mid-processing, non-local control flow bypasses cleanup procedures, leaving memory structures uninitialized or in a use-after-free state during request shutdown.
CVE-2026-109619 (PHP2Shell) is a critical Use-After-Free vulnerability in the core PHP engine caused by improper global state cleanup during out-of-memory exception handling across persistent worker requests.
In Python virtualenv prior to version 21.14.9, activation script generators and configuration writers use Path.exists() before writing files. Because Path.exists() resolves symbolic links, it returns False for dangling symlinks. Consequently, virtualenv skips unlinking pre-planted symbolic links or hard links, writing data directly to target locations outside the intended virtual environment directory.
A remote code execution vulnerability exists in Microsoft .NET due to untrusted data deserialization within the PresentationCore component. The flaw stems from implicit auto-conversion flags during clipboard data retrieval operations.
An out-of-bounds write vulnerability in Microsoft .NET Framework, .NET runtime, and Visual Studio allows unauthenticated remote attackers to execute arbitrary code via crafted pixel-shader bytecode or auto-converted clipboard payloads.