Message387193
I can only reproduce the issue if the current directory (directory used by the HTTP server, see --directory command line option) contains a .ssh/ subdirectory.
The problem is that the HTTP Header Location starts with "//domain/" and such URL is interpreted as an absolute URL of a new domain name ("domain"), rather than a relative path of the same domain ("localhost").
Maybe we should simply strip all additional leading slashes to only keep one. Replace "//path" or "/////path" with "/path" for example.
---
By the way, http.server uses urllib.parse.urlsplit() on the request URL without passing its own domain, and urllib.parse.urlsplit() interprets "//attacker.com/path" as if attacker.com is a host with no scheme:
>>> urllib.parse.urlsplit('//attacker.com/path')
SplitResult(scheme='', netloc='attacker.com', path='/path', query='', fragment='')
Maybe parse_qs() should be used instead? Or we should reinject the server domain and port number? I am not sure that it's an issue in practice.
SimpleHTTPRequestHandler.translate_path('//attacker.com/..%2f..%2f..%2f..%2f..%2f../.ssh') returns os.path.join(self.directory, ".ssh"). I don't think that it's an issue, it sounds like the expected behavior. We don't attempt to reject ".." in URL.
---
To reproduce the issue, I used two terminals.
Terminal 1:
$ python3.8 -V
Python 3.8.7
$ python3.8 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
127.0.0.1 - - [15/Feb/2021 09:18:20] "GET
//attacker.com/..%2f..%2f..%2f..%2f..%2f../.ssh HTTP/1.1" 301 -
Terminal 2:
$ wget 'http://127.0.0.1:8000//attacker.com/..%2f..%2f..%2f..%2f..%2f../.ssh'
(...)
HTTP request sent, awaiting response... 301 Moved Permanently
Location: //attacker.com/..%2f..%2f..%2f..%2f..%2f../.ssh/ [following]
--2021-02-15 09:18:20-- http://attacker.com/..%2f..%2f..%2f..%2f..%2f../.ssh/
Resolving attacker.com (attacker.com)... 45.88.202.115
Connecting to attacker.com (attacker.com)|45.88.202.115|:80... connected.
(...)
wget is redirected and connects to attacker.com.
The HTTP redirection comes from Lib/http/server.py:
def send_head(self):
path = self.translate_path(self.path)
f = None
if os.path.isdir(path):
parts = urllib.parse.urlsplit(self.path)
if not parts.path.endswith('/'):
# redirect browser - doing basically what apache does
self.send_response(HTTPStatus.MOVED_PERMANENTLY)
new_parts = (parts[0], parts[1], parts[2] + '/',
parts[3], parts[4])
new_url = urllib.parse.urlunsplit(new_parts)
self.send_header("Location", new_url)
self.end_headers()
return None
...
...
The problem is that the "Location" header starts with "//". |
|
Date |
User |
Action |
Args |
2021-02-17 22:26:33 | vstinner | set | recipients:
+ vstinner, paul.moore, tim.golden, zach.ware, steve.dower, hamzaavvan |
2021-02-17 22:26:33 | vstinner | set | messageid: <1613600793.1.0.691205415807.issue43223@roundup.psfhosted.org> |
2021-02-17 22:26:33 | vstinner | link | issue43223 messages |
2021-02-17 22:26:32 | vstinner | create | |
|