The vulnerability bug chain consists of a verification bypass, an internal redirect, and an open redirect affecting PyPI (web service) and WebOb. This series of vulnerabilities undermines the trustworthiness of verified URLs and allows for the execution of unauthorized redirects.
Medium - The individual bugs allow an attacker to craft URLs that are successfully verified by trusted publishers. By undermining the verification of URLs, attackers can launch highly plausible phishing attacks with legitimate-looking lures.
To reproduce this vulnerability, an attacker chains three distinct behaviors to create a malicious, yet "verified", Project URL on PyPI.
Step 1: The URL Verification Bypass (PyPI)
PyPI automatically considers any self-referential link (e.g., https://pypi.org/project/myproject/) as a verified link. During upload, PyPI uses the rfc3986 package to parse and normalize URLs. However, most browsers (Chrome, Firefox, Edge) follow the WHATWG URL standard, which tolerates backslashes \ and treats them as / for schemes like http and https.
By crafting a URL with backslashes, an attacker can trick rfc3986 into seeing a safe, self-referential path, while the victim's browser will resolve a path traversal.
For example: https://pypi.org/project/verified/..\x/../unverified\x/../
rfc3986 sees: https://pypi.org/project/verified/\ as /, so it interprets ..\x as a literal folder name. When followed by /../, it traverses back up, effectively erasing the malicious segments and returning the base self-referential URL).https://pypi.org/project/unverified/\ as /, so ..\x/../ is interpreted as ../x/../. This navigates up to /project/, down to /project/x/, up to /project/, and down to /project/unverified/).Step 2: The Internal Redirect Bridge (PyPI)
To trigger an open redirect, the attacker needs to reach an endpoint that accepts a query string (like /account/logout/?next=...), but the verification bypass does not allow query strings.
To bridge this gap, the attacker targets the permissive internal redirect route /p/{name}/{version}/. Because this route matches [^/]+ and unquotes percent-encoding, the attacker can pack the query string into the {name} and {version} path segments using URL encoding.
Step 3: The Open Redirect (WebOb / PyPI)
PyPI's logout page reads the next parameter and validates it using is_safe_url(). PyPI attempts to block control characters at the start of a URL, but because the crafted payload begins with a forward slash (e.g., /%09/evil.com/), it bypasses this check. urllib3.util.parse_url sees no scheme or host, so is_safe_url() incorrectly returns True.
Pyramid then uses the webob.Response class to execute the redirect. WebOb contains an existing protection (from CVE-2024-42353) that checks if the location startswith("//"). Because our payload starts with /\t/, it bypasses this check and is passed directly to urllib.parse.urljoin(base, url). In Python 3.10+, urlsplit() strips ASCII newline and tab characters entirely. Thus, urljoin("https://pypi.org/", "/\t/evil.com/") evaluates effectively as urljoin("https://pypi.org/", "//evil.com/"). This turns the relative path into a scheme-relative absolute URL, replacing the base URL entirely and returning https://evil.com/.
Actionable Reproduction:
cblinktest).https://pypi.org/p/cblinktest/..\x/../..%5Caccount%5Clogout%5C%3Fnext=\%2509%252Fgithub.com%5Cpypi%5Cwarehouse\x/..Resolution flow when clicked:
https://pypi.org/p/..%5Caccount%5Clogout%5C%3Fnext=/%2509%252Fgithub.com%5Cpypi%5Cwarehouse//p/ redirect router unquotes the percent-encoding and responds with:Location: https://pypi.org/project/..\account\logout\?next=/%09%2Fgithub.com\pypi\warehouse/project/../), issuing a GET request to:https://pypi.org/account/logout/?next=/%09%2Fgithub.com\pypi\warehouse/%09 (tab) and redirects the user to the attacker-controlled destination (github.com\pypi\warehouse, which the browser again treats as / so github.com/pypi/warehouse).This vulnerability has been confirmed in Firefox, Chrome, and Edge. Safari appears to not be vulnerable.
The issues affected the PyPI web service and WebOb. The maintainers of both projects have addressed the issues:
1.8.10 (tracked under GHSA-fh3h-vg37-cc95) which addresses the incomplete patch of CVE-2024-42353.Date reported: April 1, 2026
Date fixed: May 20, 2026 (PyPI), June 3, 2026 (WebOb 1.8.10)
Date disclosed: July 3, 2026