On Sun, Dec 04, 2011 at 02:17:43PM +0100, joris dedieu wrote: > 2011/12/2 Alexander Kabaev <kab...@gmail.com>: > > On Fri, 2 Dec 2011 18:22:57 +0100 > > joris dedieu <joris.ded...@gmail.com> wrote: > > > >> Hi, > >> > >> Here is a patch I use to prevent loading a shared object from a noexec > >> mountpoint. It's an easy way, I found, after the last root exploit > >> ((http://seclists.org/fulldisclosure/2011/Nov/452), to enhance the > >> security of my web servers (with /home, /tmp and /var/tmp mounted with > >> noexec). > >> > >> - the last ftpd/porftpd (libc ?) exploit does not work (indirect use > >> of rtld via nsswitch) > >> - the previous rtld security issue should have been more difficult to > >> use in a noexec context. > >> - It may help to prevent some miscellaneous usage of common softwares > >> using dlopen like apache or php. > >> > >> I think it also makes sens because loading a shared object sounds like > >> a kind of "execution". > >> > >> What do you think about this patch and the opportunity to open a PR on > >> this subject? > >> > >> Cheers > >> Joris > >> > >> > >> --- libexec/rtld-elf/rtld.c.orig 2011-12-02 12:09:40.000000000 > >> +0100 +++ libexec/rtld-elf/rtld.c 2011-12-02 13:45:18.000000000 > >> +0100 @@ -1123,32 +1123,50 @@ > >> { > >> char *pathname; > >> char *name; > >> + struct statfs mnt; > >> > >> if (strchr(xname, '/') != NULL) { /* Hard coded pathname */ > >> + name = NULL; > >> if (xname[0] != '/' && !trust) { > >> _rtld_error("Absolute pathname required for shared object > >> \"%s\"", xname); > >> return NULL; > >> } > >> if (refobj != NULL && refobj->z_origin) > >> - return origin_subst(xname, refobj->origin_path); > >> + pathname = origin_subst(xname, refobj->origin_path); > >> else > >> - return xstrdup(xname); > >> + pathname = xstrdup(xname); > >> + } > >> + else { /* xname is not a path */ > >> + if (libmap_disable || (refobj == NULL) || > >> + (name = lm_find(refobj->path, xname)) == NULL) > >> + name = (char *)xname; > >> + > >> + dbg(" Searching for \"%s\"", name); > >> + > >> + pathname = search_library_path(name, ld_library_path); > >> + if (pathname == NULL && refobj != NULL) > >> + pathname = search_library_path(name, refobj->rpath); > >> + if (pathname == NULL) > >> + pathname = search_library_path(name, gethints()); > >> + if (pathname == NULL) > >> + pathname = search_library_path(name, > >> STANDARD_LIBRARY_PATH); > >> + } > >> + > >> + if (pathname != NULL) { /* noexec mountpoint in pathname */ > >> + if (statfs(pathname, &mnt) != 0) > >> + free(pathname); > >> + else { > >> + if (mnt.f_flags & MNT_NOEXEC) { > >> + _rtld_error("noexec violation for shared object > >> \"%s\"", pathname); > >> + free(pathname); > >> + return NULL; > >> + } > >> + else > >> + return pathname; > >> + } > >> } > >> > >> - if (libmap_disable || (refobj == NULL) || > >> - (name = lm_find(refobj->path, xname)) == NULL) > >> - name = (char *)xname; > >> - > >> - dbg(" Searching for \"%s\"", name); > >> - > >> - if ((pathname = search_library_path(name, ld_library_path)) != > >> NULL || > >> - (refobj != NULL && > >> - (pathname = search_library_path(name, refobj->rpath)) != NULL) > >> || > >> - (pathname = search_library_path(name, gethints())) != NULL || > >> - (pathname = search_library_path(name, > >> STANDARD_LIBRARY_PATH)) != NULL) > >> - return pathname; > >> - > >> if(refobj != NULL && refobj->path != NULL) { > >> _rtld_error("Shared object \"%s\" not found, required by > >> \"%s\"", name, basename(refobj->path)); > >> _______________________________________________ > > > > > > 1. There is a race using statfs and then loading the file. > I will look at this point. Maybe statfs on the dirname ? > > > 2. We already have the check in do_load_object > It doesn't work with dlopen. > > mount |grep tank/t > tank/t on /tank/t (zfs, local, noexec, nfsv4acls) > > so /tank/t is noexec > > Here the powerful libmoo source code : > > void say_moo() { > printf("mooooooooooooooooo\n"); > } > > it's in /tank/t so noexec > > ls -l /tank/t/ > total 6 > -rwxr-xr-x 1 joris joris 4632 Dec 4 13:52 libmoo.so > > 1) First test with : > > main() { > say_moo(); > } > > LD_LIBRARY_PATH=/tank/t ./test_moo > /libexec/ld-elf.so.1: Cannot execute objects on /tank/t > > Ok cool work has expected. > > Second test with : > > main() { > void * handle = dlopen("/tank/t/libmoo.so", RTLD_LAZY); > if (! handle) { > fprintf(stderr, "%s\n", dlerror()); > exit(1); > } > void (* moo) (void) = dlsym (handle, "say_moo"); > (* moo)(); > dlclose (handle); > } > > ./test_moo > mooooooooooooooooo > > Protection is not working when you use dlopen. This is what append > with ftpd exploit . libc just load a shared object and the guy is > root.
If you started to change rtld code, it pays to read it first. The first example worked only because you used LD_LIBRARY_PATH. See r144062. It has nothing to do with dlopen/static linking. I do not think you patch is a right thing to do. noexec check is a hack anyway, because noexec _mount_ option is only there to globally disable execve(2) from the mount point, and not to disable mapping with PROT_EXEC. rtld does not provide any magic from the kernel POV, so purposive person can re-implement it at will in her code.
pgpUta3NEIR04.pgp
Description: PGP signature