CVE-2026-44889
WebOb open redirect via control characters in the Location header.
- CVSS 6.1
- CWE-601
- Input Validation and Sanitization
- Remote
WebOb provides objects for HTTP requests and responses. Prior to 1.8.10, the normalization of the HTTP Location header during a redirect is vulnerable to an open redirect: WebOb joins the redirect target to the request URI using Python's urljoin, and since Python 3.10 the underlying urlsplit strips ASCII tab, carriage return, and newline characters before parsing, so a redirect target containing such characters can be reinterpreted as a protocol-relative URL whose authority is an attacker-controlled host. This bypasses the CVE-2024-42353 fix that escaped a leading double slash, allowing an attacker who influences the redirect location to send users to an arbitrary external site instead of the intended one. This vulnerability is fixed in 1.8.10.
- CWE
- CWE-601
- CVSS base score
- 6.1
- Published
- 2026-06-22
- OWASP
- A01 Broken Access Control
- Orthogonal defect classification
- Checking
- Code defect classification
- Incorrect Algorithm
- Category
- Input Validation and Sanitization
- Subcategory
- Open Redirects
- Accessibility scope
- Remote
- Impact
- Information Disclosure
- Affected component
- Python 3.10
- Fixed by upgrading
- Yes
Solution
Upgrade WebOb to version 1.8.10.
Vulnerable code sample
import sys
from webob import Request, Response
from wsgiref.simple_server import make_server
# This code requires a vulnerable version of WebOb (< 1.8.10)
# and Python >= 3.10 to demonstrate the vulnerability.
def application(environ, start_response):
"""
A simple WSGI application that demonstrates the open redirect.
It takes a 'next' URL parameter and uses it for a redirect.
"""
request = Request(environ)
# Get the user-controlled redirect destination from the query string.
# A malicious user would provide a payload like: \n//evil-site.com
redirect_destination = request.params.get('next', '/')
# Create a 302 redirect response.
response = Response(status=302)
# VULNERABLE STEP:
# In WebOb < 1.8.10, setting response.location with a value like
# '\n//evil-site.com' on Python 3.10+ results in an open redirect.
# The internal use of urljoin->urlsplit strips the leading newline,
# reinterpreting the location as a protocol-relative URL to an
# external domain.
response.location = redirect_destination
print(f"Request for: {request.path_qs}")
print(f"Attempting redirect to: '{redirect_destination}'")
print(f"Resulting Location header: {response.location}\n")
return response(environ, start_response)
if __name__ == '__main__':
port = 8000
httpd = make_server('', port, application)
print(f"Serving on port {port}...")
print("Test the vulnerability with a URL like:")
print(f"http://localhost:{port}/?next=%0a//google.com")
print("(%0a is the URL-encoded newline character)")
try:
httpd.serve_forever()
except KeyboardInterrupt:
print("Server shutting down.")
httpd.server_close()Patched code sample
import sys
# Note: The provided CVE-2026-44889 is a placeholder, as the year is in the future.
# The code below represents the logic of the actual fix made in WebOb 1.8.10
# to address the described open redirect vulnerability.
class PatchedResponse:
"""
A simplified class demonstrating the fix applied in WebOb 1.8.10.
The key change is in the 'location' property setter.
"""
def __init__(self):
self._headers = {}
@property
def location(self):
return self._headers.get('Location')
@location.setter
def location(self, value):
"""
This setter contains the fix for the open redirect vulnerability.
It rejects any location value containing characters (newline, carriage
return, or tab) that would be stripped by urlsplit in Python 3.10+,
thus preventing a crafted value from being misinterpreted as a
protocol-relative URL.
"""
if any(c in value for c in '\r\n\t'):
raise ValueError(
"Control characters (CR, LF, TAB) are not allowed in the Location header."
)
self._headers['Location'] = str(value)Payload
\t//evil.com
Cite this entry
@misc{vaitp:cve202644889,
title = {{WebOb open redirect via control characters in the Location header.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-44889},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-44889/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
