Exploit-DB #15274
Description: GNU C library dynamic linker - '$ORIGIN' Expansion
Type: local
Platform: linux
Date: 2010-10-18
Author: Tavis Ormandy

Exploit:
from: http://marc.info/?l=full-disclosure&m=[PII_REDACTED]4072&w=2

The GNU C library dynamic linker expands $ORIGIN in setuid library search path
------------------------------------------------------------------------------

Gruezi, This is CVE-2010-3847.

The dynamic linker (or dynamic loader) is responsible for the runtime linking of
dynamically linked programs. ld.so operates in two security modes, a permissive
mode that allows a high degree of control over the load operation, and a secure
mode (libc_enable_secure) intended to prevent users from interfering with the
loading of privileged executables.

$ORIGIN is an ELF substitution sequence representing the location of the
executable being loaded in the filesystem hierarchy. The intention is to allow
executables to specify a search path for libraries that is relative to their
location, to simplify packaging without spamming the standard search paths with
single-use libraries.

Note that despite the confusing naming convention, $ORIGIN is specified in a
DT_RPATH or DT_RUNPATH dynamic tag inside the executable itself, not via the
environment (developers would normally use the -rpath ld parameter, or
-Wl,-rpath,$ORIGIN via the compiler driver).

The ELF specification suggests that $ORIGIN be ignored for SUID and SGID
binaries,

http://web.archive.org/web/[PII_REDACTED]3725/http://www.caldera.com/developers/gabi/2003-12-17/ch5.dynamic.html#substitution

"For security, the dynamic linker does not allow use of $ORIGIN substitution
 sequences for set-user and set-group ID programs. For such sequences that
 appear within strings specified by DT_RUNPATH dynamic array entries, the
 specific search path containing the $ORIGIN sequence is ignored (though other
 search paths in the same string are processed). $ORIGIN sequences within a
 DT_NEEDED entry or path passed as a parameter to dlopen() are treated as
 errors. The same restrictions may be applied to processes that have more than
 minimal privileges on systems with installed extended security mechanisms."

However, glibc ignores this recommendation. The attack the ELF designers were
likely concerned about is users creating hardlinks to suid executables in
directories they control and then executing them, thus controlling the
expansion of $ORIGIN.

It is tough to form a thorough complaint about this glibc behaviour however,
as any developer who believes they're smart enough to safely create suid
programs should be smart enough to understand the implications of $ORIGIN
and hard links on load behaviour. The glibc maintainers are some of the
smartest guys in free software, and well known for having a "no hand-holding"
stance on various issues, so I suspect they wanted a better argument than this
for modifying the behaviour (I pointed it out a few years ago, but there was
little interest).

However, I have now discovered a way to exploit this. The origin expansion
mechanism is recycled for use in LD_AUDIT support, although an attempt is made
to prevent it from working, it is insufficient.

LD_AUDIT is intended for use with the linker auditing api (see the rtld-audit
manual), and has the usual restrictions for setuid programs as LD_PRELOAD does.
However, $ORIGIN expansion is only prevented if it is not used in isolation.

The codepath that triggers this expansion is

   _dl_init_paths() -> _dl_dst_substitute() -> _is_dst()

(in the code below DST is dynamic string token)

http://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-load.c;h=a7162eb77de7a538235a4326d0eb9ccb5b244c01;hb=HEAD#l741

 741       /* Expand DSTs.  */
 742       size_t cnt = DL_DST_COUNT (llp, 1);
 743       if (__builtin_expect (cnt == 0, 1))
 744         llp_tmp = strdupa (llp);
 745       else
 746         {
 747           /* Determine the length of the substituted string.  */
 748           size_t total = DL_DST_REQUIRED (l, llp, strlen (llp), cnt);
 749
 750           /* Allocate the necessary memory.  */
 751           llp_tmp = (char *) alloca (total + 1);
 752           llp_tmp = _dl_dst_substitute (l, llp, llp_tmp, 1);
 753         }

http://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-load.c;h=a7162eb77de7a538235a4326d0eb9ccb5b244c01;hb=HEAD#l245

 253       if (__builtin_expect (*name == '$', 0))
 254         {
 255           const char *repl = NULL;
 256           size_t len;
 257
 258           ++name;
 259           if ((len = is_dst (start, name, "ORIGIN", is_path,
 260                              INTUSE(__libc_enable_secure))) != 0)
 261             {
   ...
 267                 repl = l->l_origin;
 268             }

http://sourceware.org/git/?p=glibc.git;a=blob;f=elf/dl-load.c;h=a7162eb77de7a538235a4326d0eb9ccb5b244c01;hb=HEAD#l171


 202   if (__builtin_expect (secure, 0)
 203       && ((name[len] != '\0' && (!is_path || name[len] != ':'))
 204           || (name != start + 1 && (!is_path || name[-2] != ':'))))
 205     return 0;
 206
 207   return len;
 208 }

As you can see, $ORIGIN is only expanded if it is alone and first in the path.
This makes little sense, and does not appear to be useful even if there were
no security impact. This was most likely the result of an attempt to re-use the
existing DT_NEEDED resolution infrastructure for LD_AUDIT support, accidentally
introducing this error.

Perhaps surprisingly, this error is exploitable.

--------------------
Affected Software
------------------------

At least the following versions have been tested

   2.12.1, FC13
   2.5, RHEL5 / CentOS5

Other versions are probably affected, possibly via different vectors. I'm aware
several versions of ld.so in common use hit an assertion in dl_open_worker, I
do not know if it's possible to avoid this.

--------------------
Consequences
-----------------------

It is possible to exploit this flaw to execute arbitrary code as root.

Please note, this is a low impact vulnerability that is