building and breaking a python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfexamples in...

82
Building and breaking a Python sandbox

Upload: others

Post on 21-Jul-2020

3 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Building and breaking a Python sandbox

Page 2: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Director

Organizer

@jessicamckellarhttp://jesstess.com

Page 3: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Why?

• Learning a language

• Providing a hosted scratch pad

• Distributed computation

• Inspecting running processes safely

Page 4: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Examples in the wild

• Seattle’s peer-to-peer computing network

• Google App Engine’s Python shell

• Codecademy’s empythoned

• CheckIO.org’s online coding game

Page 5: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s
Page 6: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Building a sandbox

• Language-level sandboxing (pysandbox)

• OS-level sandboxing (PyPy’s sandbox)

Page 7: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Question: How do we execute arbitrary code?

Page 8: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

How do we execute arbitrary code?

exec: compiles and evaluates statements

>>> exec "print 'Hello world'"Hello world

eval: compiles and evaluates expressions

>>> eval("1 + 2")3

Page 9: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

class Sandbox(object): def execute(self, code_string): exec code_string

sandbox.py

Page 10: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ print "Hello world!" """

s.execute(code)

test_sandbox.py

Page 11: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

$ python test_sandbox.pyHello world!

from sandbox import Sandbox

s = Sandbox()

code = """ print "Hello world!" """

s.execute(code)

Page 12: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

What should we disallow?

Page 13: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

What should we disallow?

• Resource exhaustion

• Information disclosure

• Running unexpected services

• Disabling/quitting/erroring out of the sandbox

Page 14: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ file("test.txt", "w").write("Kaboom!\\n")"""

s.execute(code)

Page 15: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> __builtins__.__dict__.keys()['bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'unicode', 'UnicodeDecodeError', 'memoryview', 'isinstance', 'copyright', 'NameError', 'BytesWarning', 'dict', 'input', 'oct', 'bin', 'SystemExit', 'StandardError', 'format', 'repr', 'sorted', 'False', 'RuntimeWarning', 'list', 'iter', 'reload', 'Warning', '__package__', 'round', 'dir', 'cmp', 'set', 'bytes', 'reduce', 'intern', 'issubclass', 'Ellipsis', 'EOFError', 'locals', 'BufferError', 'slice', 'FloatingPointError', 'sum', 'getattr', 'abs', 'exit', 'print', 'True', 'FutureWarning', 'ImportWarning', 'None', 'hash', 'ReferenceError', 'len', 'credits', 'frozenset', '__name__', 'ord', 'super', '_', 'TypeError', 'license', 'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError', 'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', 'StopIteration', 'globals', 'divmod', 'enumerate', 'apply', 'LookupError', 'open', 'quit', 'basestring', 'UnicodeError', 'zip', 'hex', 'long', 'next', 'ImportError', 'chr', 'xrange', 'type', '__doc__', 'Exception', 'tuple', 'UnicodeTranslateError', 'reversed', 'UnicodeEncodeError', 'IOError', 'hasattr', 'delattr', 'setattr', 'raw_input', 'SyntaxWarning', 'compile', 'ArithmeticError', 'str', 'property', 'GeneratorExit', 'int', '__import__', 'KeyError', 'coerce', 'PendingDeprecationWarning', 'file', 'EnvironmentError', 'unichr', 'id', 'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'any', 'complex', 'bool', 'ValueError', 'NotImplemented', 'map', 'buffer', 'max', 'object', 'TabError', 'callable', 'ZeroDivisionError', 'eval', '__debug__', 'IndentationError', 'AssertionError', 'classmethod', 'UnboundLocalError', 'NotImplementedError', 'AttributeError', 'OverflowError']

Page 16: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> __builtins__.__dict__.keys()['bytearray', 'IndexError', 'all', 'help', 'vars', 'SyntaxError', 'unicode', 'UnicodeDecodeError', 'memoryview', 'isinstance', 'copyright', 'NameError', 'BytesWarning', 'dict', 'input', 'oct', 'bin', 'SystemExit', 'StandardError', 'format', 'repr', 'sorted', 'False', 'RuntimeWarning', 'list', 'iter', 'reload', 'Warning', '__package__', 'round', 'dir', 'cmp', 'set', 'bytes', 'reduce', 'intern', 'issubclass', 'Ellipsis', 'EOFError', 'locals', 'BufferError', 'slice', 'FloatingPointError', 'sum', 'getattr', 'abs', 'exit', 'print', 'True', 'FutureWarning', 'ImportWarning', 'None', 'hash', 'ReferenceError', 'len', 'credits', 'frozenset', '__name__', 'ord', 'super', '_', 'TypeError', 'license', 'KeyboardInterrupt', 'UserWarning', 'filter', 'range', 'staticmethod', 'SystemError', 'BaseException', 'pow', 'RuntimeError', 'float', 'MemoryError', 'StopIteration', 'globals', 'divmod', 'enumerate', 'apply', 'LookupError', 'open', 'quit', 'basestring', 'UnicodeError', 'zip', 'hex', 'long', 'next', 'ImportError', 'chr', 'xrange', 'type', '__doc__', 'Exception', 'tuple', 'UnicodeTranslateError', 'reversed', 'UnicodeEncodeError', 'IOError', 'hasattr', 'delattr', 'setattr', 'raw_input', 'SyntaxWarning', 'compile', 'ArithmeticError', 'str', 'property', 'GeneratorExit', 'int', '__import__', 'KeyError', 'coerce', 'PendingDeprecationWarning', 'file', 'EnvironmentError', 'unichr', 'id', 'OSError', 'DeprecationWarning', 'min', 'UnicodeWarning', 'execfile', 'any', 'complex', 'bool', 'ValueError', 'NotImplemented', 'map', 'buffer', 'max', 'object', 'TabError', 'callable', 'ZeroDivisionError', 'eval', '__debug__', 'IndentationError', 'AssertionError', 'classmethod', 'UnboundLocalError', 'NotImplementedError', 'AttributeError', 'OverflowError']

Page 17: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

How do we disallow execution of

problematic builtins?

Page 18: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Idea: keyword blacklist

Page 19: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

class Sandbox(object): def execute(self, code_string): keyword_blacklist = ["file", "open", "eval", "exec"] for keyword in keyword_blacklist: if keyword in code_string: raise ValueError("Blacklisted") exec code_string

Idea: keyword blacklist

Page 20: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ file("test.txt", "w").write("Kaboom!\\n")"""

s.execute(code)

Testing: keyword blacklist

Page 21: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ file("test.txt", "w").write("Kaboom!\\n")"""

s.execute(code)

$ python test_sandbox.pyTraceback (most recent call last): File "test_sandbox.py", line 11, in <module> s.execute(code) File "/Users/jesstess/Desktop/sandbox/sandbox.py", line 86, in execute raise ValueError("Blacklisted")ValueError: Blacklisted

Testing: keyword blacklist

Page 22: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

How can we get around a keyword

blacklist?

Page 23: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Circumvention idea: encryption

func = __builtins__["file"]func("test.txt", "w").write("Kaboom!\n")

Page 24: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Circumvention idea: encryption

func = __builtins__["file"]func("test.txt", "w").write("Kaboom!\n")

func = __builtins__["svyr".decode("rot13")]func("test.txt", "w").write("Kaboom!\n")

Page 25: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ func = __builtins__["svyr".decode("rot13")] func("test.txt", "w").write("Kaboom!\\n")"""

s.execute(code)

Testing: keyword blacklist

Kaboom

Page 26: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Observation: if I can get a reference to something

bad, I can invoke it.

Page 27: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

How can we remove all references to

problematic builtins?

Page 28: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Idea: builtins whitelist

Page 29: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

builtins_whitelist = set(( # exceptions 'ArithmeticError', 'AssertionError', 'AttributeError', ... # constants 'False', 'None', 'True', ... # types 'basestring', 'bytearray', 'bytes', 'complex', 'dict', ... # functions '__import__', 'abs', 'all', 'any', 'apply', 'bin', 'bool', ... # block: eval, execfile, file, quit, exit, reload, etc.))

Page 30: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

import sys

main = sys.modules["__main__"].__dict__orig_builtins = main["__builtins__"].__dict__builtins_whitelist = set(( ... ))

for builtin in orig_builtins.keys(): if builtin not in builtins_whitelist: del orig_builtins[builtin]

Page 31: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ file("test.txt", "w").write("Kaboom!\\n")"""

s.execute(code)

Testing: builtins whitelist

Page 32: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ file("test.txt", "w").write("Kaboom!\\n")"""

s.execute(code)

$ python test_sandbox.pyTraceback (most recent call last): File "test_sandbox.py", line 9, in <module> s.execute(code) ... File "<string>", line 2, in <module>NameError: name 'file' is not defined

Testing: builtins whitelist

Page 33: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Circumvention idea: import something

dangerous

Page 34: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ import os fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY) os.write(fd, "Kaboom!\\n")"""

s.execute(code)

Testing: builtins whitelist

Kaboom

Page 35: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

How do we disallow problematic imports?

Page 36: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Idea: import whitelist

Page 37: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> importer = __builtins__.__dict__.get("__import__")>>> os = importer("os")>>> os<module 'os' from '/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/os.pyc'>>>> os.getcwd()'/Users/jesstess/Desktop/sandbox'

Idea: import whitelist

How does importing a module work in Python?

Page 38: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> help(__builtins__.__dict__["__import__"])

__import__(...) __import__(name, globals={}, locals={}, fromlist=[], level=-1) -> module

Idea: import whitelist

What is the expected function signature for the importer?

Page 39: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> def my_importer(module_name, globals={}, ... locals={}, fromlist=[], ... level=-1):... print "Using my importer!"... return __import__(module_name, globals, ... locals, fromlist, level)... >>> os = my_importer("os")Using my importer!>>> os.getcwd()'/Users/jesstess/Desktop/sandbox'

Idea: import whitelist

Cool, let’s write our own importer

Page 40: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

def _safe_import(__import__, module_whitelist): def safe_import(module_name, globals={}, locals={}, fromlist=[], level=-1):! if module_name in module_whitelist: return __import__(module_name,! ! ! ! ! ! ! ! ! ! ! ! globals, locals, fromlist, level) else: raise ImportError( "Blocked import of %s" ( module_name,)) return safe_import

Page 41: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

import sys

main = sys.modules["__main__"].__dict__orig_builtins = main["__builtins__"].__dict__

for builtin in orig_builtins.keys(): if builtin not in builtins_whitelist: del original_builtins[builtin]

safe_modules = ["string", "re"]orig_builtins["__import__"] = _safe_import( __import__, safe_modules)

Page 42: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ import os fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY) os.write(fd, "Kaboom!\\n")"""

s.execute(code)

Testing: import whitelist

Page 43: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ import os fd = os.open("test.txt", os.O_CREAT|os.O_WRONLY) os.write(fd, "Kaboom!\\n")"""

s.execute(code)

Testing: import whitelist

$ python test_sandbox.pyTraceback (most recent call last): File "test_sandbox.py", line 11, in <module> ... raise ImportError("Blocked import of %s" % (module_name,))ImportError: Blocked import of os

Page 44: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Circumvention idea: modifying builtins

Page 45: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Idea: make builtins read-only

Page 46: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

How can we make an object read-only in

Python?

Page 47: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

class ReadOnlyBuiltins(dict): def __delitem__(self, key): ValueError("Read-only!")

def pop(self, key, default=None): ValueError("Read-only!")

def popitem(self): ValueError("Read-only!") ... def setdefault(self, key, value): ValueError("Read-only!")

def __setitem__(self, key, value): ValueError("Read-only!")

def update(self, dict, **kw): ValueError("Read-only!")

Page 48: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

main = sys.modules["__main__"].__dict__orig_builtins = main["__builtins__"].__dict__

for builtin in orig_builtins.keys(): if builtin not in builtins_whitelist: del original_builtins[builtin]

safe_modules = ["string", "re"]orig_builtins["__import__"] = _safe_import( __import__, safe_modules)

safe_builtins = ReadOnlyBuiltins( original_builtins)main["__builtins__"] = safe_builtins

Page 49: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Observation redux: if I can get a reference to something bad, I can

invoke it.

Page 50: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Circumvention idea: exploiting the

inheritance hierarchy

Page 51: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> dir([])['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__delslice__', '__doc__', '__eq__', '__format__', '__ge__', ...]>>> [].__class__<type 'list'>

What can we find out about an object’s base classes?

Page 52: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> [].__class__<type 'list'>>>> [].__class__.__bases__(<type 'object'>,)>>> [].__class__.__bases__[0]<type 'object'>

What can we find out about an object’s base classes?

list subclasses object

Page 53: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> [].__class__.__subclasses__()[]>>> int.__subclasses__()[<type 'bool'>]>>> basestring.__subclasses__()[<type 'str'>, <type 'unicode'>]

What can we find out about an object’s subclasses?

subclasses of basestring

Page 54: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> [].__class__.__bases__(<type 'object'>,)>>> [].__class__.__bases__[0]<type 'object'>

>>> [].__class__.__bases__[0].__subclasses__()[<type 'type'>, <type 'weakref'>, <type 'weakcallableproxy'>, <type 'weakproxy'>, <type 'int'>, <type 'basestring'>, <type 'bytearray'>, <type 'list'>, <type 'NoneType'>, <type 'NotImplementedType'>, <type 'traceback'>, <type 'super'>, <type 'xrange'>, <type 'dict'>, <type 'set'>, <type 'slice'>, <type 'staticmethod'>, <type 'complex'>, <type 'float'>, <type 'buffer'>, <type 'long'>, <type 'frozenset'>, <type 'property'>, <type 'memoryview'>, <type 'tuple'>, <type 'enumerate'>, <type 'reversed'>, <type 'code'>, <type 'frame'>, <type 'builtin_function_or_method'>, <type 'instancemethod'>, <type 'function'>, <type 'classobj'>, <type 'dictproxy'>, <type 'generator'>, <type 'getset_descriptor'>, <type 'wrapper_descriptor'>, <type 'instance'>, <type 'ellipsis'>, <type 'member_descriptor'>, <type 'file'>, <type 'PyCapsule'>, <type 'cell'>, <type 'callable-iterator'>, <type 'iterator'>, <type 'sys.long_info'>, <type 'sys.float_info'>, <type 'EncodingMap'>, <type 'fieldnameiterator'>, <type 'formatteriterator'>, <type 'sys.version_info'>, <type 'sys.flags'>, <type 'exceptions.BaseException'>, <type 'module'>, <type 'imp.NullImporter'>, <type 'zipimport.zipimporter'>, <type 'posix.stat_result'>, <type 'posix.statvfs_result'>, <class 'warnings.WarningMessage'>, <class 'warnings.catch_warnings'>, <class '_weakrefset._IterationGuard'>, <class '_weakrefset.WeakSet'>, <class '_abcoll.Hashable'>, <type 'classmethod'>, <class '_abcoll.Iterable'>, <class '_abcoll.Sized'>, <class '_abcoll.Container'>, <class '_abcoll.Callable'>, <class 'site._Printer'>, <class 'site._Helper'>, <type '_sre.SRE_Pattern'>, <type '_sre.SRE_Match'>, <type '_sre.SRE_Scanner'>, <class 'site.Quitter'>, <class 'codecs.IncrementalEncoder'>, <class 'codecs.IncrementalDecoder'>, <class 'string.Template'>, <class 'string.Formatter'>, <type 'operator.itemgetter'>, <type 'operator.attrgetter'>, <type 'operator.methodcaller'>, <type 'collections.deque'>, <type 'deque_iterator'>, <type 'deque_reverse_iterator'>, <type 'itertools.combinations'>, <type 'itertools.combinations_with_replacement'>, <type 'itertools.cycle'>, <type 'itertools.dropwhile'>, <type 'itertools.takewhile'>, <type 'itertools.islice'>, <type 'itertools.starmap'>, <type 'itertools.imap'>, <type 'itertools.chain'>, <type 'itertools.compress'>, <type 'itertools.ifilter'>, <type 'itertools.ifilterfalse'>, <type 'itertools.count'>, <type 'itertools.izip'>, <type 'itertools.izip_longest'>, <type 'itertools.permutations'>, <type 'itertools.product'>, <type 'itertools.repeat'>, <type 'itertools.groupby'>, <type 'itertools.tee_dataobject'>, <type 'itertools.tee'>, <type 'itertools._grouper'>, <type '_thread._localdummy'>, <type 'thread._local'>, <type 'thread.lock'>, <class 'sandbox.Protection'>, <type 'resource.struct_rusage'>, <class 'sandbox.config.SandboxConfig'>, <class 'sandbox.proxy.ReadOnlySequence'>, <class 'sandbox.sandbox_class.Sandbox'>, <class 'sandbox.restorable_dict.RestorableDict'>]

All of the subclasses of object!

Page 55: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> [].__class__.__bases__(<type 'object'>,)>>> [].__class__.__bases__[0]<type 'object'>

>>> obj_class = [].__class__.__bases__[0]>>> for c in obj_class.__subclasses__():... print c.__name__...wrapper_descriptorinstanceellipsismember_descriptorfilePyCapsulecellcallable-iteratoriterator...

Page 56: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> [].__class__.__bases__(<type 'object'>,)>>> [].__class__.__bases__[0]<type 'object'>

>>> obj_class = [].__class__.__bases__[0]>>> for c in obj_class.__subclasses__():... print c.__name__...wrapper_descriptorinstanceellipsismember_descriptorfilePyCapsulecellcallable-iteratoriterator...

!!!

Page 57: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from sandbox import Sandbox

s = Sandbox()

code = """ obj_class = [].__class__.__bases__[0]obj_subclasses = dict((elt.__name__, elt) for \ elt in obj_class.__subclasses__())func = obj_subclasses["file"]func("text.txt", "w").write("Kaboom!\\n")"""

s.execute(code)

Testing: read-only builtins

Kaboom

Page 58: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Idea: don’t expose dangerous

implementation details

Page 59: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> type.__bases__(<type 'object'>,)>>> del type.__bases__

Let’s delete __bases__ and __subclasses__

Page 60: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> type.__bases__(<type 'object'>,)>>> del type.__bases__Traceback (most recent call last): File "<stdin>", line 1, in <module>TypeError: can't set attributes of built-in/extension type 'type'

Let’s delete __bases__ and __subclasses__

Imposed by the underlying C implementation!

Page 61: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from ctypes import pythonapi, POINTER, py_object

_get_dict = pythonapi._PyObject_GetDictPtr_get_dict.restype = POINTER(py_object)_get_dict.argtypes = [py_object]del pythonapi, POINTER, py_object

def dictionary_of(ob): dptr = _get_dict(ob) return dptr.contents.value

cpython.py

Let’s delete __bases__ and __subclasses__

Page 62: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from cpython import dictionary_of

main = sys.modules["__main__"].__dict__

...

safe_builtins = ReadOnlyBuiltins( original_builtins)main["__builtins__"] = safe_builtins

type_dict = dictionary_of(type)del type_dict["__bases__"]del type_dict["__subclasses__"]

Page 63: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Circumvention idea: would a function by any

other name smell as sweet?

Page 64: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> def foo():... print "Meow"... >>> dir(foo)['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

Page 65: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> def foo():... print "Meow"... >>> dir(foo)['__call__', '__class__', '__closure__', '__code__', '__defaults__', '__delattr__', '__dict__', '__doc__', '__format__', '__get__', '__getattribute__', '__globals__', '__hash__', '__init__', '__module__', '__name__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'func_closure', 'func_code', 'func_defaults', 'func_dict', 'func_doc', 'func_globals', 'func_name']

???

Page 66: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> foo.func_code<code object foo at 0x100509d30, file "<stdin>", line 1>>>> dir(foo.func_code)['__class__', '__cmp__', '__delattr__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'co_argcount', 'co_cellvars', 'co_code', 'co_consts', 'co_filename', 'co_firstlineno', 'co_flags', 'co_freevars', 'co_lnotab', 'co_name', 'co_names', 'co_nlocals', 'co_stacksize', 'co_varnames']>>> foo.func_code.co_code'd\x01\x00GHd\x00\x00S'

Page 67: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

>>> def foo():... print "Meow"... >>> def evil_function():... print "Kaboom!"... >>> foo()Meow>>> foo.__setattr__("func_code", evil_function.func_code)>>> foo()Kaboom!

Kaboom

Page 68: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Idea redux: don’t expose dangerous

implementation details

Page 69: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

from cpython import dictionary_offrom types import FunctionType

...

type_dict = dictionary_of(type)del type_dict["__bases__"]del type_dict["__subclasses__"]

function_dict = dictionary_of(FunctionType)del function_dict["func_code"]

Delete func_code

Page 70: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Whew. Let’s recap tactics:• Keyword blacklist

• Builtins whitelist

• Import whitelist

• Making important objects read-only (builtins)

• Deleting problematic implementation details (__bases__, __subclasses__, func_code)

• Deleting the ability to construct arbitrary code objects

Page 71: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

We have run out of tricks!

We’ve implemented 80% of a full-fledged Python sandbox

Page 72: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

builtins_whitelist = set(( # exceptions 'ArithmeticError', 'AssertionError', 'AttributeError', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'EnvironmentError', 'Exception', 'FloatingPointError','FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError','LookupError', 'MemoryError', 'NameError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError','PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'TabError', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError','UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', # constants 'False', 'None', 'True', '__doc__', '__name__', '__package__', 'copyright', 'license', 'credits', # types 'basestring', 'bytearray', 'bytes', 'complex', 'dict', 'float', 'frozenset', 'int', 'list', 'long', 'object', 'set', 'str', 'tuple', 'unicode', # functions '__import__', 'abs', 'all', 'any', 'apply', 'bin', 'bool', 'buffer', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'delattr', 'dir', 'divmod', 'enumerate', 'filter', 'format', 'getattr', 'globals', 'hasattr', 'hash', 'hex', 'id', 'isinstance', 'issubclass', 'iter', 'len', 'locals', 'map', 'max', 'min', 'next', 'oct', 'ord', 'pow', 'print', 'property', 'range', 'reduce', 'repr', 'reversed', 'round', 'setattr', 'slice', 'sorted', 'staticmethod', 'sum', 'super', 'type', 'unichr', 'vars', 'xrange', 'zip', ))

def _safe_import(__import__, module_whitelist): def safe_import(module_name, globals={}, locals={}, fromlist=[], level=-1): if module_name in module_whitelist: return __import__(module_name, globals, locals, fromlist, level) else: raise ImportError("Blocked import of %s" % (module_name,)) return safe_import

class ReadOnlyBuiltins(dict): def clear(self): ValueError("Read-only!")

def __delitem__(self, key): ValueError("Read-only!")

def pop(self, key, default=None): ValueError("Read-only!")

def popitem(self): ValueError("Read-only!")

def setdefault(self, key, value):! ValueError("Read-only!")

def __setitem__(self, key, value): ValueError("Read-only!")

def update(self, dict, **kw): ValueError("Read-only!")

class Sandbox(object): def __init__(self):! import sys! from types import FunctionType! from cpython import dictionary_of

! original_builtins = sys.modules["__main__"].__dict__["__builtins__"].__dict__

! for builtin in original_builtins.keys(): if builtin not in builtins_whitelist:! ! del sys.modules["__main__"].__dict__["__builtins__"].__dict__[builtin]

original_builtins["__import__"] = _safe_import(__import__, ["string", "re"]) safe_builtins = ReadOnlyBuiltins(original_builtins) sys.modules["__main__"].__dict__["__builtins__"] = safe_builtins

! type_dict = dictionary_of(type)! del type_dict["__bases__"]! del type_dict["__subclasses__"]

! function_dict = dictionary_of(FunctionType)! del function_dict["func_code"]

def execute(self, code_string):! exec code_string

builtins whitelist

import whitelist

read-only builtins

deleting __bases__, __subclasses_, and func_code

Page 73: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Building a sandbox

• Language-level sandboxing (pysandbox)

• OS-level sandboxing (PyPy’s sandbox)

Page 74: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

What should we disallow?

• Resource exhaustion

• Information disclosure

• Running unexpected services

• Disabling/quitting/erroring out of the sandbox

Page 75: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Food for thought

Page 76: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Is this level of reflectiveness good

or bad?

Page 77: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Do other languages have these sandboxing

concerns?

Page 78: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

If you were designing a new language, how would you do this?

Page 79: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Experiments

• How does an alternative Python implementation like PyPy handle these issues?

• How does the CPython interpreter compile and run bytecode?

• What does the Python stack look like?

• How do ctypes work?

• How can the operating system help provide a secure environment?

Page 80: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Bedtime reading

• The full pysandbox implementation: https://github.com/haypo/pysandbox/

• A retrospective on pysandbox’s challenges: https://lwn.net/Articles/574215/

• PyPy’s sandbox implementation: http://pypy.readthedocs.org/en/latest/sandbox.html

• How PythonAnywhere’s sandbox works: http://blog.pythonanywhere.com/83/

Page 81: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Thank you!

Page 82: Building and breaking a Python sandboxweb.mit.edu/jesstess/www/pytennessee_sandbox.pdfExamples in the wild • Seattle’s peer-to-peer computing network • Google App Engine’s

Let’s talk!O’Reilly booth, 3pm

Thank you!