Bypassing a Python sandbox by abusing code objects
pbiernat.blogspot.com
pbiernat.blogspot.com
1. By completely reimplementing Python at a lower level and intercepting system calls, like PyPy's sandbox feature [1].
2. By utilizing other, more mature OS level constructs (IE: think containerization like LXC (which is imperfect), stacking OS features like seccomp/chroot/etc. or better, true virtualization like Xen).
Ideally, you'd combine them both and then run them on a "dumb box" which is just a REST API with no other keys or system access. The key to designing moderately secure sandboxes is just accepting that there are angles of attack you haven't considered yet, so design in layers.
If you find yourself inspecting code to decide if it is safe, you are fighting a losing battle... I'd love to hear of any success stories here though!
Great detailed write up of the pitfalls @op, thanks.
[0] https://github.com/haypo/pysandbox/ or https://lwn.net/Articles/574215/ [1] http://pypy.readthedocs.org/en/latest/sandbox.html
If you still insist on trying to build safe-code inspectors, be very skeptical of what your protection is capable of. For instance, why even bother blocking static strings? Such trivially-breakable "security" being placed in your code only serves to distract you and give false confidence.
Any comments or loopholes found would be appreciated...
NaCL, seL4 and the original proof-carrying code examples of packet filter are inspecting code, but on a very rigorous level.
Sandboxing based on blacklists is a recipe for disaster, and it's an unfortunate accident of history that the default Python implementation (CPython) makes whitelisting practically impossible. Luckily, there are alternatives as others in the thread have pointed out.
The approach there is to rewrite your Python code to disallow any potentially bad operation. E.g. your code cannot access identifiers starting with _ and the list of builtins is restricted (no "open" by default obviously, but also no "type"!)
Any sandbox code doing X.Y is rewritten to _your_getattr(X, "Y") which can decide whether to allow access or not at runtime.
The main thing to be careful of, is not accidentally injecting any callables like "file" into there, as just calling an object does not undergo a security check (given that you might want to pass some data INTO your sandbox).
This does not limit resource usage however, i.e. you can still try to allocate memory/use up CPU time.
"RestrictedPython was bad idea and mostly causes headache. Avoid through-the-web Zope scripts if possible."[1]
[1]http://docs.plone.org/develop/plone/security/sandboxing.html
> Restricted code doesn't actually protect against malicious users. It's only a best-effort attempt against introducing accidental security issues for semi-professional developers.
BTW, another attack vector (besides resource use) is to use code that causes Python itself to crash. For example, "{[{}]}".format({"{}": 5}) will segfault Python 3.3 (see http://bugs.python.org/issue17644 ).
The harder question is, are any of the ways to segfault Python exploitable?
I believe it is fairly secure, but I don't know if it's comparable to the ones you mentioned.
Of course, you make all kinds of sacrifices using ZeroVM. Nevertheless, it's pretty robust and lightweight.
I managed to get Python-on-ZeroVM running on Docker (ie, LXC) which gave me a pretty decently safe platform for running arbitrary code. It's atrophied since it was working, but I think it had some real potential.
Just use Lua if you need a sandboxed dynamically-typed environment.
It certainly seems like you could just ban a bunch of byte codes and easily detect them. I must be missing something.
The way the JVM works is by having specific permission checks at points within the code, and they are enforced by the JVM. I think the CLR works in a similar fashion.
You can run Python on top of the JVM and take advantage of this, but I think the version of Python (JYthon) is pretty old.
That's the approach I took with https://github.com/danthedeckie/simpleeval .
I did a web app test once for an application created by IBM. They offered the ability for administrators to run Python code in a 'restricted' sandbox to manipulate data. The app was Java so it was run through Jython, and they locked down all the usual suspects like open(), file() etc. But because it was Jython you could just use the Java io.* classes and bypass all their restrictions.
1. http://tomforb.es/breaking-out-of-secured-python-environment...
But you've already shown that you can build the string "func_code" using your xor code. So you can access the member using getattr/ setattr:
getattr(func, str_containing_func_code)
setattr(func, str_containing_func_code, new_code)