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. 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. 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. 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. How to Sandbox?
OS Level
• Linux seccomp
• PyPy Sandboxing
Language Level/In-Process*
• PySandbox
• rexec
Don’t use those examples @Home/Production
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. Shit Can Happen…
• Resource exhaustion – DoS
• Information disclosure
• Server takeover
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
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. 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. 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. 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. 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. Whitelisting Imports
• Ever wondered how do Python imports work?
importer = __builtins__.__dict__.get('__import__')
os = importer('os')
• And how to roll your own?
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__()
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.
24. Questions Time!
How many interactive Python interpreters were
harmed while preparing this talk?