# 工具基类 ''' 工具基类----工具子类约束: 1.一个工具子类一个py文件 2.工具类文件名必须为:[指令名]+Tool.py 3.未完成的工具子类以__开头 4. ''' import abc import subprocess import argparse import shlex import subprocess import tempfile import os from myutils.ReturnParams import ReturnParams class ToolBase(abc.ABC): def get_time_out(self): return 60*30 def create_extparams(self): ext_params = ReturnParams() ext_params["is_user"] = False # 是否要提交用户确认 -- 默认False ext_params["is_vulnerability"] = False # 是否是脆弱点 return ext_params def parse_sublist3r_command(self,command): parser = argparse.ArgumentParser(add_help=False,allow_abbrev=False) #创建命令行参数解析器对象‌ parser.add_argument("-o", "--output", type=str) #添加需要解析的参数规则‌ parser.add_argument("-oN", "--Noutput", type=str) #nmap parser.add_argument("-oG","--NMG",type=str) #nmap parser.add_argument("-output", "--nikto", type=str) #nikto args, _ = parser.parse_known_args(command.split()[1:]) return args def parse_output_params(self,command, valid_flags=None): """ 解析 shell 命令中用于输出文件的参数。 只检查完全匹配的 token,忽略诸如 "-oKexAlgorithms" 这样的参数。 :param command: 完整的命令字符串 :param valid_flags: 合法的输出参数列表,默认支持 -o, -oN, -oG, -output :return: dict,键为输出参数,值为对应的文件路径(如果有的话) """ if command.strip().startswith("mkdir"): return "" #额外指令排除:ssh -o 不是输出结果到文件 if "ssh" in command: return "" if valid_flags is None: valid_flags = ['-o', '-oN', '-oG', '-output'] tokens = shlex.split(command) output_params = {} output_file = "" i = 0 while i < len(tokens): token = tokens[i] # 如果 token 完全匹配有效的参数,则认为它是输出参数 if token in valid_flags: # 如果紧跟着有值,则把它作为输出文件路径,否则为 None if i + 1 < len(tokens): #output_params[token] = tokens[i + 1] #i += 2 output_file = tokens[i+1] # else: # output_params[token] = None # i += 1 break # 一般只有一个输出参数 else: i += 1 return output_file def read_output_file(self,filename): """ 读取 -o 参数指定的文件内容 """ content = "" try: with open(filename, "r") as f: content = f.read() with open(filename,'w') as f: f.write("") except FileNotFoundError: print(f"错误: 文件 {filename} 不存在") except PermissionError: print(f"错误: 无权限读取文件 {filename}") return content def do_worker_script(self,str_instruction,timeout,ext_params): # 创建临时文件保存输出 with tempfile.NamedTemporaryFile(delete=False) as tmpfile: output_file = tmpfile.name # 构建并执行 script 命令 script_cmd = f"script -c '{str_instruction}' {output_file}" try: result = subprocess.run(script_cmd, shell=True, text=True,timeout=timeout) # 读取输出文件内容 with open(output_file, 'r') as f: output = f.read() lines = output.splitlines() # 跳过第一行(Script started)和最后一行(Script done) ftp_output = lines[1:-1] output = '\n'.join(ftp_output) except subprocess.TimeoutExpired: output = "命令超时返回" try: with open(output_file, 'r') as f: partial_output = f.read() if partial_output: output += f"\n部分输出:\n{partial_output}" except FileNotFoundError: pass # 文件可能未创建 except subprocess.CalledProcessError as e: output = f"错误: {e}" finally: # 删除临时文件 try: os.remove(output_file) except FileNotFoundError: pass # 文件可能未创建 return output def execute_instruction(self, instruction_old): ''' 执行指令:验证合法性 -> 执行 -> 分析结果 *****如果指令要做验证,只做白名单,所有逻辑不是全放开就是白名单***** :param instruction_old: :return: bool:true-正常返回给大模型处理下一步,false-结果不返回给大模型,2--需要人工确认的指令 str:执行的指令 str:执行指令的结果-解析过滤后的结果--也是提交给LLM的结果 str:执行指令的结果-原结果 object:补充参数-封装一个对象: 0-不知是否攻击成功,1-明确存在漏洞,2-明确不存在漏洞 ''' ext_params = self.create_extparams() # 第一步:验证指令合法性 instruction,timeout = self.validate_instruction(instruction_old) if not instruction: ext_params.is_user= True return False,instruction_old,"该指令暂不执行!由用户确认是否要兼容支持","",ext_params #未 #过滤修改后的指令是否需要判重?同样指令再执行结果一致?待定---#? # 第二步:执行指令 #print(f"执行指令:{instruction}") output = "" stdout = "" stderr = "" try: if timeout == 0: result = subprocess.run(instruction, shell=True, capture_output=True, text=True) elif timeout >0: result = subprocess.run(instruction, shell=True, capture_output=True, text=True, timeout=timeout) else: print("timeout参数错误,需要自查程序逻辑!") #output = result.stdout if result.stdout else result.stderr #-o 的命令需要处理 parsed_arg = self.parse_output_params(instruction) if parsed_arg: fole_out = self.read_output_file(parsed_arg) stderr ="" stdout = fole_out else: stderr = result.stderr stdout = result.stdout # 第三步:分析执行结果 # if result.returncode == 0: # 执行正确 # output = stdout if stdout: output = stdout else: if result.returncode ==28: #curl 命令超时 output = "指令执行超时!" else: #执行错误只拿错误信息 output = stderr if isinstance(output, bytes): # 若是bytes则转成str output = output.decode('utf-8', errors='ignore') analysis = self.analyze_result(output, instruction, stderr, stdout) if not analysis: # analysis为“” 不提交LLM ext_params.is_user = True return True, instruction, analysis, output, ext_params except subprocess.TimeoutExpired: ext_params.is_user = True #对于超时的也需要人工进行确认,是否是预期的超时 return False, instruction, f"执行超时:{str(timeout)}秒", "", ext_params # 执行失败,提交给人工确认指令的正确性 except Exception as e: ext_params.is_user = True return False,instruction,f"执行失败:{str(e)}","",ext_params #执行失败,提交给人工确认指令的正确性 @abc.abstractmethod def validate_instruction(self, instruction:str): """指令合法性分析 :return: str:非空代表合法(函数内会涉及修改指令),为空代表非法已取消指令 int:表示超时时间,0不设置超时时间,正值具体的超时时间设置。单位秒 """ pass @abc.abstractmethod def analyze_result(self, result:str,instruction:str,stderr:str,stdout:str) -> str: """分析执行结果""" pass def read_output_file_test(filename): """ 读取 -o 参数指定的文件内容 """ try: with open(filename, "r") as f: return f.read() except FileNotFoundError: print(f"错误: 文件 {filename} 不存在") except PermissionError: print(f"错误: 无权限读取文件 {filename}") return "" if __name__ =="__main__": pass