Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Thinking Outside The [Sand]Box

A 40 minute talk about sandboxes, Python, creating Python in process sandboxes, and defeating them. (Based in part on PySandbox).

  • Login to see the comments

Thinking Outside The [Sand]Box

  1. 1. Thinking Outside the [Sand]Box
  2. 2. >>> dir(self) • Michael Genkin • A computer engineer • A researcher • A jack of many trades • And a master of some • Prefers Python [2.7] to your favorite programming language since 2008. • Isn’t afraid of the bytecode.
  3. 3. Outline • Sandboxes – how & why? • A bit of Python • Code execution • __builtins__ • Python Sandbox – HowTo & Examples • Blacklisting • Whitelisting • Modifying __builtins__ • If time allows • CPython implementation details • Code objects
  4. 4. What’s a Sandbox? “A security mechanism for separating running programs. It is often used to execute untested code, or untrusted programs from unverified third parties, suppliers, untrusted users and untrusted websites. The sandbox typically provides a tightly controlled set of resources for guest programs to run in…” [Wikipedia]
  5. 5. Why a Sandbox? • UNTRUSTED CODE? Why we’d ever want to execute untrusted code? • Learning platform • A certain challenge site • Development environment as a Service
  6. 6. How to Sandbox? OS Level • Linux seccomp • PyPy Sandboxing Language Level/In-Process* • PySandbox • rexec Don’t use those examples @Home/Production
  7. 7. A Bit of Python… Quick detour
  8. 8. Code Execution in Python • How does one execute untrusted code? • Or simply dynamically generated code… • A few ways… • exec(file) – compile & execute a statement (or a file). • eval – compile & execute an expression. • if you really need eval – try using ast.literal_eval() • os.exec* – create & execute a new shell • subprocess... • pickle – a minefield • Don’t do this at home..! • Really. Don’t. Ever.
  9. 9. Shit Can Happen… • Resource exhaustion – DoS • Information disclosure • Server takeover
  10. 10. Tools of Chaos • file/open • Though we might need those… • eval/exec(file) • exit/quit • pickle/os/subprocess • We might need those as well
  11. 11. Nice to Meet You __builtins__ >>> print dir(__builtins__) ['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BufferError', 'BytesWarning', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'NameError', 'None', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'ReferenceError', 'RuntimeError', 'RuntimeWarning', 'StandardError', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '_', '__debug__', '__doc__', '__import__', '__name__', '__package__', 'abs', 'all', 'any', 'apply', 'basestring', 'bin', 'bool', 'buffer', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'cmp', 'coerce', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'execfile', 'exit', 'file', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'intern', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'long', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'raw_input', 'reduce', 'reload', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'unichr', 'unicode', 'vars', 'xrange', 'zip']
  12. 12. We Need a Sandbox… A Builder & Breaker How-To
  13. 13. An Optimal [Python] Sandbox class Sandbox(object): def __make_secure(self, unsafecode): """ Black Magic """ return safecode def execute(self, code): exec self.__make_secure(code) if __name__ == '__main__': s = Sandbox() s.execute("print 'Hello World!'") # Hello World! s.execute("*bad stuff*") # RuntimeException • How does this *black magic* really looks like?
  14. 14. Blacklisting __builtins__ def __make_secure(self, unsafecode): keyword_blacklist = ["file", "quit", "eval", "exec", "execfile", "exit"] for keyword in keyword_blacklist: if keyword in unsafecode: raise ValueError("Blacklisted") return unsafecode
  15. 15. Circumventing a Blacklist • The problem with blacklist is that they’re always incomplete… • What isn’t in the blacklist? s.execute(""" __builtins__.__dict__["ZXZhbA==".decode("base64")](*bad stuff*) """) • Lesson learned… • If we can get a reference to something – we can invoke it.
  16. 16. Whitelisting __builtins__ import sys def __make_secure(self, unsafecode): # Blacklisting code main = sys.modules["__main__"].__dict__ orig_builtins = main["__builtins__"].__dict__ builtins_whitelist = set(( 'ArithmeticError', 'AssertionError', 'AttributeError', ... # Exceptions 'False', 'None', 'True', ... # Constants 'basestring', 'bytearray', 'bytes', 'complex', 'dict', ... # Types '__import__', 'abs', 'all', 'any', 'apply', 'bin', 'bool', ... # Functions # Block: eval, execfile, file, quit, exit, reload, etc. )) for builtin in orig_builtins.keys(): if builtin not in builtins_whitelist: del orig_builtins[builtin] return unsafecode # No way to do bad stuff now... s.execute('__builtins__.__dict__["ZXZhbA==".decode("base64")](*bad stuff*)') # NameError
  17. 17. I brought This Little Something… • The whitelist insures we don’t have anything useful in scope… • But, can we bring more stuff into the scope? s.execute(""" import os os.exec("python -c '*something bad*'") """) • Lesson learned… • Whitelisting __builtins__ isn’t enough if the attacker can just import stuff
  18. 18. Whitelisting Imports • Ever wondered how do Python imports work? importer = __builtins__.__dict__.get('__import__') os = importer('os') • And how to roll your own?
  19. 19. Whitelisting Imports def safe_importer(module_name, globals={}, locals={}, fromlist=[], level=-1): print "You can't import anything bad now..." good_modules = ['string', 're', ...] # Doesn't include os, subprocess, or pickle! if module_name in good_modules: return __import__(module_name, globals, locals, fromlist, level) else: raise ImportError('You can't import this!') def __make_secure(self, unsafecode): # Blacklisting code # Whitelisting code orig_builtins['__import__'] = safe_importer s.execute(""" import os os.exec("python -c '*something bad*'") """) # ImportError
  20. 20. I Know I Left This Somewhere… • What do we have left? • Do we have anything useful left? • We have some types… let’s check them out • If we have a class – why not have a metaclass as well? • PEP 0253 - __bases__ & __subclasses__()
  21. 21. I Know I Left This Somewhere…
  22. 22. If We Have a Reference… s.execute(""" __builtins__.__dict__['__import__'] = ().__class__.__bases__[0].__subclasses__()[59]()._module.__builtins__['__import__'] import os os.exec("python -c '*something bad*'") """)
  23. 23. Questions Time! How many interactive Python interpreters were harmed while preparing this talk?
  24. 24. Thanks for listening!