|
|
@ -39,6 +39,7 @@ class TaskObject: |
|
|
|
#全局变量 |
|
|
|
self.app_work_type = myCongif.get_data("App_Work_type") #app工作为0时,只允许单步模式工作,是附加规则,不影响正常逻辑处理 |
|
|
|
self.brun = False #任务的停止可以用该变量来控制 |
|
|
|
self.max_layer = myCongif.get_data("max_node_layer") |
|
|
|
self.sleep_time = myCongif.get_data("sleep_time") |
|
|
|
self.target = test_target |
|
|
|
self.cookie = cookie_info |
|
|
@ -414,19 +415,32 @@ class TaskObject: |
|
|
|
|
|
|
|
async def put_instr_mq_async(self,node): |
|
|
|
#这里不做状态的判断,调用前处理 |
|
|
|
self.instr_node_queue.put(node) |
|
|
|
self.put_instr_node(node) |
|
|
|
await self.update_node_work_status_async(node,2) #在执行--1.work_status不影响整个任务的执行,错了问题不大,2--attack_tree持久化需要出去lock信息。 |
|
|
|
|
|
|
|
async def put_llm_mq_async(self,node): |
|
|
|
#同instr_mq |
|
|
|
self.llm_node_queue.put(node) |
|
|
|
self.put_llm_node(node) |
|
|
|
await self.update_node_work_status_async(node,4) #提交中 |
|
|
|
|
|
|
|
#修改节点的执行状态,并需要基于websocket推送到前端显示 同步线程调用 |
|
|
|
def update_node_work_status(self,node,work_status): |
|
|
|
#更新状态 |
|
|
|
bchange = node.update_work_status(work_status) #1,3会返回Flase |
|
|
|
#基于websocket推送到前端 |
|
|
|
if work_status != 1: #llm执行完成后会发送单独的指令更新树,所以不发送1更新节点了 |
|
|
|
#判断是否是web端最新获取数据的task |
|
|
|
if self.taskM.web_cur_task == self.task_id: |
|
|
|
idatatype = 1 |
|
|
|
strdata = {"node_path":node.path,"node_workstatus":work_status} |
|
|
|
asyncio.run(g_WSM.send_data(idatatype,strdata)) |
|
|
|
|
|
|
|
#web端调用 |
|
|
|
async def update_node_work_status_async(self,node,work_status): |
|
|
|
#更新状态 |
|
|
|
bchange = node.update_work_status(work_status) |
|
|
|
#基于websocket推送到前端 |
|
|
|
if bchange: |
|
|
|
if work_status != 1: |
|
|
|
#判断是否是web端最新获取数据的task |
|
|
|
if self.taskM.web_cur_task == self.task_id: |
|
|
|
idatatype = 1 |
|
|
@ -521,21 +535,32 @@ class TaskObject: |
|
|
|
node.step_num -= 1 |
|
|
|
self.put_instr_mq(node) #2-执行中 |
|
|
|
|
|
|
|
def put_work_node(self): |
|
|
|
'''遍历节点需要处理的任务,提交mq,load_task-在自动模式下-触发--线程安全''' |
|
|
|
def put_work_node(self,work_type): |
|
|
|
'''遍历节点需要处理的任务,提交mq,load_task-在自动模式下-触发--线程安全 |
|
|
|
work_type 0-人工,1-自动 |
|
|
|
''' |
|
|
|
instr_status = None |
|
|
|
llm_status = None |
|
|
|
if work_type == 0: |
|
|
|
instr_status = (2,) |
|
|
|
llm_status = (4,) |
|
|
|
else: |
|
|
|
instr_status = (1,2) |
|
|
|
llm_status = (3,4) |
|
|
|
|
|
|
|
nodes = self.attack_tree.traverse_bfs() |
|
|
|
for node in nodes: |
|
|
|
if not node.bwork: |
|
|
|
continue |
|
|
|
node_work_status = node.get_work_status() |
|
|
|
if node_work_status in (1,2): #待执行指令 |
|
|
|
if node_work_status in instr_status: #待执行指令 |
|
|
|
if node.is_instr_empty():#说明数据有问题了,放弃掉 |
|
|
|
node.update_work_status(-1) #置0 -1作为额外的条件参数 |
|
|
|
else: |
|
|
|
self.put_instr_node(node) #1,2都提交执行 |
|
|
|
node.update_work_status(-2)# 置2 |
|
|
|
#llm-list不处理,正常应该为空 |
|
|
|
elif node_work_status in (3,4): |
|
|
|
elif node_work_status in llm_status: |
|
|
|
if node.is_llm_empty():#数据有问题,放弃掉 |
|
|
|
node.update_work_status(-1) |
|
|
|
else: |
|
|
@ -551,11 +576,10 @@ class TaskObject: |
|
|
|
iwork_status = node.get_work_status() |
|
|
|
if iwork_status in (1,3): |
|
|
|
node.step_num = step_num - 1 #单步步次赋值 -1 规则提交时-1,执行结束连续判断再提交 |
|
|
|
|
|
|
|
if iwork_status == 1: |
|
|
|
self.put_instr_mq(node) |
|
|
|
await self.put_instr_mq_async(node) |
|
|
|
else: |
|
|
|
self.put_llm_mq(node) |
|
|
|
await self.put_llm_mq_async(node) |
|
|
|
return True,"已提交单步任务" |
|
|
|
else: |
|
|
|
error = "" |
|
|
@ -591,23 +615,11 @@ class TaskObject: |
|
|
|
else: |
|
|
|
return False,"当前的任务状态不允许执行单步,请检查!" |
|
|
|
|
|
|
|
#修改节点的执行状态,并需要基于websocket推送到前端显示 同步线程调用 |
|
|
|
def update_node_work_status(self,node,work_status): |
|
|
|
#更新状态 |
|
|
|
bchange = node.update_work_status(work_status) #1,3会返回Flase |
|
|
|
#基于websocket推送到前端 |
|
|
|
if work_status != 1: #llm执行完成后会发送单独的指令更新树,所以不发送1更新节点了 |
|
|
|
#判断是否是web端最新获取数据的task |
|
|
|
if self.taskM.web_cur_task == self.task_id: |
|
|
|
idatatype = 1 |
|
|
|
strdata = {"node_path":node.path,"node_workstatus":work_status} |
|
|
|
asyncio.run(g_WSM.send_data(idatatype,strdata)) |
|
|
|
|
|
|
|
#获取本次的提交提示词 |
|
|
|
def get_llm_prompt(self,llm_type,str_res,user_Prompt): |
|
|
|
if llm_type == 0: |
|
|
|
ext_Prompt = f''' |
|
|
|
初始信息:{str_res} |
|
|
|
补充信息:{str_res} |
|
|
|
任务:请开始对该目标的渗透测试工作。 |
|
|
|
''' |
|
|
|
elif llm_type == 1: # 提交指令执行结果 --- 正常提交 |
|
|
@ -649,10 +661,11 @@ class TaskObject: |
|
|
|
def add_children_node(self,parent_node,children_names,cur_message=None,status="未完成"): |
|
|
|
existing_names = {node.name for node in parent_node.children} # 现有子节点名称集合 |
|
|
|
unique_names = list(set(children_names)) # 去重 |
|
|
|
layer_num = parent_node.cur_layer + 1 |
|
|
|
for child_name in unique_names: |
|
|
|
if child_name not in existing_names: |
|
|
|
# 添加节点 |
|
|
|
new_node = TreeNode(child_name, parent_node.task_id, status) |
|
|
|
new_node = TreeNode(child_name, parent_node.task_id,layer_num,status) |
|
|
|
parent_node.add_child(new_node) |
|
|
|
#existing_names.add(child_name) # 更新集合 -- 已经去重过了,不需要在添加到比对 |
|
|
|
|
|
|
@ -680,10 +693,13 @@ class TaskObject: |
|
|
|
# #ad_instr_nodes --- 还没处理 |
|
|
|
|
|
|
|
residue_cmd_no_add = [] |
|
|
|
all_add_node = [] |
|
|
|
add_node_names = [] |
|
|
|
for node_json in node_cmds: |
|
|
|
action = node_json["action"] |
|
|
|
if action == "add_node": # 新增节点 |
|
|
|
if node.cur_layer >= self.max_layer: |
|
|
|
continue #节点层级达到上限后不允许再添加子节点-- 平级的一样 |
|
|
|
parent_node_name = node_json["parent"] |
|
|
|
# status = "未完成" #2025-4-11修改MGS-节点指令格式,取消了status |
|
|
|
add_node_names = node_json["nodes"].replace(',', ',').split(',') |
|
|
@ -691,53 +707,42 @@ class TaskObject: |
|
|
|
if node.name == parent_node_name or parent_node_name.endswith(node.name): # 2233ai,节点名称字段会返回整个路径 |
|
|
|
# 添加当前节点的子节点 -- 这是标准情况 |
|
|
|
self.add_children_node(node, add_node_names) |
|
|
|
all_add_node.extend(add_node_names) #只有当前节点的子节点才进行指令有无的校验补充 |
|
|
|
elif node.parent.name == parent_node_name or parent_node_name.endswith(node.parent.name): # 添加当前节点的平级节点 |
|
|
|
# 是添加当前节点的平级节点(当前节点的父节点下添加子节点) --使用2233ai-o3时遇到的情况 |
|
|
|
self.add_children_node(node.parent, add_node_names) |
|
|
|
self.logger.debug("遇到一次添加平级节点") |
|
|
|
else: |
|
|
|
badd = False |
|
|
|
for child_node in node.children: # 给子节点添加子节点 |
|
|
|
if parent_node_name == child_node.name or parent_node_name.endswith(child_node.name): |
|
|
|
badd = True |
|
|
|
self.add_children_node(child_node, add_node_names) |
|
|
|
self.logger.debug("遇到一次添加子节点的子节点") |
|
|
|
break |
|
|
|
if not badd: |
|
|
|
self.logger.error(f"添加子节点失败!父节点不是当前节点,不是当前节点的父节点,不是当前节点的子节点,需要介入!!{node_json}---当前节点为:{node.path}") # 丢弃该节点 |
|
|
|
else: # 未处理的节点指令添加到list |
|
|
|
residue_cmd_no_add.append(node_json) |
|
|
|
|
|
|
|
#处理on_instruction |
|
|
|
residue_node_cmds = [] |
|
|
|
no_instr_nodes = [] |
|
|
|
for node_cmd in residue_cmd_no_add: |
|
|
|
action = node_cmd["action"] |
|
|
|
if action == "no_instruction": |
|
|
|
node_names = node_cmd["nodes"].replace(',',',').split(',') |
|
|
|
for node_name in node_names: |
|
|
|
# 先判断是否在测试指令中,若在则不提交llm任务,只能接受在一次返回中同一节点有多条测试指令,不允许分次返回 |
|
|
|
bcommand = False |
|
|
|
for com in commands: |
|
|
|
if node_name in com: |
|
|
|
bcommand = True |
|
|
|
break |
|
|
|
if bcommand: # 如果存在测试指令,则不把该节点放入补充信息llm任务---尝试不对比是否有返回指令,DS会一直返回指令,还返回on_instruction |
|
|
|
continue |
|
|
|
#判断是否有对应节点---原则上只允许同批次add的节点没有添加指令的情况 |
|
|
|
if node_name in add_node_names: |
|
|
|
no_instr_nodes.append(node_name) |
|
|
|
else: |
|
|
|
self.logger.error("遇到一次不在add_node中,但在no_instr_nodes中的数据") |
|
|
|
#粗暴的做法,添加在当前节点下 |
|
|
|
self.add_children_node(node, [node_name]) |
|
|
|
no_instr_nodes.append(node_name) |
|
|
|
else:#剩余的节点指令 |
|
|
|
residue_node_cmds.append(node_cmd) |
|
|
|
#2025-5-12 是否采用本地校验节点是否有指令,如果使用,则no_instruction就可以不用了 |
|
|
|
for add_node in all_add_node: |
|
|
|
bcommand = False |
|
|
|
for com in commands: |
|
|
|
if add_node in com: |
|
|
|
bcommand = True |
|
|
|
break |
|
|
|
if bcommand: # 如果存在测试指令,则不把该节点放入补充信息llm任务---尝试不对比是否有返回指令,DS会一直返回指令,还返回on_instruction |
|
|
|
continue |
|
|
|
#没有对应指令 |
|
|
|
no_instr_nodes.append(add_node) |
|
|
|
if no_instr_nodes: # 阻塞式,在当前节点提交补充信息,完善节点指令 -- 优势是省token |
|
|
|
new_commands = self.get_other_instruction(no_instr_nodes, DBM, node) |
|
|
|
commands.extend(new_commands) |
|
|
|
|
|
|
|
#执行剩余的节点指令--不分先后 |
|
|
|
for node_json in residue_node_cmds:#2025-4-11重新调整了节点指令格式定义 |
|
|
|
for node_json in residue_cmd_no_add: |
|
|
|
action = node_json["action"] |
|
|
|
if action == "find_vul": |
|
|
|
node_name = node_json["node"] |
|
|
@ -754,7 +759,7 @@ class TaskObject: |
|
|
|
vul_node.vul_type = node_json["vulnerability"]["name"] |
|
|
|
vul_node.vul_grade = node_json["vulnerability"]["risk"] |
|
|
|
vul_node.vul_info = node_json["vulnerability"]["info"] |
|
|
|
#保存到数据库 |
|
|
|
#保存到数据库 --- 数据库有记录多个,tree只保留最新一个 |
|
|
|
DBM.insert_taks_vul(self.task_id,vul_node.name,vul_node.path,vul_node.vul_type,vul_node.vul_grade, |
|
|
|
vul_node.vul_info) |
|
|
|
except: |
|
|
@ -778,6 +783,7 @@ class TaskObject: |
|
|
|
def get_other_instruction(self,nodes,DBM,cur_node): |
|
|
|
res_str = ','.join(nodes) |
|
|
|
new_commands = [] |
|
|
|
no_instr_nodes = nodes |
|
|
|
ierror = 0 |
|
|
|
while res_str: |
|
|
|
self.logger.debug(f"开始针对f{res_str}这些节点请求测试指令") |
|
|
@ -791,15 +797,15 @@ class TaskObject: |
|
|
|
任务: |
|
|
|
1.请生成这些子节点的测试指令,注意不要生成重复的测试指令; |
|
|
|
2.这些节点的父节点为当前节点,请正确生成这些节点的节点路径; |
|
|
|
3.只有当还有节点未能生成测试指令或不完整时,才返回未生成指令的节点列表。 |
|
|
|
''' |
|
|
|
fake_prompt = self.DataFilter.filter_prompt(user_Prompt) |
|
|
|
#正常不应该会有node_cmds |
|
|
|
bsuccess,node_cmds, commands, reasoning_content, content = self.LLM.get_llm_instruction(fake_prompt, |
|
|
|
cur_node,self.DataFilter) # message要更新 |
|
|
|
if not bsuccess: |
|
|
|
self.logger.error(f"模型接口调用出错:{content}") |
|
|
|
ierror += 1 |
|
|
|
if ierror == 3: #重试3词 |
|
|
|
if ierror == 3: #重试3次 |
|
|
|
break |
|
|
|
continue# res_str没有调整,重复使用 |
|
|
|
res_str = "" |
|
|
@ -811,18 +817,21 @@ class TaskObject: |
|
|
|
self.logger.error(f"{cur_node.name}-llm入库失败!") |
|
|
|
#把返回的测试指令进行追加 |
|
|
|
new_commands.extend(commands) |
|
|
|
#判断是否还有未添加指令的节点 |
|
|
|
for node_json in node_cmds: #正常应该只有一条no_instruction --暂时只处理 |
|
|
|
if "no_instruction" in node_json and "nodes" in node_json: |
|
|
|
tmp_nodes = [] |
|
|
|
node_names = node_json["nodes"].replace(',',',').split(',') |
|
|
|
for node_name in node_names: |
|
|
|
if node_name in nodes: |
|
|
|
tmp_nodes.append(node_name) |
|
|
|
res_str = ','.join(tmp_nodes) |
|
|
|
break |
|
|
|
else:#其他节点指令不处理 |
|
|
|
self.logger.error(f"遇到一次no_instruction补充指令时返回了其他节点指令{node_cmds}") |
|
|
|
#再验证是否还有缺少的 |
|
|
|
tmp_nodes = [] |
|
|
|
for no_instr_node in no_instr_nodes: |
|
|
|
bcommand = False |
|
|
|
for com in commands: |
|
|
|
if no_instr_node in com: |
|
|
|
bcommand = True |
|
|
|
break |
|
|
|
if bcommand: # 如果存在测试指令,则不把该节点放入补充信息llm任务---尝试不对比是否有返回指令,DS会一直返回指令,还返回on_instruction |
|
|
|
continue |
|
|
|
# 没有对应指令 |
|
|
|
tmp_nodes.append(no_instr_node) |
|
|
|
res_str = ','.join(tmp_nodes) |
|
|
|
no_instr_nodes = tmp_nodes |
|
|
|
|
|
|
|
self.logger.debug("未添加指令的节点,都已完成指令的添加!") |
|
|
|
return new_commands |
|
|
|
|
|
|
@ -833,11 +842,12 @@ class TaskObject: |
|
|
|
if attack_tree: # 有值的情况是load |
|
|
|
self.attack_tree = attack_tree |
|
|
|
# 加载未完成的任务 |
|
|
|
if self.work_type == 1: # 自动模式 |
|
|
|
# 提交到mq,待线程执行 |
|
|
|
self.put_work_node() |
|
|
|
# if self.work_type == 1: # 自动模式 |
|
|
|
# # 提交到mq,待线程执行 |
|
|
|
self.put_work_node(self.work_type) |
|
|
|
|
|
|
|
else: # 无值的情况是new_create |
|
|
|
root_node = TreeNode(self.target, self.task_id) # 根节点 |
|
|
|
root_node = TreeNode(self.target, self.task_id,0) # 根节点 |
|
|
|
self.attack_tree = AttackTree(root_node) # 创建测试树,同时更新根节点相关内容 |
|
|
|
self.LLM.build_initial_prompt(root_node) # 对根节点初始化system-msg |
|
|
|
# 插入一个user消息 |
|
|
@ -938,6 +948,15 @@ class TaskObject: |
|
|
|
return b_over |
|
|
|
|
|
|
|
|
|
|
|
def test(self,task_id): |
|
|
|
root_node = TreeNode(self.target, task_id,0) # 根节点 |
|
|
|
self.attack_tree = AttackTree(root_node) # 创建测试树,同时更新根节点相关内容 |
|
|
|
self.LLM.build_initial_prompt(root_node) # 对根节点初始化system-msg |
|
|
|
# 初始保存个attack_tree文件 |
|
|
|
g_PKM.WriteData(self.attack_tree, str(task_id)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
pass |