Title: Directory traversal attack for CGIHTTPRequestHandler
Type: security Stage: resolved
Components: Library (Lib) Versions: Python 3.4, Python 3.3, Python 3.2, Python 2.7
Status: closed Resolution: fixed
Dependencies: Superseder:
Assigned To: christian.heimes Nosy List: Alexander.Kruppa, Arfrever, barry, benjamin.peterson, christian.heimes, georg.brandl, glondu, haypo, janzert, larry, python-dev
Priority: release blocker Keywords: patch

Created on 2013-10-29 16:34 by Alexander.Kruppa, last changed 2013-11-01 00:39 by Arfrever. This issue is now closed.

File name Uploaded Description Edit
cgi.patch benjamin.peterson, 2013-10-29 21:10 review
Messages (4)
msg201645 - (view) Author: Alexander Kruppa (Alexander.Kruppa) Date: 2013-10-29 16:34
An error in separating the path and filename of the CGI script to run in http.server.CGIHTTPRequestHandler allows running arbitrary executables in the directory under which the server was started.

The problem is that in CGIHTTPRequestHandler we have:

  def run_cgi(self):    
      """Execute a CGI script."""    
      path = self.path    
      dir, rest = self.cgi_info    
      i = path.find('/', len(dir) + 1)    

where path is the uncollapsed path in the URL, but cgi_info contains the first path segment and the rest from the *collapsed* path as filled in by is_cgi(), so indexing into path via len(dir) is incorrect.

An example exploit is giving the request path:


Note that Firefox and wget at least simplify the path in the request; to make sure this exact path is used, do for example:

  (echo "GET /////////// HTTP/1.1"; echo) | telnet localhost 4443

This causes the CGIHTTPRequestHandler to execute the file in the directory in which the server was started, so script execution is not restricted to the cgi-bin/ or htbin/ subdirectories.
msg201647 - (view) Author: Christian Heimes (christian.heimes) * (Python committer) Date: 2013-10-29 16:48
I can confirm the issue:

$ mkdir www
$ cd www
$ cat << EOF >
echo hacked
$ chmod +x
$ ../python -m http.server --cgi

$ echo "GET /////////// HTTP/1.1" | nc localhost 8000
HTTP/1.0 200 Script output follows
Server: SimpleHTTP/0.6 Python/3.4.0a4+
Date: Tue, 29 Oct 2013 16:47:22 GMT
msg201673 - (view) Author: Benjamin Peterson (benjamin.peterson) * (Python committer) Date: 2013-10-29 21:10
msg201747 - (view) Author: Roundup Robot (python-dev) Date: 2013-10-30 16:51
New changeset e4fe8fcaef0d by Benjamin Peterson in branch '2.7':
use the collapsed path in the run_cgi method (closes #19435)

New changeset b1ddcb220a7f by Benjamin Peterson in branch '3.1':
use the collapsed path in the run_cgi method (closes #19435)

New changeset dda1a32748e0 by Benjamin Peterson in branch '3.2':
merge 3.1 (#19435)

New changeset 544b654d000c by Benjamin Peterson in branch '3.3':
merge 3.2 (#19435)

New changeset 493a99acaf00 by Benjamin Peterson in branch 'default':
merge 3.3 (#19435)
Date User Action Args
2013-11-01 00:39:27Arfreversetnosy: + Arfrever
2013-10-30 16:51:29python-devsetstatus: open -> closed

nosy: + python-dev
messages: + msg201747

resolution: fixed
stage: test needed -> resolved
2013-10-29 21:10:13benjamin.petersonsetfiles: + cgi.patch
keywords: + patch
messages: + msg201673
2013-10-29 18:49:24janzertsetnosy: + janzert
2013-10-29 16:54:48glondusetnosy: + glondu
2013-10-29 16:51:02barrysetnosy: + barry
2013-10-29 16:48:40christian.heimessetpriority: normal -> release blocker

assignee: christian.heimes
versions: + Python 2.7, Python 3.3, Python 3.4
nosy: + larry, benjamin.peterson, georg.brandl

messages: + msg201647
stage: test needed
2013-10-29 16:35:41hayposetnosy: + haypo, christian.heimes
2013-10-29 16:34:01Alexander.Kruppacreate