rtld and noexec
Kostik Belousov
kostikbel at gmail.com
Sun Dec 4 17:18:30 UTC 2011
On Sun, Dec 04, 2011 at 02:17:43PM +0100, joris dedieu wrote:
> 2011/12/2 Alexander Kabaev <kabaev at gmail.com>:
> > On Fri, 2 Dec 2011 18:22:57 +0100
> > joris dedieu <joris.dedieu at 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.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 196 bytes
Desc: not available
Url : http://lists.freebsd.org/pipermail/freebsd-hackers/attachments/20111204/b8780a65/attachment.pgp
More information about the freebsd-hackers
mailing list