Windows path traversal in http.server SimpleHTTPRequestHandler.translate_path() via trailing dot/space components
まだ誰も着手していません。
- 主要言語
- Python
- スター
- 77.2k
- フォーク
- 35.9k
- PR マージ指標
- PR 指標を取得中
説明
Bug report
Bug description:
Opening this issue publicly per the request of the Python Security team as the http.server module doesn't implement security guarantees and is not meant for production.
Summary
On Windows, translate_path() filters out only the exact . and .. path components before joining the request path to the served directory. Components such as .. (trailing space) or .. . are not equal to .., so they survive the filter and are appended to the filesystem path. Because Windows strips trailing spaces and dots from each path component during filesystem resolution, a request like GET /..%20/secret.txt resolves to the parent of the document root at file-open time, allowing a remote client to read files outside the configured directory.
Details
Lib/http/server.py, SimpleHTTPRequestHandler.translate_path() (also reached by CGIHTTPRequestHandler on the branches where it still exists).
The per-component filter in translate_path():
path = posixpath.normpath(path)
words = path.split('/')
words = filter(None, words)
path = self.directory
for word in words:
if os.path.dirname(word) or word in (os.curdir, os.pardir):
# Ignore components that are not a simple file/directory name
continue
path = os.path.join(path, word)
os.path.dirname('.. ') is empty and '.. ' in (os.curdir, os.pardir) is False, so .. is treated as an ordinary filename and joined onto the path. send_head() then uses the result directly:
path = self.translate_path(self.path)
...
f = open(path, 'rb')
There is no canonicalization or containment check between translate_path() and the open().
I verified the code-path bypass locally on Linux by reproducing the exact translate_path() logic against both posixpath and ntpath. /..%20/secret.txt and /..%20./secret.txt both survive the filter and produce a component .. / .. . appended after the served directory, while a plain /../secret.txt is correctly collapsed by normpath and rejected. The escape itself relies on documented Windows path canonicalization (trailing spaces and dots stripped per component). I did not run this against a live Windows.
The POSIX runtime is not affected: there .. is a literal directory name (dot dot space) that does not exist, so the request returns 404 and never escapes the served directory.
The vulnerable filter is byte-identical on every currently supported branch: main, 3.15, 3.14, 3.13, 3.12, 3.11 and 3.10. 3.9 and earlier are EOL. CGIHTTPRequestHandler, which reaches the same code, was removed on main in https://github.com/python/cpython/issues/133810 and is deprecated-since-3.13 / removed-in-3.15, but is still present in the maintenance branches.
Disclosure
This issue was found by AISLE in partnership with Red Hat.
CPython versions tested on:
CPython main branch, 3.16, 3.15, 3.14, 3.13, 3.12, 3.11, 3.10
Operating systems tested on:
Windows
コントリビューションガイド
はじめの一歩
- issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
- 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
- リポジトリをフォークし、ブランチを切って変更します。
- issue 番号を参照したプルリクエストを送ります。
調査の方向性
Lib/http/server.py の SimpleHTTPRequestHandler.translate_path() から始め、maintenance branches 上の send_head() と CGIHTTPRequestHandler のパスを追跡します。Windows で末尾にドットまたはスペースがあるリクエストを再現し、適切なカバレッジに使える既存の http.server テストを確認します。完了の条件は、通常のパスが影響を受ける各ブランチで引き続き機能しつつ、リクエストによって提供ディレクトリの外部を解決できないことです。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- python
- 領域
- backend, security
- issue の種類
- バグ
- 難易度
- 4/5
- 見積もり時間
- 3〜5日
- 活発さ
- 活発
- 明瞭さ
- おおむね明確
- 初心者へのやさしさ
- 48/100