Exploit-DB #43331
Description: GNU C Library Dynamic Loader glibc ld.so - Memory Leak / Buffer Overflow
Type: local
Platform: linux
Date: 2017-12-13
Author: Qualys Corporation

Exploit:
Qualys Security Advisory

Buffer overflow in glibc's ld.so


========================================================================
Contents
========================================================================

Summary
Memory Leak
Buffer Overflow
Exploitation
Acknowledgments


========================================================================
Summary
========================================================================

We have discovered a memory leak and a buffer overflow in the dynamic
loader (ld.so) of the GNU C Library (glibc):

- the memory leak (CVE-2[PII_REDACTED]) first appeared in glibc 2.1.1
  (released on May 24, 1999) and can be reached and amplified through
  the LD_HWCAP_MASK environment variable;

- the buffer overflow (CVE-2[PII_REDACTED]) first appeared in glibc 2.5
  (released on September 29, 2006) and can be triggered through the
  LD_LIBRARY_PATH environment variable.

Further investigation showed that:

- the buffer overflow is not exploitable if
  /proc/sys/fs/protected_hardlinks is enabled (it is not enabled by
  default on vanilla Linux kernels, but most Linux distributions turn it
  on by default);

- the memory leak and the buffer overflow are not exploitable if the
  glibc is patched against CVE-2[PII_REDACTED], because this patch ignores
  the LD_HWCAP_MASK and LD_LIBRARY_PATH environment variables when SUID
  binaries are executed (CVE-2[PII_REDACTED] was first patched in glibc
  2.26, released on August 2, 2017, but most Linux distributions had
  already backported this patch on June 19, 2017).

We have therefore rated the impact of these vulnerabilities as Low.
Nevertheless, we give a brief analysis of the vulnerable function, and
present a simple method for exploiting a SUID binary on the command line
and obtaining full root privileges (if /proc/sys/fs/protected_hardlinks
is not enabled, and CVE-2[PII_REDACTED] is not patched).


========================================================================
Memory Leak (CVE-2[PII_REDACTED])
========================================================================

------------------------------------------------------------------------
Analysis
------------------------------------------------------------------------

In _dl_init_paths(), ld.so malloc()ates "rtld_search_dirs.dirs[0]", a
cache of information about the system's trusted directories (typically
"/lib" and "/usr/lib" on 32-bit or "/lib64" and "/usr/lib64" on 64-bit).
To compute the number of system directories, ld.so uses the classic C
idiom "sizeof (system_dirs) / sizeof (system_dirs[0])":

 691   rtld_search_dirs.dirs[0] = (struct r_search_path_elem *)
 692     malloc ((sizeof (system_dirs) / sizeof (system_dirs[0]))
 693             * round_size * sizeof (struct r_search_path_elem));

Unfortunately, "system_dirs" is not a classic array: it is not an array
of strings (pointers to characters), but rather an array of characters,
the concatenation of all system directories, separated by null bytes:

 109 static const char system_dirs[] = SYSTEM_DIRS;

where "SYSTEM_DIRS" is generated by "gen-trusted-dirs.awk" (typically
"/lib/\0/usr/lib/" on 32-bit or "/lib64/\0/usr/lib64/" on 64-bit). As a
result, the number of system directories is overestimated, and too much
memory is allocated for "rtld_search_dirs.dirs[0]": if "system_dirs" is
"/lib/\0/usr/lib/" for example, the number of system directories is 2,
but 16 is used instead (the number of characters in "system_dirs") to
compute the size of "rtld_search_dirs.dirs[0]".

This extra memory is never accessed, never freed, and mostly filled with
null bytes, because only the information about "nsystem_dirs_len" system
directories (the correct number of system directories) is written to
"rtld_search_dirs.dirs[0]", and because the minimal malloc()
implementation in ld.so calls mmap(), but never munmap().

Moreover, this memory leak can be amplified through the LD_HWCAP_MASK
environment variable, because ld.so uses "ncapstr" (the total number of
hardware-capability combinations) to compute the size of
"rtld_search_dirs.dirs[0]":

 687   round_size = ((2 * sizeof (struct r_search_path_elem) - 1
 688                  + ncapstr * sizeof (enum r_dir_status))
 689                 / sizeof (struct r_search_path_elem));

------------------------------------------------------------------------
History
------------------------------------------------------------------------

We tracked down this vulnerability to:

commit ab7eb292307152e706948a7b19164ff5e6d593d4
Date:   Mon May 3 21:59:35 1999 +0000

    Update.

        * elf/Makefile (trusted-dirs.st): Use gen-trusted-dirs.awk.
        * elf/gen-trusted-dirs.awk: New file.
        * elf/dl-load.c (systems_dirs): Moved into file scope.  Initialize
        from SYSTEM_DIRS macro.
        (system_dirs_len): New variable.  Contains lengths of system_dirs
        strings.
        (fillin_rpath): Rewrite for systems_dirs being a simple string.
        Improve string comparisons.  Change parameter trusted to be a flag.
        Change all callers.
        (_dt_init_paths): Improve using new format for system_dirs.

which transformed "system_dirs" from an array of strings (pointers to
characters) into an array of characters:

-  static const char *system_dirs[] =
-  {
-#include "trusted-dirs.h"
-    NULL
-  };
...
+static const char system_dirs[] = SYSTEM_DIRS;


========================================================================
Buffer Overflow (CVE-2[PII_REDACTED])
========================================================================

------------------------------------------------------------------------
Analysis
------------------------------------------------------------------------

In _dl_init_paths(), ld.so computes "nllp", the number of
colon-separated directories in "llp" (the LD_LIBRARY_PATH environment
variable), malloc()at