Tuesday, 7 December 2010
Securing Python From Path Manipulation
« Securing Your Android Phone | Main | Free Security Audit For An Open Source Web App »Path manipulation is where a hacker injects a fake path into an application to trick it into reading something different than it would normally. Take for example this simple application:
import csv
import sys
filename = sys.argv[1]
user = sys.argv[2]
password = sys.argv[3]
csvfile = '%(file)s.csv' % { 'file': filename }
print 'Checking %(file)s' % { 'file': csvfile }
userReader = csv.reader( open(csvfile, 'rb'), delimiter=',' )
found = 0
for row in userReader:
if user == row[0] and password == row[1]:
found = 1
print 'Logged in'
if found == 0:
print 'Not logged in'
This is a simple application that reads user accounts and does a login check. The first parameter is supposed to be 'users' or 'administrators'. The second and third parameters are the user name and password. Because the coder (me) is lazy I'm just going to use that first parameter to point to a particular password file. And because I believe that the file system permissions for the directory prevent tampering all is well.
Not so fast. Sure the happy path works just fine:
$ python bad.py users user1 password Checking users.csv Logged in
But so does the hacker path:
$ python bad.py ../mydata/users hacker password Checking ../mydata/users.csv Logged in
Because of the way I construct the path a hack can easily point the application at a completely different users file in another directory which they control and, viola, they have access. Whoops.
There are some simple fixes for this. One would be to selectively sanitize the input.
password = sys.argv[3]
filename = filename.replace( '.', '' )
filename = filename.replace( '/', '' )
csvfile = '%(file)s.csv' % { 'file': filename }
print 'Checking %(file)s' % { 'file': csvfile }
In this case I'm using a string replace to remove any dots or slashes. That will kill any directory referencing on most operating systems.
The problem with this approach is that there is still too much to check to be guaranteed that there will never be an issue. So another, better, option is to just check the value against known goods:
password = sys.argv[3]
if filename != 'users' and filename != 'administrators':
print 'Bad user type'
exit( 0 )
csvfile = '%(file)s.csv' % { 'file': filename }
print 'Checking %(file)s' % { 'file': csvfile }
This way we can give the user some informative response if their input is invalid, and we can check all of the possible input variations without worrying about patterns we hadn't yet come up with.
[Trackback URL for this entry]







