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.


Joris
>
> --
> Alexander Kabaev
_______________________________________________
freebsd-hackers@freebsd.org mailing list
http://lists.freebsd.org/mailman/listinfo/freebsd-hackers
To unsubscribe, send any mail to "freebsd-hackers-unsubscr...@freebsd.org"

Reply via email to