NilsToedtmann

Disclaimer

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.

Introduction

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:

  • the UML kernel binary itself, *=x=* (bindmounted or copied)
  • the filesystem image files, *=rw=* (or *=r=* and cow files *=rw=*)
  • *=/tmp, rw=* with enough free space to write the UML mem as a file
  • *=/dev/net/tun, rw=* (bindmounted or mknod) if you use networking with tuntap or uml_net
  • *=/proc/mm, w=* (bindmounted) if you use SKAS
  • *=/proc/cpuinfo, r=* (bindmounted or copied) only obligatory for older guest in SKAS mode. Newer versions just give a small warning if missing

Simple example

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

Start up

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=*".

Advanced paranoic example

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/

Variants

  • If the UML filesystem shall be non-persistant: make the filesystem images readonly and touch new, temporary cow files within =chroot/tmp/=
  • As the jail is read-only anyway, you can omit the "=nodev,noexec=" within "=mount -t tmpfs ... chroot=", copy the uml binary and =mknod /dev/net/tun= as in the simple example above. This spares two bindmounts.
  • Mounting with the option "=-n=" does not update =/etc/mtab=, so the output of =mount(8)= gets less confused by all those bindmounts.
  • Harden the host kernel with extra security patches like grsecurity, OpenWall, LIDS or SElinux. That can make escaping the chroot harder, even for root!

To Do

  • testing
  • restricting resources of the guest via *=ulimit=* and/or *=/etc/security/limits.conf=*
  • security consideration
  • The console problem: If you start the UML as in the above example screen -S uml compartment --user uml --group uml --chroot ./chroot /linux [UML options] only root can reconnect to that UML console, and an attacker inside the UML could try to attack =screen(1)= (eg. by flooding =/dev/tty0=). So =screen= (or whatever forked process holds UML's initial tty) should be owned by =uml= and should be chrooted. I tried to compile a static =screen=, put some pty/tty devices into the chroot and reverse it: compartment --user uml --group uml --chroot ./chroot /screen -S uml /linux [UML options] but i failed :-( I also tried dtach 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=.

Credits

This documents originates in this thread on the UML user list, thanks to Maarten, Jim Carter & Blaisorblade. Thanks to Anthony Brock for productive comments.

Changelog

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

  Page Info My Prefs Log in
This page (revision-3) last changed on 09:42 03-Nov-2006 by Dave Pearson.
 

JSPWiki v2.4.71
[RSS]