Kind of Directory Traversal

If you see this code: what’s wrong?

from os import path

page_id = req.args.get('page_id', 'index')
filename = path.join(path.dirname(__file__), 'includes', page_id)
try:
    f = file(filename)
except IOError:
    handle_not_found()

First of all, the page_id comes from an user submitted variable in a web application. It’s in fact a variable from the query string. Now someone could say ?page_id=../../../etc/htpasswd etc. There are numerous ways to fix that. For example you can do this:

from os import path
fn = path.join(*[x for x in fn.split('/') if x != '..'])

This also makes sure that the path separator is valid. I guess everybody that knows about security also knows how to defend those problems.

What you might not know is that python itself doesn’t accept null bytes in the “file/open” call. This is some sort of built in security feature. Other languages such as PHP forward the nullbyte to the C layer. Thus an attacker could cut off a string at a given position to gain further control over the input layer (cutting of a .php extension etc). This is more severe in Perl where you can also pipe stuff in the file call.

This security feature however doesn’t raise an IOError but a TypeError! So the corrected example from above looks like this:

from os import path

page_id = req.args.get('page_id', 'index')
filename = path.join(path.dirname(__file__), 'includes',
                     *[x for x in page_id.split('/') if x != '..'])
try:
    f = file(filename)
except (IOError, TypeError):
    handle_not_found()

Leave a Reply

cogitations driven by wordpress