Skip to content

2.7.18.16: SimpleHTTPServer // open redirect (CVE-2021-28861) - #97

Merged
icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-httpserver-open-redirect
Oct 28, 2026
Merged

icanhasmath merged 1 commit into
2.7.18.16from
2.7.18.16-httpserver-open-redirect

Conversation

@icanhasmath

Copy link
Copy Markdown

Backport of python/cpython pythongh-87389 (CVE-2021-28861, PSF-2022-5) to 2.7.18.x. This fix was released after Python 2.7's end of life.

Problem: if a request path starts with // and resolves to a directory, SimpleHTTPRequestHandler answers with a 301 whose Location also starts with //. Browsers treat //host/path as a URL to another host, so a crafted link to the server can redirect users anywhere.

Fix: BaseHTTPRequestHandler.parse_request() now collapses any leading run of slashes in the path to a single /.

Test: test_get_dir_redirect_location_domain_injection_bug, adapted from upstream with string formatting and the literal 301.

  • On 2.7.18.15 the test fails: the server returns Location: //python.org/..%2f…/existing_directory/.
  • With the fix, test_httpservers passes 41/41.

Part of 2.7.18.16. It targets the 2.7.18.16 integration branch.

CVE-2021-28861)

Backport of python/cpython pythongh-87389 (pythonGH-93879, 4dc2cae) to 2.7.18.x.

A request path starting with "//" that resolves to a directory made
SimpleHTTPRequestHandler.send_head() answer with a 301 whose Location
header also started with "//".  Browsers treat "//host/path" as a
scheme-relative URL, so a crafted link to the server redirected the user
to an attacker-chosen host.  BaseHTTPRequestHandler.parse_request() now
reduces a leading run of slashes in the path to a single "/".

Verified on 2.7.18.15: the "//python.org/..." request answers
"Location: //python.org/..."; with this change it is "/python.org/...".

2.7.18.x: the fix lands in BaseHTTPServer.parse_request(); the test uses
string concatenation and the literal 301 status instead of f-strings and
HTTPStatus.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

@ezequielp-activestate ezequielp-activestate left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No blockers.

  • Fix matches upstream. The leading-slash collapse in parse_request() is upstream pythongh-87389 (4abab6b). No later upstream fix changes it.

  • Repro (SimpleHTTPRequestHandler, serving a directory with existing_directory/):

    request path base Location this branch Location
    //python.org/..%2f..%2f..%2f..%2f..%2f../%0a%0d/../existing_directory //python.org/… /python.org/…
    ////evil.com/..%2f../existing_directory //evil.com/… /evil.com/…
    /existing_directory /existing_directory/ unchanged
  • Tests: test_httpservers passes.

  • FYI, not for this PR: /\evil.com/..%2f../existing_directory still redirects to Location: /\evil.com/..%2f../existing_directory/, which browsers read as //evil.com/…. Upstream 3.14 does exactly the same, so this isn't a gap in the backport.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants