This is poorly tested and a quick write-up. It may not work for you. If you find a mistake, inform me on the uml user list
or fix this document yourself and leave a note in the changelog at the bottom.
DavePearson hacked together a shell script to ChrootWithPerl too, as compartment wasn't handy on gentoo, and perl was (as ever!) within reach.
If an attacker gets root inside a UML guest kernel, he may try to escape that guest kernel, for example by /dev/kmem patching
or other kernel intrusion techniques
. To minimize the impact, run the UML guest kernel as an unprivileged user (during this document: uid *=uml=*, gid *=uml=*, all filesystem permissions like *=w=* (_writable_) refer to those ids) and jail it inside a chroot environment. As the UML kernel binary is statically linked, a chroot jail only needs a few things:
Assume that you have a root filesystem image *=./rootfs.img=* and a UML kernel binary *=./linux-uml=*. As root, do this:
umask 027 mkdir -p chroot/proc chroot/dev/net chroot/tmp cp linux-uml chroot/linux mv rootfs.img chroot #(or copy or bindmount) touch chroot/proc/mm mount -o bind /proc/mm chroot/proc/mm cp /proc/cpuinfo chroot/proc/ mknod chroot/dev/net/tun c 10 200 chown -R root:uml chroot chmod g+w chroot/dev/net/tun chroot/tmp chroot/rootfs.img
Now if you use the *=chroot(1)=* to chroot into that jail, you have to be root. But you do not want to execute UML as root, and you do not want to install *=su(1)=* into the jail! So i looked for tools which chroot and drop privileges at the same time, there are several. I chose compartment
from SuSE's Marc Heuse. There are rpm packages over at Dag Wieers apt-repository
for Fedora Core and Redhat, i do not know other distros. *=compartment(1)=* works simple:
compartment --user uml --group uml --chroot ./chroot /linux uml_dir=/tmp ubd0=/rootfs.img [lots of UML options]
which means "run the binary *=/linux uml_dir...=* chrooted inside *=./chroot=* as uid *=uml=* and gid *=uml=*".
You can try to force the _principle of least privilege_ on the chroot as strict as possible. Assumed the attacker escaped the UML guest kernel and now runs code as user =uml= inside that chroot on the host. There shall be no devices, no directory where he can write *_and_* execute binaries. If you meet that design goal, he has to smuggle _all_ code he wants to execute on the host (bindshell, kernel exploit, ...) through the guest kernel again. Here we go:
Restrict the rights of *=/dev/net/tun=*, the filesystem image and the UML kernel binary such that they are only writable or executable by the uml user:
chown root:uml /dev/net/tun rootfs.img linux-uml chmod 660 /dev/net/tun rootfs.img chmod 550 linux-uml
Create a tiny (4k for =cpuinfo=) temporary chroot jail as =tmpfs=, no devices and no executables allowed, sticky (new subdirs get owned by gid =uml=). It will become read-only at the end. Inside, create the *=/tmp=* directory as =tmpfs=, too, it has to be at least as big as the RAM of the UML guest kernel (example: 128MB) and has to allow executables (but not suid executables):
umask 027 mkdir chroot mount -t tmpfs -o size=4k,mode=2750,gid=uml,nodev,noexec none chroot mkdir chroot/tmp mount -t tmpfs -o size=129M,mode=0770,gid=uml,nodev,nosuid none chroot/tmp/
Create all the mountpoints and bindmount the necessary things into the jail:
mkdir -p chroot/proc chroot/dev/net touch chroot/proc/mm chroot/dev/net/tun chroot/linux chroot/rootfs mount -o bind /proc/mm chroot/proc/mm cp /proc/cpuinfo chroot/proc mount -o bind /dev/net/tun chroot/dev/net/tun mount -o bind linux-uml chroot/linux mount -o bind rootfs.img chroot/rootfs
Finally, make the chroot read-only and boot the UML. Wait a few seconds until the UML is set up and unmount =/tmp/= lazy (that means it cannot be used anymore except for inodes already opened by running processes):
mount -o remount,ro chroot/ screen -S uml compartment --user uml --group uml --chroot ./chroot /linux mem=128M uml_dir=/tmp ubd0=/rootfs [lots of UML options] sleep 10 umount -l chroot/tmp
After shutdown, destroy the temporary jail by unmounting all that stuff:
umount chroot/linux chroot/rootfs chroot/dev/net/tun chroot/proc/mm chroot/
, OpenWall
, LIDS
or SElinux
. That can make escaping the chroot harder, even for root!
without success. Of course you could allow the user =uml= via some *=sudo(8)=* magic to run the =compartment= command, but that does not chroot =screen=.
This documents originates in this thread
on the UML user list, thanks to Maarten, Jim Carter & Blaisorblade
. Thanks to Anthony Brock for productive comments.
2005-03-12 initial release; NilsToedtmann
2005-03-12 corrected compartment URL; Anthony Brock
2005-03-13 added credits, "screen inside chroot" section, deep phrack link, note on cpuinfo, selinux; NilsToedtmann
2005-03-15 chroot sticky, size=4k, ulimit, typos; NilsToedtmann