Pylons Security Advisory
Topic: Path traversal bug in default error controller
Module: controllers/error.py
Announced: 2008-05-28
Credits: Webwise Security
Affects: All Pylons releases with Routes < 1.7.3
Corrected: Routes 1.7.3 or greater
Pylons 0.9.6.2
I. Background
The Pylons error.py controller is a default controller created in new Pylons
projects. It handles serving media and the default error page for a Pylons
application.
II. Problem Description
The error.py controller uses paste.fileapp to serve the static resources to
the browser. The default error.py controller uses os.path.join to combine
the id from Routes with the media path. Routes prior to 1.8 double unquoted
the PATH_INFO, resulting in FileApp returning files from the filesystem that
can be outside of the intended media path directory.
III. Impact
An attacker can craft URL's which utilize the double escaping to pass in a
name to the error.py controller which contains a leading slash thus escaping
the intended media path and serving files from any location on the filesystem
that the Pylons application has access to.
IV. Workaround
Any of the following will prevent the file traversal:
1) Upgrade Routes to Routes 1.7.3 (easy_install -U Routes)
This is a fix only for the multiple escaping which made it possible to
exploit FileApp.
2) Remove the methods 'img' and 'style' from the ErrorController inside
controllers/error.py. These methods are only needed to serve the default
error media, customizing the error page to render your own template doesn't
require these 2 methods.
3) Patch the controllers/error.py controller to import urllib, and then
urllib.quote_plus the id values before having them served by the
fileapp.
V. Solution
For new Pylons projects, starting with Pylons 0.9.6.2, the project template
will include changes to ensure that the base media path is not escaped.
For existing Pylons projects perform one of the following:
1) Update the controllers/error.py to use the StaticURLParser, this requires
the following changes:
a) Replace the fileapp import at the top with:
from paste.urlparser import StaticURLParser
b) Replace the img, style and _serve_file methods with the following ones:
def img(self, id):
"""Serve Pylons' stock images"""
return self._serve_file(os.path.join(media_path, 'img'), id)
def style(self, id):
"""Serve Pylons' stock stylesheets"""
return self._serve_file(os.path.join(media_path, 'style'), id)
def _serve_file(self, root, path):
"""Call Paste's FileApp (a WSGI application) to serve the file
at the specified path
"""
static = StaticURLParser(root)
request.environ['PATH_INFO'] = '/%s' % path
return static(request.environ, self.start_response)
2) Remove the 'img' and 'style' methods entirely. If the error.py 'document'
method is loading a custom error handler, the additional methods to load
Pylons media for the default error page is unnecessary.
3) Upgrade to Routes 1.7.3. This will prevent the double unquoting behavior,
but is not as secure as option (1) as there is still no additional check
that fileapp is being constrainted to the appropriate media directories.