5 ms·
Bypassing a Python sandbox by abusing code objects
- zzleeper 12y agoI'm wondering if it's possible to get a simpler/better attempt at sandboxing by just inspecting the generated bytecode, instead of focusing on the .py text file..
- mrfusion 12y agoThat's a really good point. I'm eager to hear a response. It certainly seems like you could just ban a bunch of byte codes and easily detect them. I must be missing something.
- nl 12y agoGenerally speaking it's the data associated with the calls that is risky, not the bytecode itself. 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.
- deckiedan 12y agoThere's an in-between step, the "AST" (Abstract Syntax Tree), which is a better place to do this stuff than in the actual byte code. You still have context and a lot more useful information at that point, and can re-write the AST to use workarounds, etc. That's the approach I took with https://github.com/danthedeckie/simpleeval https://github.com/danthedeckie/simpleeval .
- bryanh 12y agoBuilding a truly safe Python sandbox (well, in CPython at least) is widely considered a fool's errand [0]. However, a Python sandbox can be relatively safely done in two ways: 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/ https://github.com/haypo/pysandbox/ or https://lwn.net/Articles/574215/ https://lwn.net/Articles/574215/ [1] http://pypy.readthedocs.org/en/latest/sandbox.html http://pypy.readthedocs.org/en/latest/sandbox.html
- usaar333 12y ago#2 alone - running items in an LXC jail without root access is far - is quite secure (and what we relied on at PiCloud to set up our python sandboxes). 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.
- bryanh 12y agoAs a rule, an LXC jail is definitely very much better than code-inspection, but it is worth taking the time reading up on some of the rather specific configuration needed to tighten up LXC/namespaces. Docker (previously DotCloud) obviously has a lot riding on this, so they are taking secure-by-default configuration pretty seriously. [0] [0] https://docs.docker.com/articles/security/ https://docs.docker.com/articles/security/
- deckiedan 12y agoI wrote a very locked down python "expression evaluator" library ( https://github.com/danthedeckie/simpleeval https://github.com/danthedeckie/simpleeval ) which uses code inspection and is reasonably safe (caveats listed in the README). Any comments or loopholes found would be appreciated...
- eru 12y ago> 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! NaCL, seL4 and the original proof-carrying code examples of packet filter are inspecting code, but on a very rigorous level.
- nhaehnle 12y agoNaCL is a good example that shows how inspecting code is not actually the mistake that was made by the Python sandbox that was attacked in the article. The mistake was that the inspection was based on a blacklist rather than a whitelist. 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.
- orf 12y agoVery interesting read, I did something similar[1] on a Microsoft site. I didn't think of creating a code object from scratch though, great idea. 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-environments http://tomforb.es/breaking-out-of-secured-python-environment...
- thu 12y agoI was able to connect to any database from a multi-tenant ERP written in Python. The idea was to get hold of the class of the connection object, and re-instanciate it, while passing a new database name in parameter. That ERP lets users (of different tenants) write Python code in various places. The kind of code you could write was already restricted but not sufficiently. Now all the dunder are forbidden, things like getattr() are also forbidden, which makes the trick used in that blog post not possible. One of the funny thing I had to do was to use lambdas to name intermediate values, because statements were prohibited.
- bobbyi_settv 12y ago> However, that requires accessing the "func_code" member, which is explicitly blocked. 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)
- ClintonMN 12y agoI found another way of doing this, we can use ruby codex with the sandbox, it worked for me like a charm, I will also try using the python sandbox. It sound relativly safer this way.