Untrusted code execution
Chris Angelico
rosuav at gmail.com
Tue Apr 5 14:14:58 EDT 2016
On Wed, Apr 6, 2016 at 3:52 AM, Ian Kelly <ian.g.kelly at gmail.com> wrote:
> On Tue, Apr 5, 2016 at 11:48 AM, Chris Angelico <rosuav at gmail.com> wrote:
>> Your code is a *lot* safer for using 'eval' rather than 'exec'.
>> Otherwise, you'd be easily exploited using exceptions, which carry a
>> ton of info. But even so, I would not bet money (much less the
>> security of my systems) on this being safe.
>
> Not to mention "import". :-P
Nah. That one's easy to blank out. Once you go to a restricted
builtins, the import statement breaks:
>>> def safe_exec(untrusted_code):
... tree = compile(untrusted_code, "<script>", "exec", ast.PyCF_ONLY_AST)
... for node in ast.walk(tree):
... if (isinstance(node, ast.Name) and node.id.startswith("_") or
... isinstance(node, ast.Attribute) and node.attr.startswith("_")):
... raise ValueError("Access to private values is not allowed.")
... namespace = {"__builtins__": {"int": int, "str": str, "len": len}}
... print(eval(compile(tree, "<script>", "exec"), namespace))
...
>>> safe_exec("import foo")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 8, in safe_exec
File "<script>", line 1, in <module>
ImportError: __import__ not found
But exceptions aren't blocked, and you can retrieve them and do all
manner of stuff. You can also create objects of various types using
literal/display syntax, and that might let you craft some weird
construct that effectively access those attributes without actually
having an attribute that starts with an underscore. (Think of
"getattr(x, '\x5f_class__')", although obviously it'll take more work
than that, since getattr itself isn't available.)
ChrisA
More information about the Python-list
mailing list