Change-roots, a making-and-breaking overview. (Talk given at LA2600 meeting, May 2003, by Sorel) -- Talk Overview ----------------------------------------------------- intro { - Layout of an inode-based filesystem - The changeroot mechanism - Setting up a basic changeroot } intro; make&break { - working directory - file descriptors - chroot-in-chroot - uploading utils - /dev - /proc - logging, pids - inode numbers - DoS } make&break; conclusions { - chroot can be good for non-root services - non-trivial to set up effectively - is not a cureall - alternatives exist - most alternatives aren't appropriate } conclusions; -- Talk Detail ------------------------------------------------------- intro { Layout of an inode-based filesystem { - Files are pointed to by 'inodes' - 'inodes' are associated with filenames This means: - Files can stay open after they've been deleted - What actual file a filename is tied to can change really fast - You can run out of inodes - What filename is tied to an inode depends on the process } The changeroot mechanism { - A system call to change what a programs thinks if as '/' in the filesystem. - Doesn't actually change how anything is mounted. - All the children of that program inherit the changeroot from it. - Programs in a changeroot (for the most part) can't access files outside the changeroot. - Often used as a security measure to limit damage if a service gets compromised. } Setting up a basic changeroot { - Must place all needed files in the changeroot. - This may include parts of /etc, /dev, /proc... - This may include libraries. - This may include supporting applications. - Cannot use symlinks, using hard links is a really bad idea. - More files generall == more risk. - Make sure to test all functionality after setting it up! } } intro; make&break { working directory { - After a program does chroot(), it should chdir() into the chroot. - If it doesn't, an attacker can use that open file descriptor to get out of the chroot. } file descriptors { - Any open file descriptor outside the chroot can be used that way, so they should all be closed. } chroot-in-chroot { - There is only one chroot for any process, they are not kept on any sort of stack. - You can create a file descriptor outside the chroot by opening a file descriptor inside the chroot then chrooting to someplace in the chroot that that file descriptor wasn't inside of. - This only works if you're root. } uploading utils { - Since chroots often contain only minimal stuff, some deviousness can be needed to upload files / utilities. - The methods for this will depend highly on the situation. - The better you understand the system, the more creative you can get if needed. } /dev { - Devices in /dev are shared between the chroot and the rest of the system. - This includes such things as /dev/kmem and /dev/mem. - Yet another reason not to have root programs in a chroot. } /proc { - Also shared with main system. - Can be leveraged to escape the chroot. } logging, pids { - Pids are also whole-system, so you can kill processes by pid even if you're inside a chroot and they aren't. - Log files for chrooted services are often read by non-chrooted utilities. If you know what utilities are being used and are patient, this could possibly be useful. } inode numbers { - If you can access files by inode number, you're home free. Just chroot for inode #2. Unless your chroot area is on a seperate filesystem... in which case that won't work. But I think there may be ways around that, not sure. (inode 2 is the root inode of the filesystem.) } DoS { - Since you're sharing resources with the rest of the system, you always have the option of just bogging it down. } } make&break; conclusions { chroot can be good for non-root services { - If you don't need your services to be run as root, putting them in a changeroot will atleast slow attackers down. - If your services require root, don't bother. } non-trivial to set up effectively { - There are a lot of details that have to be doublechecked when setting up a changeroot. - It's not a sure-fire wall, it's a tiny smidge of a wall that you then have to make sure nothing goes around. } is not a cureall { - Changeroots can almost always be broken out of. - Most people don't know how to break out of them, so they arne't useless. } alternatives exist { - User Mode Linux - BSD Jail - Prolly some I forgot } most alternatives aren't appropriate { - Almost all, if not all, of these systems require setting up a full self-contained system, rather than just the infrastructure for a single service. - Full-blown systems don't buy you as much annoying of the hacker as single-service sections do, and they are easier to just use as launch points rather than bothering to break them. } } conclusions; -- Resources --------------------------------------------------------- http://www.bpfh.net/simes/computing/chroot-break.html http://www.linuxsecurity.com/feature_stories/feature_story-99.html http://www.ussg.iu.edu/hypermail/linux/kernel/0207.0/0683.html http://www.ussg.iu.edu/hypermail/linux/kernel/0207.0/0730.html http://www.ussg.iu.edu/hypermail/linux/kernel/0207.0/0669.html http://user-mode-linux.sourceforge.net/slides/ists2002/text0.htm http://www.linux-consulting.com/Boot/Chroot/chroot.txt http://obfuscated.yi.org/1111--ANON_FTP_SERVER/archive/koan.txt http://proftpd.linux.co.uk/localsite/Userguide/linked/chroot-symlinks.html http://www.networkdweebs.com/chroot.html -- Breaking methods -------------------------------------------------- 1) Uber-tempdir Create a temporary directory in its current working directory Open the current working directory Note: only required if chroot() changes the calling program's working directory. Change the root directory of the process to the temporary directory using chroot(). Use fchdir() with the file descriptor of the opened directory to move the current working directory outside the chroot()ed area. Note: only required if chroot() changes the calling program's working directory. Perform chdir("..") calls many times to move the current working directory into the real root directory. Change the root directory of the process to the current working directory, the real root directory, using chroot(".") 2) Linux-Stuff Second, the number of ways that root user can break out of chroot is huge. Starting from simple use of a chroot() call with no chdir() [see code below] to esoteric methods as the creation of your own /dev/hda or /dev/kmem devices, injection code into the running kernel (http://www.big.net.au/~silvio/runtime-kernel-kmem-patching.txt), using open directory handles outside chroot or chroot-breaking buffer Sample code to break out of chroot: #include #include #include #include #include int main(void) { int i; mkdir("breakout", 0700); chroot("breakout"); for (i = 0; i < 100; i++) chdir("..") ; chroot("."); execl("/bin/sh", "/bin/sh",NULL); } compile statically (using "gcc -static") and run within chrooted directory (after doing "chroot ." or similar from shell prompt) 3) Nod-stuff If you can get a process to run as root, you're in try creating devices to access RAM through the 'mknod' command. 3) FuD You need to avoid devices, root, fd passing from untrusted agents outside the chroot world and shared uid (ptrace) attacks. 4) Proc-ed I wanted to create special block/char devices to modify data there to get root. But that wasn't even necessary because after uploading mount and mount_procfs, and mounting procfs to /proc i found out that root was allowed to modify file permissions in /proc/pid/file