VAITP Dataset

← Back to the dataset

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.

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 ::