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