Use cases of ujail
Monday's post was rather technical, so let's have a look at possible use cases today.
The main reason for both having the idea of ujail and starting working on it is my web server. I am running quite a few (S)CGI scripts there and, even though running them as different users, on a per-vhost basis, I have the impression of the whole thing being a bit insecure.
Okay, PHP does provide its famous open_basedir feature, but I am also running some Python applications which I simply cannot restrict easily. My first ideas involved adding something similar to open_basedir to Python, followed by the idea of replacing some C library functions, like fopen and friends on startup time.
Whilst the adding open_basedir to Python would have involved changing a lot of Python's internals I soon discarded the library patching idea as those could be worked around by injected code directly invoking syscalls. It didn't take long for me to notice that I have to dig deeper. The idea of ujail was born and after coming up with the proof of concept this seems to be a viable solution.
Now ujail is not only about protecting a web server from its web applications, but could do a lot more, for example:
- Creating a sandbox for untrusted code (socket&file i/o emulation)
- Implementing some sort of personal firewall (socket-call only emulation)
- Testing applications that perform low-level system operations (read: package managers and friends, filesystem emulation)
FAQs
There have been some questions about ujail in comments to my first post which I would like to answer. Also, I have been thinking about things that are different about ujail compared to other virtualization techniques. Feel free to add additional questions either in a comment or drop me an email: debian at sp dot or dot at.
- Could you change the license of ujail to ... ?
Not likely to happen. The proof of concept's license is GPLv3 and the actual code's license will be too. However, ujail is a userspace application that does not need any modifications to the kernel so there should be no problems with porting ujail from GNU/Linux to any other system.
- Does ujail work on operating systems other than GNU/Linux?
Not yet. If it's technically possible to implement the technique on other operating systems I would be happy to accept patches.
- Do I need to patch my kernel for ujail to work?
No, ujail is running in userspace. The only thing it needs is Linux with support for PTRACE_SYSEMU.
- How is this approach different from using LD_PRELOAD?
With LD_PRELOAD one can replace library functions, but malicious code could still directly invoke syscalls, working around this protection completely. Also, statically linked binaries cannot be restricted with LD_PRELOAD.
- How is this approach different from user-mode-linux?
User-mode-linux (UML) works by emulating a full kernel in userspace and allows you to virtualize a whole Linux instance (including a new init process, etc). ujail is about providing a way of restricting a single process (and its childs) inside a running system in terms of access to syscalls and the partial emulation of those.
- How is this approach different from linux-vserver?
Linux-vserver is a kernel patch and runs in kernel space, as opposed to ujail, which works in userspace.
Also, linux-vserver works similarly to user-mode-linux, providing a fully virtualized Linux instance.
- Does the account running ujail need any special privileges?
No, the only restrictions that apply are those of ptrace.
- Where is the code?
Right now ujail is in a planning phase, and only the proof of concept code has been written and published. The actual ujail code is yet to be written and the code will be hosted on launchpad.net.
An anonymous person (who were you stranger?) added a comment to my first post, suggesting "Also, why patch the process rather than just modifying its state and trapping into the kernel?". I have had a look at this approach earlier, but it didn't work out. However, I decided to give it yet another try and created a second proof of concept. That code does not require patching any code, but only modifies the instruction pointer (eip) and the first register (eax). This should be a lot faster than patching the code.
Technically the new main loop works by calling PTRACE_SYSEMU and waiting for a notification. It then saves the instruction pointer and switches to PTRACE_SYSCALL. As before it waits for the emulated syscall to exit and at this point sets eax from orig_eax and decreases the value of the instruction pointer by the size of the "int $0x80" instruction. Another call to PTRACE_SYSCALL resumes the process. The next event is the process actually entering the real syscall and yet another one leaving the syscall again. These are resumed by PTRACE_SYSCALL and PTRACE_SYSEMU respectively. So, comparing this with the first approach we are only modifying two registers now, instead of writing to the TEXT area of the running process.
Thanks should go to the anonymous commenter for making me give this approach another try.
Questions? Criticism? More ideas? Want to contribute?
Coming to an end I would yet again like to let you know that I am open for questions, criticism, more ideas and contributions in general. So if you are interested in this topic come join the discussion by either dropping me an email, writing a comment to this post or replying to this post on your own blog.
You might want add comparison to plash to your FAQ. For example
ReplyDeletepython foo.py test1.txt test2.txt
in pola-shell will let foo.py only read the files test1.txt and test2.txt (There's a lot more syntax to specify what files can be written to).
Afaik plash works using a chroot and a modified glibc that can talk to the outside world using a unix socket. Via this unix socket it can prompt for my permissions and also ask the helper to open file descriptors for it, the file descriptors can then be passed via the unix socket so that they can be used in side the chroot.
Very cool and faster than tracing every syscall.
@lindi:
ReplyDeleteI totally agree with you that this approach is possibly faster than ujail. However, this can also be worked around by injecting code that invokes the syscalls directly. I also agree that this is hard to achieve from within python code, but not impossible, at least in theory.
Also, ujail should be a lot more flexible, as it applies to *any* binary, including the Python interpreter (and Perl, and machine-native code, and ...).
I will consider adding a comparison to plash the next time I update the FAQ.
Thanks for your input!
Geordi is an IRC bot that uses a ptrace-based supervisor to compile execute arbitrary code. The code is written in literate Haskell so could provide a good example of what you want to do. Alternatively, the supervisor section can even be used outside of IRC - indeed, codepad.org runs on precisely this code.
ReplyDelete@sp: Do you mean that a process could escape plash by making syscall directly? I thought the new UID/GID, chroot and iptables tricks were there to block file and network access. Of course you can try to exploit unknown linux bugs but can ujail really do much about those other than block some seldomly used syscalls completely?
ReplyDeleteHere is another use-case to consider: Sometimes I use LD_PRELOAD tricks to change the behavior of a closed-source process, e.g. if the process creates and then deletes a file, I can replace the unlink operation so that the deleted file gets moved to a temporary directory instead. This only works if they use shared libraries, while replacing the unlink system call would instead let me easily do this to any binary.
ReplyDelete3jAmY5a7: you can easily do that with subterfugue. It lets you write python code that can install handlers for syscalls.
ReplyDelete@lamby:
ReplyDeleteThanks for the hint. This is actually similar to what I am working on right now.
@lindi
Without having had a look at the exact details plash seems to rely on software using the C-libraries functions for accessing syscalls instead of calling them directly. I have not yet tried it myself, but if my assumption is correct limitations could be worked around by doing a syscall directly. With a chroot in place and the "iptables tricks" (haven't looked at them) it is possible that plash provides a similar level of security though.
ujail is not about protecting the system from bugs in syscall implementations, but rather providing a secure sandboxing environment.
On your note about subterfugue: according to what looks like its official website it has been unmaintained since 2001, so it's probably not an option.
@3jAmY5a7:
Yes, this should be possible to do with ujail, once I get the code ready (which is likely to take some time).