Date: Fri, 1 Nov 1996 23:38:23 -0700 (MST) From: Marc Slemko <marcs@znep.com> To: Don Lewis <Don.Lewis@tsc.tdk.com> Cc: Dev Chanchani <dev@trifecta.com>, freebsd-security@freebsd.org Subject: Re: chroot() security Message-ID: <Pine.BSF.3.95.961101225850.22655K-100000@alive.ampr.ab.ca> In-Reply-To: <199611020401.UAA07806@salsa.gv.ssi1.com>
next in thread | previous in thread | raw e-mail | index | archive | help
On Fri, 1 Nov 1996, Don Lewis wrote: > You can add various checks to the kernel to keep chroot()ed processes > from doing a lot of these things, but there is one deadly exploit that > someone posted to this list back in September. By the clever use of > chroot() and chdir(), it is possible for a root process to waltz out > of a chroot()ed environment. I don't know of a clean way of plugging > that hole. > > BTW, thanks for mentioning ptrace(). I hadn't thought of that one. Yup, you certainly can add checks and in theory you should be able to plug all the holes IF you can find them. My bet is that you won't be able to find them, so you can't make it secure. I assume the below message is the one you are referring to, so I'll include a copy so people know what the issue is. Heck, I may as well run through what the code does. I forgot about the obvious because well, there are just so many ways to get out. This one is quite clean though. When you do a chroot(2), the current working directory is not changed, the only thing that is changed is what the kernel thinks the root directory is for that process. The chroot program that comes with FreeBSD does something like: if (chdir(path) || chroot(".")) print_error_and_die where path is the directory you want to chroot to. If either of the calls fail, it exits. This ensures that the current directory is inside the new root directory so no access outside can be made. Now, there is only one root directory per process (well, that isn't quite true but the details get complicated). Keeping this in mind, examine the code below. It does a chroot to a new directory inside the chrooted environment; since there is only one root directory per process, this is NOT a chrooted environment inside a chrooted environment, but is simply a chrooted environment. If you can break the new chrooted environment, you have access to the whole filesystem, not just to the old chrooted envionment. Now, how can we go about breaking out of this new chrooted environment? Go back to how the chroot program does it; first it changes the directory, then it does the chroot to be sure the current directory is inside the chrooted tree. This program doesn't do that. After it does the chroot, the current directory is still outside the new chrooted directory; it is inside what was the old one, but that doesn't matter any more. From there, it is a simple matter to back up until we get to the root, then make the real root our new root with another chroot call. A trivial solution would be to modify the kernel chroot routine to change the current directory to something inside the chrooted directory, however that solution is too trivial in that it would break some existing programs and I'm not sure it would help anything because I would suggest that many of the data structures involved could perhaps be manipulated using some other method. Date: Mon, 9 Sep 1996 16:09:46 -0500 (CDT) From: Zach Heilig <zach@blizzard.gaffaneys.com> To: rkw@dataplex.net (Richard Wackerbarth) Cc: freebsd-security@freebsd.org Sender: owner-security@freebsd.org Subject: Re: Question about chroot In a previous message, Richard Wackerbarth wrote: >In looking at some of the "make" problems, I ran up against a >characteristic of "chroot" that puzzles me. >In order to chroot, you must be root. Why? Basically, all you have to do to become root in a chroot() environment is to hard-link the appropriate setuid executables into a publicly writable directory (usually /usr/tmp), and build your own minimal filesystem to use while in that environment. You need the /etc/master.passwd file (you create it yourself), /etc/group (to put yourself in the wheel group), /usr/sbin/vipw (to rebuild the other passwd files), /usr/bin/su (to gain root), /bin/sh, and any other file utilities that you find you need in the course of the attack (probably hard links to each of the lib*.so.* libraries, and ld/ldd). You should probably put the binaries in /bin (relative to the chroot() root directory). Breaking out of the chroot() environment after you become root is trivial. I have source here that works for FreeBSD: #include <stdio.h> #include <unistd.h> int main(void) { int i; mkdir("break-out", 0755); chroot("./break-out"); chdir("."); for (i = 0; i < 30; ++i) chdir(".."); chroot("."); execl("/bin/sh", "sh" , (char *) 0); puts("exec failed!"); return 1; } You compile this before calling chroot for /usr/tmp/wherever. At this point, the attacker would clean up his mess in /usr/tmp, and possibly put a setuid shell in an accessible spot. >It appears to me than the only thing that chroot does is to restrict the >"visable" tree. It does not ADD anything that is not already there. But the user can BEFORE running chroot. >If that is the case, why wouldn't it be good enough for chroot to be suid >root and allow any user to execute it? >Am I overlooking some security hole? Yes. This is one reason it is bad to have a world-writable directory on the same filesystem as the /usr filesystem. -- Zach Heilig (zach@blizzard.gaffaneys.com) | ALL unsolicited commercial email Support bacteria -- it's the | is unwelcome. I avoid dealing only culture some people have! | with companies that email ads.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?Pine.BSF.3.95.961101225850.22655K-100000>