[Python-Dev] Challenge: Please break this! (was: Reviving restricted mode)
Hey all, As an attempt to convince everyone of the merits of my functions-based approach to security, I've come up with a simple challenge. I've attached it as safelite.py The challenge is simple: * Open a fresh Python interpreter * 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 Please note that the aim of this isn't to protect Python against crashes/segfaults or exhaustion of resources attacks, so those don't count. I'm keen to know your experiences even if you don't manage to write to the filesystem -- and especially if you do! Dinner and drinks on me for an evening -- when you are next in London or I am in your town -- to the first person who manages to break safelite.py and write to the filesystem. Good luck and thanks! =) If you block __closure__ and __globals__ on function objects you will get a semblance of a private namespace. That way you might (I have not thought this one through like securing the interpreter for embedding) be able to get what you need to safely pass in Python code through the globals of the code being executed. Brett, this is exactly what I do. You also need to restrict func_code. The patch is simply for closing the other loopholes: type.__subclasses__, GeneratorType.gi_frame and gi_code. All possible in a patch of 6 lines of code thanks to Python's existing restricted framework in the interpreter. Please review and accept =) * http://codereview.appspot.com/20051 * http://codereview.appspot.com/21051 Thanks! -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | @tav | skype:tavespian Please try and break this. On a fresh Python interpreter, do the following: from safelite import FileReader You should now be able to read files as you want... Now, please try and *write* to a file on the filesystem from within the interpreter. Please note that the aim of this isn't to protect Python against segfaults or exhaustion of resources attacks, so those don't count. Let me know t...@espians.com or Python-Dev how your experience goes -- whether this seems to work for you or not. Thanks! import __builtin__ import sys from sys import _getframe as get_frame from types import FunctionType, GeneratorType __all__ = ['FileReader'] # -- # map funktion attribute names for python versions = 2.6 # -- FUNCTION_PY26_ATTRS = { 'func_code': '__code__', 'func_globals': '__globals__', 'func_closure': '__closure__' } # -- # sekure the interpreter! # -- def secure_python(): Remove insecure variables from the Python interpreter. from ctypes import pythonapi, POINTER, py_object get_dict = pythonapi._PyObject_GetDictPtr get_dict.restype = POINTER(py_object) get_dict.argtypes = [py_object] def dictionary_of(ob): dptr = get_dict(ob) if dptr and dptr.contents: return dptr.contents.value if sys.version_info = (3, 0): py_version = 2 elif sys.version_info = (2, 6): py_version = 1 else: py_version = 0 for attr in FUNCTION_PY26_ATTRS.keys(): if py_version = 1: del dictionary_of(FunctionType)[attr] if py_version = 1: del dictionary_of(FunctionType)[FUNCTION_PY26_ATTRS[attr]] del dictionary_of(type)['__subclasses__'] del dictionary_of(GeneratorType)['gi_frame'] if py_version: del dictionary_of(GeneratorType)['gi_code'] def secure_python_builtins(): Remove dangerous builtins like ``file`` and patch appropriately. for item in ['open', 'file', 'execfile']: del __builtin__.__dict__[item] def null(*args, **kwargs): pass import linecache linecache.open = FileReader import site site.file = FileReader __builtin__.__import__ = null # -- # pseudo-klass-like namespase wrapper # -- class NamespaceContext(type): A Namespace Context metaclass. def __call__(klass, __getter): for name, obj in __getter: setattr(klass, name, obj) return type.__call__(klass, __getter) def __str__(klass): return 'NamespaceContext%s' % (tuple(klass.__dict__.keys()),) def _Namespace(): __private_data = {} def Namespace(*args, **kwargs): Return a Namespace from the current scope or the given arguments. class NamespaceObject(tuple): __metaclass__ = NamespaceContext __slots__ = ()
Re: [Python-Dev] Challenge: Please break this! (was: Reviving restricted mode)
On Mon, Feb 23, 2009 at 12:10, tav t...@espians.com wrote: Hey all, As an attempt to convince everyone of the merits of my functions-based approach to security, I've come up with a simple challenge. I've attached it as safelite.py The challenge is simple: * Open a fresh Python interpreter * 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 Please note that the aim of this isn't to protect Python against crashes/segfaults or exhaustion of resources attacks, so those don't count. I'm keen to know your experiences even if you don't manage to write to the filesystem -- and especially if you do! Dinner and drinks on me for an evening -- when you are next in London or I am in your town -- to the first person who manages to break safelite.py and write to the filesystem. Good luck and thanks! =) If you block __closure__ and __globals__ on function objects you will get a semblance of a private namespace. That way you might (I have not thought this one through like securing the interpreter for embedding) be able to get what you need to safely pass in Python code through the globals of the code being executed. Brett, this is exactly what I do. Ah, OK. I just quickly looked at your patches on codereview and noticed that neither __closure__ or __globals__ have been touched. You also need to restrict func_code. I assume you are worried about getting a hold of the code type and constructing code objects from scratch? The patch is simply for closing the other loopholes: type.__subclasses__, GeneratorType.gi_frame and gi_code. All possible in a patch of 6 lines of code thanks to Python's existing restricted framework in the interpreter. Please review and accept =) I personally don't have the time. The feedback in this email is all I can spare. -Brett ___ 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! (was: Reviving restricted mode)
Hey Brett, Ah, OK. I just quickly looked at your patches on codereview and noticed that neither __closure__ or __globals__ have been touched. Those are already restricted by Python when __builtins__ is not the same as the standard one. I assume you are worried about getting a hold of the code type and constructing code objects from scratch? Right. And, func_code is restricted too so didn't need any additional support. Please review and accept =) I personally don't have the time. The feedback in this email is all I can spare. That's cool -- thanks for this much! I'm hoping someone out there has a few spare minutes. The patch is just 6 lines of code... Someone? Pretty please? =) -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.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! (was: Reviving restricted mode)
On Mon, Feb 23, 2009 at 12:10 PM, tav t...@espians.com wrote: Hey all, As an attempt to convince everyone of the merits of my functions-based approach to security, I've come up with a simple challenge. I've attached it as safelite.py The challenge is simple: * Open a fresh Python interpreter * 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 If you really want people to try and break it, I suggest you send it to c.l.py - there are load of people there with too much time on their hands. ;-) Steve -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy ___ 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! (was: Reviving restricted mode)
I sent a link out to Twitter... On Mon, Feb 23, 2009 at 12:40 PM, Steven Bethard steven.beth...@gmail.com wrote: On Mon, Feb 23, 2009 at 12:10 PM, tav t...@espians.com wrote: Hey all, As an attempt to convince everyone of the merits of my functions-based approach to security, I've come up with a simple challenge. I've attached it as safelite.py The challenge is simple: * Open a fresh Python interpreter * 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 If you really want people to try and break it, I suggest you send it to c.l.py - there are load of people there with too much time on their hands. ;-) Steve -- I'm not *in*-sane. Indeed, I am so far *out* of sane that you appear a tiny blip on the distant coast of sanity. --- Bucky Katt, Get Fuzzy -- --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! (was: Reviving restricted mode)
The challenge is simple: * Open a fresh Python interpreter * 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 Well, the challenge is to get access to a module. And... it's quite simple :-p $ ./python from safelite import FileReader __builtins__.file Traceback (most recent call last): File stdin, line 1, in module AttributeError: 'module' object has no attribute 'file' reload(__builtins__) module '__builtin__' (built-in) file('0wn3d', 'w').write('w00t\n') $ cat 0wn3d w00t Dinner and drinks on me for an evening -- when you are next in London or I am in your town -- to the first person who manages to break safelite.py and write to the filesystem. Cool. It's a good reason to go to Pycon UK this yeak ;-) -- 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! (was: Reviving restricted mode)
Woo! victor file('0wn3d', 'w').write('w00t\n') victor Cool. It's a good reason to go to Pycon UK this yeak ;-) Thank you so much Victor! Please mail/phone me when you are heading to London and I shall honour the evening out! Now, how about this adapted version without reload? I could make life much easier for myself by just removing all builtins except the fundamental types, but this is way more interesting!! Please continue the challenge the same as before, but with the new safelite.py steven If you really want people to try and break it, I steven suggest you send it to c.l.py Done. -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.com | http://twitter.com/tav | skype:tavespian Please try and break this. On a fresh Python interpreter, do the following: from safelite import FileReader You should now be able to read files as you want... Now, please try and *write* to a file on the filesystem from within the interpreter. Please note that the aim of this isn't to protect Python against segfaults or exhaustion of resources attacks, so those don't count. Let me know t...@espians.com or Python-Dev how your experience goes -- whether this seems to work for you or not. Thanks! import __builtin__ import sys from sys import _getframe as get_frame from types import FunctionType, GeneratorType __all__ = ['FileReader'] # -- # map funktion attribute names for python versions = 2.6 # -- FUNCTION_PY26_ATTRS = { 'func_code': '__code__', 'func_globals': '__globals__', 'func_closure': '__closure__' } # -- # sekure the interpreter! # -- def secure_python(): Remove insecure variables from the Python interpreter. from ctypes import pythonapi, POINTER, py_object get_dict = pythonapi._PyObject_GetDictPtr get_dict.restype = POINTER(py_object) get_dict.argtypes = [py_object] def dictionary_of(ob): dptr = get_dict(ob) if dptr and dptr.contents: return dptr.contents.value if sys.version_info = (3, 0): py_version = 2 elif sys.version_info = (2, 6): py_version = 1 else: py_version = 0 for attr in FUNCTION_PY26_ATTRS.keys(): if py_version = 1: del dictionary_of(FunctionType)[attr] if py_version = 1: del dictionary_of(FunctionType)[FUNCTION_PY26_ATTRS[attr]] del dictionary_of(type)['__subclasses__'] del dictionary_of(GeneratorType)['gi_frame'] if py_version: del dictionary_of(GeneratorType)['gi_code'] def secure_python_builtins(): Remove dangerous builtins like ``file`` and patch appropriately. # thanks Victor Stinner! for item in ['open', 'file', 'execfile', 'reload']: del __builtin__.__dict__[item] def null(*args, **kwargs): pass import linecache linecache.open = FileReader import site site.file = FileReader __builtin__.__import__ = null # -- # pseudo-klass-like namespase wrapper # -- class NamespaceContext(type): A Namespace Context metaclass. def __call__(klass, __getter): for name, obj in __getter: setattr(klass, name, obj) return type.__call__(klass, __getter) def __str__(klass): return 'NamespaceContext%s' % (tuple(klass.__dict__.keys()),) def _Namespace(): __private_data = {} def Namespace(*args, **kwargs): Return a Namespace from the current scope or the given arguments. class NamespaceObject(tuple): __metaclass__ = NamespaceContext __slots__ = () def __new__(klass, __getter): return tuple.__new__(klass, __getter) ns_items = []; populate = ns_items.append if args or kwargs: frame = None for arg in args: kwargs[arg.__name__] = arg for name, obj in kwargs.iteritems(): if isinstance(obj, FunctionType): populate((name, staticmethod(obj))) else: populate((name, obj)) else: frame = get_frame(1) for name, obj in frame.f_locals.iteritems(): if isinstance(obj, FunctionType): if not (name.startswith('_') and not name.startswith('__')): populate((name, staticmethod(obj))) elif name.startswith('__') and name.endswith('__'): populate((name, obj)) del frame, args, kwargs
Re: [Python-Dev] Challenge: Please break this! (was: Reviving restricted mode)
On Mon, Feb 23, 2009 at 1:12 PM, Victor Stinner victor.stin...@haypocalc.com wrote: The challenge is simple: * Open a fresh Python interpreter * 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 Well, the challenge is to get access to a module. And... it's quite simple :-p $ ./python from safelite import FileReader __builtins__.file Traceback (most recent call last): File stdin, line 1, in module AttributeError: 'module' object has no attribute 'file' reload(__builtins__) module '__builtin__' (built-in) file('0wn3d', 'w').write('w00t\n') $ cat 0wn3d w00t Dinner and drinks on me for an evening -- when you are next in London or I am in your town -- to the first person who manages to break safelite.py and write to the filesystem. Cool. It's a good reason to go to Pycon UK this yeak ;-) Tav should have made another stipulation: the attack must not be trivial to fix. This one seems trivial, e.g. by adding 'reload' to the list in secure_python_builtins(). -- --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! (was: Reviving restricted mode)
On Mon, Feb 23, 2009 at 1:36 PM, Guido van Rossum gu...@python.org wrote: On Mon, Feb 23, 2009 at 1:12 PM, Victor Stinner victor.stin...@haypocalc.com wrote: The challenge is simple: * Open a fresh Python interpreter * 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 Well, the challenge is to get access to a module. And... it's quite simple :-p $ ./python from safelite import FileReader __builtins__.file Traceback (most recent call last): File stdin, line 1, in module AttributeError: 'module' object has no attribute 'file' reload(__builtins__) module '__builtin__' (built-in) file('0wn3d', 'w').write('w00t\n') $ cat 0wn3d w00t Dinner and drinks on me for an evening -- when you are next in London or I am in your town -- to the first person who manages to break safelite.py and write to the filesystem. Cool. It's a good reason to go to Pycon UK this yeak ;-) Tav should have made another stipulation: the attack must not be trivial to fix. This one seems trivial, e.g. by adding 'reload' to the list in secure_python_builtins(). I take it back, we need to find all the trivial ones too. BTW Tav, you ought to create a small website for this challenge. A blog post or wiki page would suffice. -- --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! (was: Reviving restricted mode)
Le Monday 23 February 2009 22:36:47, vous avez écrit : reload(__builtins__) (...) Tav should have made another stipulation: the attack must not be trivial to fix. Why not? Any hole is enough to break a jail. The cracker doesn't care if it's trivial to fix or not :-p -- 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! (was: Reviving restricted mode)
It seems like some code in safelite passes a file object to isinstance. By overriding the builtin isinstance function I can get access to the original file object and create a new one. Here is the code I used: from safelite import FileReader _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) _old_isinstance = __builtins__.isinstance __builtins__.isinstance = _new_isinstance FileReader('nul') f = _real_file('foo.txt','w') f.write('hello') f.close() -Farshid On Mon, Feb 23, 2009 at 12:10 PM, tav t...@espians.com wrote: Hey all, As an attempt to convince everyone of the merits of my functions-based approach to security, I've come up with a simple challenge. I've attached it as safelite.py The challenge is simple: * Open a fresh Python interpreter * 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 Please note that the aim of this isn't to protect Python against crashes/segfaults or exhaustion of resources attacks, so those don't count. I'm keen to know your experiences even if you don't manage to write to the filesystem -- and especially if you do! Dinner and drinks on me for an evening -- when you are next in London or I am in your town -- to the first person who manages to break safelite.py and write to the filesystem. Good luck and thanks! =) If you block __closure__ and __globals__ on function objects you will get a semblance of a private namespace. That way you might (I have not thought this one through like securing the interpreter for embedding) be able to get what you need to safely pass in Python code through the globals of the code being executed. Brett, this is exactly what I do. You also need to restrict func_code. The patch is simply for closing the other loopholes: type.__subclasses__, GeneratorType.gi_frame and gi_code. All possible in a patch of 6 lines of code thanks to Python's existing restricted framework in the interpreter. Please review and accept =) * http://codereview.appspot.com/20051 * http://codereview.appspot.com/21051 Thanks! -- love, tav plex:espians/tav | t...@espians.com | +44 (0) 7809 569 369 http://tav.espians.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/flashk%40gmail.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! (was: Reviving restricted mode)
Farshid Lashkari flashk at gmail.com writes: It seems like some code in safelite passes a file object to isinstance. By overriding the builtin isinstance function I can get access to the original file object and create a new one. Here is the code I used: I guess Tav should open a restaurant :-) ___ 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! (was: Reviving restricted mode)
Le Tuesday 24 February 2009 00:51:25 Farshid Lashkari, vous avez écrit : It seems like some code in safelite passes a file object to isinstance. By overriding the builtin isinstance function I can get access to the original file object and create a new one. Wow, excellent idea! -- 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! (was: Reviving restricted mode)
On Mon, Feb 23, 2009 at 4:06 PM, Victor Stinner victor.stin...@haypocalc.com wrote: Le Tuesday 24 February 2009 00:51:25 Farshid Lashkari, vous avez écrit : It seems like some code in safelite passes a file object to isinstance. By overriding the builtin isinstance function I can get access to the original file object and create a new one. Wow, excellent idea! I think in the next version Tav will have to stop the sharing of __builtins__ between the supervisor and the sandboxed code. There are too many tricks you can play with this. -- --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! (was: Reviving restricted mode)
Hey all, victor Could you keep all versions of safelite.py? I took Steven D'Aprano's advice and added a VERSION attribute and state the latest version on http://tav.espians.com/a-challenge-to-break-python-security.html Is that okay? antoine I guess Tav should open a restaurant :-) Hehe!! Thankfully I only offered to it to the first person *phew!* farshid It seems like some code in safelite passes farshid a file object to isinstance. By overriding the farshid builtin isinstance function I can get access to frashid the original file object and create a new one. Farshid, this is beautiful!!! Thank you -- it's very nicely done!! Do you have a website I could link to from the blog article? guido I think in the next version Tav will have to stop guido the sharing of __builtins__ between the supervisor guido and the sandboxed code. There are too many guido tricks you can play with this. Done. The common pattern that arised out of the various builtins-overriding-hacks is that safe code should *never* make assumptions about the state of the globals. The use of closures seems to fix this problem with an easily-auditable design pattern. -- 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