Given the recent discussion, I thought the list might be interested in:
http://www.links.org/?p=1242. I'm currently working on transparently
wrapping libtiff (that is, wrapping it such that the calling application is
unaware it is wrapped).

Using Capsicum For Sandboxing <http://www.links.org/?p=1242>

FreeBSD 9.0 <http://www.freebsd.org/releases/9.0R/announce.html>, released
in January 2012, has experimental
Capsicum<http://www.cl.cam.ac.uk/research/security/capsicum/> support
in the kernel, disabled by default. In FreeBSD 10, Capsicum will be enabled
by default.

But unless code uses it, we get no benefit. So far, very little code uses
Capsicum, mostly just experiments we did for our paper. I figured it was
time to start changing that. Today, I’ll describe my first venture –
sandboxing bzip2 <http://www.bzip.org/>. I chose bzip2 partly because Ilya
Bakulin <https://github.com/kibab> had already done some of the
work<https://github.com/kibab/capsicum/blob/00546c31c6f9681bf045d31219c10a7d8e9e45e9/contrib/bzip2/bzip2.c>
for
me, but mostly because a common failure mode in modern software is mistakes
made in complicated bit twiddling code, such as decompressors and ASN.1
decoders.

These can often lead to buffer overflows or integer over/underflows – and
these often lead to remote code execution. Which is bad. bzip2 is no
stranger to this problem:
CVE-2010-0405<http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2010-0405>
describes
an integer overflow that could lead to remote code execution. The question
is: would Capsicum have helped – and if it would, how practical is it to
convert bzip2 to use Capsicum?

The answers are, respectively, “yes” and “fairly practical”.

First of all, how does Capsicum mitigate this problem? The obvious way to
defend a decompressor is to run the decompression engine in a separate
process with no privilege beyond that needed to get its job done – which is
the ability to read the input and write the output. In Capsicum, this is
easy to achieve: once the appropriate files are open, fork the process and
enter capability mode in the child. Discard all permissions except the
ability to read the input and write the output (in Capsicum, this means
close all other file descriptors and limit those two to read and write),
and then go ahead and decompress. Should there be a bug in the
decompressor, what does the attacker get? Well, pretty much what he had
already: the ability to read the input file (he supplied it, so no news
there!) and the ability to write arbitrary content to the output file (he
already had that, since he could have chosen arbitrary input and compressed
it). He also gets to burn CPU and consume memory. But that’s it – no access
to your files, the network, any other running process, or anything else
interesting.

I think that’s pretty neat.

But how hard is it to do? I answer that question in a series of diffs on
GitHub, showing a step-by-step transformation of bzip2 into the desired
form. I used a technique I like to call *error-driven development*; the
idea is you attempt to make changes that will cause compilation to fail
until you have completely accomplished your goal. This is a useful way to
reassure yourself that you have made all necessary updates and there’s
nothing hiding away you didn’t take care of. If you follow along by
building the various stages, you’ll see how it works.

It turns out that in bzip2 this matters – it isn’t very beautifully
written, and the code that looks like it might cleanly just take an input
file and an output file and do the work in isolation, actually interacts
with the rest of the code through various function calls and globals. This
causes a problem: once you’ve forked, those globals and functions are now
in the wrong process (i.e. the child) and so it is necessary to use RPC to
bridge any such things back to the parent process. Error-driven development
assures us that we have caught and dealt with all such cases.

So how did this work out in practice? Firstly, it turns out we have to give
the compressor a little more privilege: it writes to stderr if there are
problems, so we need to also grant write on stderr (note that we could
constrain what it writes with a bit more effort). The callbacks we have to
provide do not, I think, let it do anything interesting: cause the program
to exit, make the output file’s permissions match the input file’s, and
remove the input or output files (ok, removing the input file is slightly
interesting – but note that bzip2 does this anyway).

Secondly, because we have not yet decided on an RPC mechanism, this
particular conversion involves quite a bit of boilerplate: wrapping and
unwrapping arguments for RPCs, wiring them up and all that, all of which
would be vastly reduced by a proper RPC generator. Try not to let it put
you off [image: :-)]

Finally, the system has (at least) one global, errno. I did not deal with
that so far, which means some errors will report the wrong error – but it
is not particularly hard to do so.

So, on to the diffs. This is something of an experimental way to present a
piece of development, so I’d be interested in feedback. Here they are, in
order:

   - Step 1: move functions to be
wrapped.<https://github.com/benlaurie/freebsd/commit/922ce661a8555910d5234a87dcb376851848d130>
   - Step 2: add header for wrapped
functions.<https://github.com/benlaurie/freebsd/commit/499e5452213602c9311a22fb5065bb20f2db601d>
   - Step 3: wrap a function requiring
authority.<https://github.com/benlaurie/freebsd/commit/9960df3575231e3a580dd4032868929dd8239933>
   - Step 4: move functions that do not require
authority.<https://github.com/benlaurie/freebsd/commit/12efce8d2ef5f7032c9a8e95900f8e82adb16e5b>
   - Step 5: rename and invoke wrapped
functions.<https://github.com/benlaurie/freebsd/commit/21cbf9122dcbfdf0c9fafa1a98c3e88fa0009364>
   - Step 6: build other
files.<https://github.com/benlaurie/freebsd/commit/493fbb46501ce3876861d5cc4e3183affdbe4ae0>
   - Step 7: wrap a
global.<https://github.com/benlaurie/freebsd/commit/95166e7e820fa96b7c7bc322487db534bbe62731>
   - Step 8: fix similar
problems.<https://github.com/benlaurie/freebsd/commit/098d103768ad2b63c90f801e12c03597ecea718d>
   - Step 9: wrap a function in the
parent.<https://github.com/benlaurie/freebsd/commit/33c12a242c84f68dfd54fd25234ebe2fd67d7ee1>
   - Step 10: wrap a 2-way
global.<https://github.com/benlaurie/freebsd/commit/81b0c3c00d0a7111dd282428b314aaa8de5cefa2>
   - Step 11: continue to apply the
methodology.<https://github.com/benlaurie/freebsd/commit/1df47b5c640bf0db2c3ba18190bb3f89ec159ea5>
   - Step 12: get back on track with error-driven
development.<https://github.com/benlaurie/freebsd/commit/df1203313bc05160cb1fbdaddfc886fd398c2b1d>
   - Step 13: define
unwrappers.<https://github.com/benlaurie/freebsd/commit/5c5ef33fa97e678a89bb9777e9e7695574d72706>

And there you are: bzip2 is now rendered safe from decompressor exploits,
and it was only a few hours work. As we refine the support infrastructure,
it will be even less work.
_______________________________________________
Secure Coding mailing list (SC-L) SC-L@securecoding.org
List information, subscriptions, etc - http://krvw.com/mailman/listinfo/sc-l
List charter available at - http://www.securecoding.org/list/charter.php
SC-L is hosted and moderated by KRvW Associates, LLC (http://www.KRvW.com)
as a free, non-commercial service to the software security community.
Follow KRvW Associates on Twitter at: http://twitter.com/KRvW_Associates
_______________________________________________

Reply via email to