Date: Sun, 4 Dec 2011 14:17:43 +0100 From: joris dedieu <joris.dedieu@gmail.com> To: freebsd-hackers <freebsd-hackers@freebsd.org> Subject: Re: rtld and noexec Message-ID: <CAPd55qD%2B7PQ6vkc27%2BxQeeehJwCMG8x%2BqBxZ7sTit4i1M_yotQ@mail.gmail.com> In-Reply-To: <20111202164157.3058d91d@kan.dyndns.org> References: <CAPd55qAoCttEbV8fkALDaFBc60jigfbu9=Uwa_%2BaECYaHFJpSw@mail.gmail.com> <20111202164157.3058d91d@kan.dyndns.org>
next in thread | previous in thread | raw e-mail | index | archive | help
2011/12/2 Alexander Kabaev <kabaev@gmail.com>: > On Fri, 2 Dec 2011 18:22:57 +0100 > joris dedieu <joris.dedieu@gmail.com> wrote: > >> Hi, >> >> Here is a patch I use to prevent loading a shared object from a noexec >> mountpoint. =A0It's an easy way, I found, after the last root exploit >> ((http://seclists.org/fulldisclosure/2011/Nov/452), =A0to enhance =A0the >> security of my web servers (with /home, /tmp and /var/tmp mounted with >> noexec). >> >> - the last ftpd/porftpd =A0(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 =A0 =A0 =A0 =A02011-12-02 12:09:40.0000= 00000 >> +0100 +++ libexec/rtld-elf/rtld.c =A0 =A0 2011-12-02 13:45:18.000000000 >> +0100 @@ -1123,32 +1123,50 @@ >> =A0{ >> =A0 =A0 =A0char *pathname; >> =A0 =A0 =A0char *name; >> + =A0 =A0struct statfs mnt; >> >> =A0 =A0 =A0if (strchr(xname, '/') !=3D NULL) { =A0/* Hard coded pathname= */ >> + =A0 =A0 =A0name =3D NULL; >> =A0 =A0 =A0 =A0 if (xname[0] !=3D '/' && !trust) { >> =A0 =A0 =A0 =A0 =A0 =A0 _rtld_error("Absolute pathname required for shar= ed object >> \"%s\"", xname); >> =A0 =A0 =A0 =A0 =A0 =A0 return NULL; >> =A0 =A0 =A0 =A0 } >> =A0 =A0 =A0 =A0 if (refobj !=3D NULL && refobj->z_origin) >> - =A0 =A0 =A0 =A0 =A0 return origin_subst(xname, refobj->origin_path); >> + =A0 =A0 =A0 =A0 =A0 pathname =3D origin_subst(xname, refobj->origin_pa= th); >> =A0 =A0 =A0 =A0 else >> - =A0 =A0 =A0 =A0 =A0 return xstrdup(xname); >> + =A0 =A0 =A0 =A0 =A0 pathname =3D xstrdup(xname); >> + =A0 =A0} >> + =A0 =A0else { /* xname is not a path */ >> + =A0 =A0 =A0 if (libmap_disable || (refobj =3D=3D NULL) || >> + =A0 =A0 =A0 =A0 =A0 (name =3D lm_find(refobj->path, xname)) =3D=3D NUL= L) >> + =A0 =A0 =A0 =A0 =A0 name =3D (char *)xname; >> + >> + =A0 =A0 =A0 dbg(" Searching for \"%s\"", name); >> + >> + =A0 =A0 =A0 pathname =3D search_library_path(name, ld_library_path); >> + =A0 =A0 =A0 if (pathname =3D=3D NULL && refobj !=3D NULL) >> + =A0 =A0 =A0 =A0 =A0 =A0pathname =3D search_library_path(name, refobj->= rpath); >> + =A0 =A0 =A0 if (pathname =3D=3D NULL) >> + =A0 =A0 =A0 =A0 =A0 =A0pathname =3D search_library_path(name, gethints= ()); >> + =A0 =A0 =A0 if (pathname =3D=3D NULL) >> + =A0 =A0 =A0 =A0 =A0 =A0pathname =3D search_library_path(name, >> STANDARD_LIBRARY_PATH); >> + =A0 =A0} >> + >> + =A0 =A0if (pathname !=3D NULL) { /* noexec mountpoint in pathname */ >> + =A0 =A0 =A0 if (statfs(pathname, &mnt) !=3D 0) >> + =A0 =A0 =A0 =A0 =A0 =A0free(pathname); >> + =A0 =A0 =A0 else { >> + =A0 =A0 =A0 =A0 =A0 =A0if (mnt.f_flags & MNT_NOEXEC) { >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0_rtld_error("noexec violation for shared ob= ject >> \"%s\"", pathname); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0free(pathname); >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0return NULL; >> + =A0 =A0 =A0 =A0 =A0 =A0} >> + =A0 =A0 =A0 =A0 =A0 =A0else >> + =A0 =A0 =A0 =A0 =A0 =A0 =A0return pathname; >> + =A0 =A0 =A0 } >> =A0 =A0 =A0} >> >> - =A0 =A0if (libmap_disable || (refobj =3D=3D NULL) || >> - =A0 =A0 =A0 (name =3D lm_find(refobj->path, xname)) =3D=3D NULL) >> - =A0 =A0 =A0 name =3D (char *)xname; >> - >> - =A0 =A0dbg(" Searching for \"%s\"", name); >> - >> - =A0 =A0if ((pathname =3D search_library_path(name, ld_library_path)) != =3D >> NULL || >> - =A0 =A0 =A0(refobj !=3D NULL && >> - =A0 =A0 =A0(pathname =3D search_library_path(name, refobj->rpath)) != =3D NULL) >> || >> - =A0 =A0 =A0(pathname =3D search_library_path(name, gethints())) !=3D N= ULL || >> - =A0 =A0 =A0(pathname =3D search_library_path(name, >> STANDARD_LIBRARY_PATH)) !=3D NULL) >> - =A0 =A0 =A0 return pathname; >> - >> =A0 =A0 =A0if(refobj !=3D NULL && refobj->path !=3D NULL) { >> =A0 =A0 =A0 =A0 _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=3D/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 =3D dlopen("/tank/t/libmoo.so", RTLD_LAZY); if (! handle) { fprintf(stderr, "%s\n", dlerror()); exit(1); } void (* moo) (void) =3D 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. Joris > > -- > Alexander Kabaev
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAPd55qD%2B7PQ6vkc27%2BxQeeehJwCMG8x%2BqBxZ7sTit4i1M_yotQ>