GHSA-6hx8-3wjj-gr8g: Medium severity pip/webob vulnerability

Published Aug 27, 2026
·
Updated

Summary

This is a third follow-up to CVE-2024-42353 / GHSA-mg3v-6m49-jhp3 and CVE-2026-44889 / GHSA-fh3h-vg37-cc95.

WebOb makes the Location header absolute when it serves a redirect. To stop a relative or protocol-relative target from redirecting users off-host, it checks the value for a URI scheme and for a leading //, then joins it against the request URI with urllib.parse.urljoin(). The previous fix additionally stripped ASCII tab/CR/LF from the value before those checks.

However, on Python 3.10+ urllib.parse.urljoin() (via urlsplit()) does more than remove tab/CR/LF: it also strips leading and trailing C0 control characters (U+0000–U+001F) and spaces from the URL before parsing it. Because WebOb's guard checks (SCHEMERE and startswith("//")) run against the un-stripped value, a single leading space or control byte slips past them, and urljoin() then silently removes that byte and parses what remains as a protocol-relative — or even absolute — URL. The result is an open redirect to an attacker-controlled host.

Details

Response.makelocationabsolute() (in src/webob/response.py) performed, prior to the fix:

python value = value.replace("\t", "").replace("\r", "").replace("\n", "")

if SCHEMERE.search(value): # ^[a-z]+: -> already absolute, return as-is return value

if value.startswith("//"): # neutralize protocol-relative URLs value = f"/%2f{value[2:]}"

newlocation = urlparse.urljoin(requesturi(environ), value)

Consider the Location value " //www.example.com/test" (a single leading space):

1. The explicit strip only removes \t, \r, \n — the leading space survives. 2. SCHEMERE (^[a-z]+:) does not match — the value starts with a space. 3. value.startswith("//") is False — the value starts with a space, not /. The // → /%2f neutralization is skipped. 4. urllib.parse.urljoin(requesturi(environ), " //www.example.com/test") then strips the leading space before parsing, sees //www.example.com/test, treats it as protocol-relative, and returns http://www.example.com/test.

The same bypass works with a value such as " https://www.example.com/test" (leading space + a full scheme): SCHEMERE does not match the space-prefixed string, but urljoin() strips the space and returns the fully absolute attacker URL https://www.example.com/test.

Any C0 control character works equally well in place of the space, e.g. "\x00//www.example.com/test" or "\x1f//www.example.com/test", because urlsplit() strips the whole leading C0-control-and-space run.

Affected entry points

- Response.location — any application that sets a relative/attacker-influenced Location and serves the response (the classic redirect path). - Request.relativeurl() — used urllib.parse.urljoin() directly and was subject to the same character stripping. - webob.exc.HTTPMove subclasses (HTTPMovedPermanently, HTTPFound, HTTPSeeOther, HTTPTemporaryRedirect, HTTPPermanentRedirect, etc.) — these built their absolute Location with urlparse.urljoin(req.pathurl, self.location) without going through makelocationabsolute() at all, so they bypassed even the tab/CR/LF strip and the // → /%2f neutralization. A protocol-relative location passed to e.g. HTTPFound(location="//evil.example") redirected off-host.

Proof of Concept

python from webob import Response from webob.request import Request

res = Response() res.status = "301" res.location = " //www.example.com/test" # note the single leading space

req = Request.blank("/") # request host is "localhost" print(req.getresponse(res).location) Vulnerable (<= 1.8.10): http://www.example.com/test <-- open redirect Fixed: http://localhost/ //www.example.com/test

Absolute-URL variant:

python res.location = " https://www.example.com/test" Vulnerable: https://www.example.com/test <-- off-host Fixed: http://localhost/ https://www.example.com/test

Via the HTTP exceptions:

python from webob import exc

environ = { "wsgi.urlscheme": "http", "SERVERNAME": "localhost", "SERVERPORT": "80", "REQUESTMETHOD": "HEAD", "PATHINFO": "/", } m = exc.HTTPFound(location="//www.example.com/test") m(environ, lambda a, k: None) print(m.location) Vulnerable: //www.example.com/test <-- open redirect Fixed: http://localhost/%2fwww.example.com/test

Impact

An unauthenticated remote attacker who controls (in whole or part) the redirect target of an application built on WebOb can redirect a user from a trusted host to an attacker-controlled host. This enables phishing and credential-theft campaigns that abuse the trusted origin, and can be chained with OAuth/SSO redirecturi flows to leak tokens. Exploitation requires user interaction (following the redirect). Confidentiality and integrity impact are limited (L); the scope is changed (C) because the trust boundary of the originating site is crossed.

Patches

Fixed by replacing the use of urllib.parse.urljoin() with WebOb's own RFC 3986 reference-resolution implementation, webob.util.urljoin(), which resolves the reference exactly as given, character for character, with no whitespace or control-character removal.

- Response.makelocationabsolute() now uses webob.util.urljoin(). - Request.relativeurl() now uses webob.util.urljoin(). - webob.exc.HTTPMove now normalizes its Location through the same makelocationabsolute() code path as Response, so protocol-relative and whitespace-smuggled locations are neutralized there too.

Users should upgrade to the patched release. There are no API changes.

Workarounds

- Only ever set the Location header / redirect target to a fully-qualified URI whose host you control, or strictly allowlist redirect destinations before handing them to WebOb. - Reject any redirect target that does not begin with https://yourhost/ (or a validated relative path with no leading whitespace/control bytes).

References

- This advisory: GHSA-6hx8-3wjj-gr8g - GHSA-fh3h-vg37-cc95 (CVE-2026-44889) — second incomplete fix (tab/CR/LF) - GHSA-mg3v-6m49-jhp3 (CVE-2024-42353) — original open redirect fix - RFC 3986, Section 5 — Reference Resolution: https://www.rfc-editor.org/rfc/rfc3986#section-5 - Python urllib.parse URL stripping behavior (CPython 3.10+, removal of leading and trailing C0 control and space characters): https://docs.python.org/3/library/urllib.parse.html To report a vulnerability to the Pylons Project please take a look at:

- Pylons Project security policy and reporting process: https://github.com/Pylons/.github/blob/main/SECURITY.md - Security contact (private, coordinated disclosure): pylons-project-security@googlegroups.com (the Pylons Project requests a 90-day disclosure embargo)

Credit

Reported via the Pylons Project security mailing list by:

- tonghuaroot — for the residual open redirect in Response.makelocationabsolute(): the 1.8.10 fix stripped only ASCII tab/CR/LF, but urllib.parse.urljoin() also strips leading C0 control and space characters, so values such as " //attacker.example/path" (and " https://attacker.example/path") still escaped off-host. - Matheus Polkorny — for identifying that the webob.exc.HTTPMove redirect exceptions (HTTPFound and friends) performed their own urllib.parse.urljoin() normalization and never went through makelocationabsolute(), so a protocol-relative location such as //evil.example/path/ redirected off-host through that separate code path.

Affected Software

1 affected componentFixes available
pip/webob<1.8.11
1.8.11

Remediation

Recommended actions to resolve this vulnerability, in priority order.

  1. Upgrade

    Upgrade pip/webob to a version that resolves this vulnerability.

    Fixed in 1.8.11
  2. Upgrade

    Upgrade WebOb to a version that resolves this vulnerability.

    Fixed in 1.8.10
  3. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Patch GHSA-mg3v-6m49-jhp3
  4. Upgrade

    Upgrade to a fixed release to a version that resolves this vulnerability.

    Patch GHSA-fh3h-vg37-cc95
  5. Configuration

    For WebOb redirect exceptions (webob.exc._HTTPMove: HTTPFound, HTTPMovedPermanently, etc.) and Response location handling, replace the use of urllib.parse.urljoin() with webob.util.urljoin(), and ensure Response._make_location_absolute() and webob.exc._HTTPMove normalize the Location through the same code path, including stripping ASCII tab/CR/LF and leading C0 control characters and spaces before any protocol/host checks.

    WebOb (redirect Location handling) Location normalization/absolute URL resolution = Use webob.util.urljoin() and normalize Location via the same checks; strip leading/trailing C0 control characters and space before parsing; apply tab/CR/LF stripping before checks
  6. Configuration

    Only ever set the Location/redirect target to a fully-qualified URI that begins with the allowed/trusted origin pattern specified by the advisory (the text states: reject redirect targets that do not begin with `https://yourhost/`).

    Application redirect generation using WebOb Redirect target (Location header / redirect location) = Fully-qualified URI with explicit trusted origin (example constraint)

Event History

Aug 27, 2026
Advisory Published
via GitHub·10:10 PM
Data Sourced
via GitHub·10:10 PM
DescriptionSeverityWeaknessAffected Software

Frequently Asked Questions

1

Which deployments are affected?

The issue applies to WebOb redirect handling when running on Python 3.10 or later. It is relevant where an attacker can influence a redirect target used in a Location header.

2

What does an attacker need to exploit this?

An attacker needs to supply a redirect value with a leading space or C0 control character followed by a protocol-relative or absolute attacker-controlled URL. WebOb's checks evaluate the unstripped value, while Python's URL parsing strips the leading character before resolving the URL.

3

Does exploitation require an authenticated account?

No privileges are required according to the supplied severity vector. Exploitation does require user interaction, such as a user following the resulting redirect.

4

What is the likely impact?

A successful exploit can redirect users to an attacker-controlled host. The supplied severity vector rates confidentiality and integrity impact as low and availability impact as none.

Contact

SecAlerts Pty Ltd.
132 Wickham Terrace
Fortitude Valley,
QLD 4006, Australia
info@secalerts.co
By using SecAlerts services, you agree to our services end-user license agreement. This website is safeguarded by reCAPTCHA and governed by the Google Privacy Policy and Terms of Service. All names, logos, and brands of products are owned by their respective owners, and any usage of these names, logos, and brands for identification purposes only does not imply endorsement. If you possess any content that requires removal, please get in touch with us.
© 2026 SecAlerts Pty Ltd.
ABN: 70 645 966 203, ACN: 645 966 203