WebOb: Location header normalization during redirect leads to open redirect - againGHSA-fh3h-vg37-cc95
MEDIUMFix: Pylons/webob@1f681a4GHSA-fh3h-vg37-cc95 is a medium-severity (CVSS 6.1) Open Redirect vulnerability in webob. A fix is available for webob — see the affected versions and patch details below.
Exploitation Status
No confirmed exploitation observed yet
- CISA’s own triage has not observed active exploitation or public proof-of-concept code for this CVE as of its last assessment.
Exploitation and automatability from CISA’s SSVC triage for GHSA-fh3h-vg37-cc95.
EPSS Exploitation Probability
Probability of exploitation in the next 30 days, from FIRST.org EPSS.
How urgent is this, really
GHSA-fh3h-vg37-cc95 by exploitation likelihood (EPSS) against impact (CVSS). Outside the shaded patch-first corner.
Where this sits among everything scored
Of 390,400 CVEs with a current EPSS score, this one falls in the < 10% band (highlighted). Counts from FIRST.org, log-scaled.
Real-World Exposure
webobReal-time download stats are indexed for npm and PyPI packages. This vulnerability affects PyPI packages — download data is not available via public APIs for these ecosystems.
Description
Impact
When WebOb normalizes the HTTP Location header to include the request hostname, it does so by parsing the URL that the user is to be redirected to with Python's urllib.parse, and joining it to the base URL. urlsplit (called internally by urljoin) however treats a // at the start of a string as a URI without a scheme, and then treats the next part as the hostname. urljoin will then use that hostname from the second part as the hostname replacing the original one from the request.
In a previous advisory https://github.com/Pylons/webob/security/advisories/GHSA-mg3v-6m49-jhp3 an attempt to fix this was made by forcing the replacement of // with /%2f, however this did not take into account that since Python 3.10 urlsplit internally strips ASCII tab, carriage return, and newline characters from the string, so /\t/attacker.com gets turned into //attacker.com and the attacker is able to bypass the changes introduced in that previous advisory, thereby bringing back the problem that was attempted to be fixed.
>>> parse.urlparse("//attacker.com/some/path")
ParseResult(scheme='', netloc='attacker.com', path='/some/path', params='', query='', fragment='')
WebOb uses urljoin to take the request URI and join the redirect location to it, so assuming the request URI is https://example.org/ and the URL to redirect to is /\t/attacker.com/some/path/:
>>> parse.urljoin("https://example.org/", "/\t/attacker.com/some/path/")
'https://attacker.com/some/path/'
Which redirects from example.org where we want the user to stay to attacker.com.
Patches
This issue has been fixed in WebOb 1.8.10.
Workarounds
Any use of the Response class that includes a location can be rewritten to make sure to always pass a full URI that includes the hostname to redirect the user to, or to validate that the redirect target starts with a scheme (e.g. http:// or https://) before assigning to Response.location.
References
Thanks
- Caleb Brown of Google
Affected Packages
| Ecosystem | Package | Vulnerable range | Fix |
|---|---|---|---|
| 🐍PyPI | webob | all versions | 1.8.10pip install --upgrade 'webob==1.8.10' |
Affected Products
webobpylonsprojectDetection & mitigation playbook
Open-source dependencyDetect
Scan your dependency tree (package-lock.json, pnpm-lock.yaml, requirements.txt, go.sum, etc.) for webob, including transitive dependencies — a direct dependency you never call can still pull in a vulnerable version.
Fix
Update webob to 1.8.10 or later, then make sure no transitive (indirect) dependency still pins the vulnerable range — O3 confirms GHSA-fh3h-vg37-cc95 is resolved across your whole dependency graph.
Workarounds
Stop reflecting attacker-controlled destinations: resolve every redirect target against an allowlist of paths or hosts you own, prefer a server-side key over a full URL in the request, and reject absolute URLs entirely where the flow only ever needs a relative one.
Fixing This On Your OS
If you run this on a Linux distribution, patch through your package manager against the distro's own security advisory below — it tracks the exact backported fix for your release, which can ship on a different timeline (and sometimes a different severity) than the upstream project.
This is rated as Moderate (CVSS 6.1) because exploitation requires user interaction — a victim must click a crafted link that triggers the redirect (UI:R). While the attack is network-accessible (AV:N) and requires no privileges (PR:N), the impact is limited to low confidentiality and integrity effects typical of an…
This vulnerability in WebOb is only exploitable when running under Python 3.10 or later, where urllib.parse.urlsplit strips ASCII tab, carriage return, and newline characters before parsing. Red Hat Enterprise Linux, OpenShift Container Platform, Red Hat OpenStack Platform, Red Hat Ceph Storage, and Red Hat Quay all ship python-webob with Python 3.9 or earlier as the platform Python runtime, and are therefore not affected by this vulnerability. Fedora and EPEL ship python-webob under Python 3.12+, where the vulnerability is exploitable. Users running WebOb on Python 3.10+ should upgrade to WebOb 1.8.10. As a workaround, applications can validate that redirect targets start with a scheme…Source: Red Hat security advisory for GHSA-fh3h-vg37-cc95 (CC BY 4.0)
Frequently Asked Questions
Is GHSA-fh3h-vg37-cc95 in your dependencies?
Find it across PyPI, including transitive dependencies.