rtld and noexec
joris dedieu
joris.dedieu at gmail.com
Sun Dec 4 13:17:44 UTC 2011
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.
Joris
>
> --
> Alexander Kabaev
More information about the freebsd-hackers
mailing list