本专栏内容主要用于渗透测试工程师应对在工作中的自动化操作难题,高效摸鱼专用
解决问题
1、对Nessus导出的html报告进行自动化的提取操作,包括IP地址,漏洞个数,漏洞等级,漏洞描述,CVE编号等
2、由于Nessus导出的报告内容只有英文,因此对导出的信息进行自动化在线翻译
3、对导出的信息进行自动化处理,形成word文档报告表格
4、支持对单个报告大量漏洞数量的快速处理
TIP:由于不同方式导出的报告html格式可能不同,该脚本可能暂时只支持以下导出格式,但是其解析思路是通用的,各位大佬可以根据其思路自行开发
以下是我对上述导出报告的html文件的规律分析,也是我对于各个元素的定位参考
根据分析脚本的思路就已经非常明确了:
1、使用Xpath定位到第一个IP位置:/html/body/div[1]/div[3]/div[1]
2、通过相对位置分别定位到其他描述,漏洞个数就是div+1,开始时间,结束时间div+3,系统信息div+5,第一个漏洞名称div+7,第n个漏洞名称就是div+(n-1)3+7,第n个漏洞描述是div+(n-1)3+8
3、对于漏洞的信息,进行修正,漏洞描述,漏洞详情,相关链接,漏洞等级等项,每一个占据2个div,缺失或者增加进行修正即可
4、对于第n个IP的信息,则与之前的IP漏洞个数有关,其相对位置为+7+(高中低信息总个数*3)tip:这里的个数不能直接使用定位到的即div[2]中的个数,和报告中的不相符合,Nessus可能会将漏洞进行合并
源代码如下:
from lxml import etree
import re
class ALocation:
''''
该类中包含有所有的定位函数,其功能主要就是从html文件中定位出所有我们需要的信息
'''
def __init__(self,path,BaseLocation):
self.BaseLocation=BaseLocation #每个IP名称的div位置,即相对基地址
self.target_IP="" #用于保存该基地址下对应的IP地址
self.Critical_number=0 #div[2]中展示的紧急漏洞的个数
self.High_number=0 #div[2]中展示高危漏洞的个数
self.midium_number=0 #div[2]中展示中危漏洞的个数
self.low_number=0 #div[2]中展示低危漏洞的个数
self.vul_Critical_list=[] #div+(n-1)*3+7定位到的紧急漏洞列表
self.vul_Advance_list=[] #div+(n-1)*3+7定位到的高危漏洞列表
self.vul_midium_list=[] #div+(n-1)*3+7定位到的中危漏洞列表
self.vul_low_list=[] #div+(n-1)*3+7定位到的低危漏洞列表
self.Vul_Sum=[] #定位到漏洞描述
self.vul_descrition=[] #定位到的详情,tip:由于漏洞描述的篇幅较大,尽可能减少一次性的保存,单个保读取更加节约内存,所以该函数需要的是通过漏洞名定位到其描述的方法
self.Information=[] #信息列表
self.parser=etree.HTMLParser(encoding="utf-8")
self.XmlTree=etree.parse(path,parser=self.parser)
self.BaseXpath='/html/body/div[1]/div[3]/'
def LocalToIP(self):
#定位以该基地址为起点的IP地址,其格式为一个IP字符串
IP=self.XmlTree.xpath(self.BaseXpath+'div['+str(self.BaseLocation)+']/text()')
self.target_IP=IP[0]
return self.target_IP
def LocalToCritical(self):
#定位该基地址中的紧急漏洞的个数,但是其个数可能会与报告中不同
Critical=self.XmlTree.xpath(self.BaseXpath+'div['+str(int(self.BaseLocation)+1)+']/table/tbody/tr[1]/td[1]/div/text()')
self.Critical_number=int(Critical[0])
return self.Critical_number
def LocalToAdvance(self):
#定位该基地址中高危漏洞的个数,但是其个数可能会与报告中不同
Advance=self.XmlTree.xpath(self.BaseXpath+'div['+str(int(self.BaseLocation)+1)+']/table/tbody/tr[1]/td[2]/div/text()')
self.High_number=int(Advance[0])
return self.High_number
def LocalToMidiun(self):
#定位该基地址中中危漏洞的个数,但是其个数可能会与报告中不同
medium=self.XmlTree.xpath(self.BaseXpath+'div['+str(int(self.BaseLocation)+1)+']/table/tbody/tr[1]/td[3]/div/text()')
self.midium_number=int(medium[0])
return self.midium_number
def LocalToLow(self):
#定位低危漏洞的个数,但是其个数可能会与报告中不同
Low=self.XmlTree.xpath(self.BaseXpath+'div['+str(int(self.BaseLocation)+1)+']/table/tbody/tr[1]/td[4]/div/text()')
self.low_number=int(Low[0])
#print(self.low_number)
return self.low_number
#本来还需要定位信息个数,但是由于这里的信息数目个数不在报告之内,所以舍去
def LocalToVul(self):
i=self.BaseLocation+7
#定位该基地址下所有的漏洞名称,即div+7+(n-1)*3,根据颜色来判断漏洞等级,为了避免透露扫描器特征,将漏洞编号剔除,保留漏洞名称
vul=self.XmlTree.xpath(self.BaseXpath+'div['+str(i)+']/@style')
while(vul!=[]):
vul=self.XmlTree.xpath(self.BaseXpath+'div['+str(i)+']/@style')
if '#91243E' in str(vul):
data=self.XmlTree.xpath(self.BaseXpath+'div['+str(i)+']/text()')
data[0]=data[0].split("-",1)[1]
self.vul_Critical_list.append(data[0])
if '#DD4B50' in str(vul):
data=self.XmlTree.xpath(self.BaseXpath+'div['+str(i)+']/text()')
data[0]=data[0].split("-",1)[1]
self.vul_Advance_list.append(data[0])
if '#F18C43' in str(vul):
data=self.XmlTree.xpath(self.BaseXpath+'div['+str(i)+']/text()')
data[0]=data[0].split("-",1)[1]
self.vul_midium_list.append(data[0])
if '#F8C851' in str(vul):
data=self.XmlTree.xpath(self.BaseXpath+'div['+str(i)+']/text()')
data[0]=data[0].split("-",1)[1]
self.vul_low_list.append(data[0])
if '#67ACE1' in str(vul):
data=self.XmlTree.xpath(self.BaseXpath+'div['+str(i)+']/text()')
data[0]=data[0].split("-",1)[1]
self.Information.append(data[0])
i=i+3
vul=self.XmlTree.xpath(self.BaseXpath+'div['+str(i)+']/@style')
def get_vul_index(self,name):
#通过漏洞的名称来获取该漏洞位于该IP地址的相对位置编号,基于此判断漏洞的div地址
for i in self.vul_Critical_list:
if name==i:
return self.vul_Critical_list.index(i)
for i in self.vul_Advance_list:
if name==i:
return len(self.vul_Critical_list)+self.vul_Advance_list.index(i)
#此处不能使用之前定位的漏洞数量,因为报告中会进行合并
for i in self.vul_midium_list:
if name==i:
return len(self.vul_Critical_list)+len(self.vul_Advance_list)+self.vul_midium_list.index(i)
for i in self.vul_low_list:
if name==i:
return len(self.vul_Critical_list)+len(self.vul_Advance_list)+len(self.vul_midium_list)+self.vul_low_list.index(i)
def Check_Base(self,name,condition):
#对漏洞信息里的div位置进行修正
index=self.get_vul_index(name)
i=self.BaseLocation+8
div=0
contents=self.XmlTree.xpath(self.BaseXpath+'div['+str(i+index*3)+']')
for e in contents:
dis=e.xpath('string(.)')
if condition==0:#定位解决方案的时候使用
if "See Also" not in dis:
div=div-1
if condition==1:#定位CVE编码的时候使用
if 'CVSS v3.0 Base Score' in dis:
div=div+1
if 'CVSS v2.0 Base Score' in dis:
div=div+1
if 'CVSS v3.0 Temporal Score' in dis:
div=div+1
if 'CVSS v2.0 Temporal Score' in dis:
div=div+1
if "STIG Severity" in dis:
div=div+1
if "See Also" not in dis:
div=div-1
if condition==2:#定位插件输出的时候使用
if 'CVSS v3.0 Base Score' in dis:
div=div+1
if 'CVSS v2.0 Base Score' in dis:
div=div+1
if 'CVSS v3.0 Temporal Score' in dis:
div=div+1
if 'CVSS v2.0 Temporal Score' in dis:
div=div+1
if "STIG Severity" in dis:
div=div+1
if "See Also" not in dis:
div=div-1
if "References" not in dis:
div=div-1
if "Exploitable With" in dis:
div=div+1
return div
def LocalToDir(self,name):
#通过漏洞名称定位到漏洞描述
index=self.get_vul_index(name)
i=self.BaseLocation+8
dis=self.XmlTree.xpath(self.BaseXpath+'div['+str(i+index*3)+']/div[2]/text()')
return dis
def LocalToPluginsOut(self,name):
#通过漏洞名称定位到报告中的插件输出部分
i=self.BaseLocation+8
index=self.get_vul_index(name)
plug_out=[]
number=self.Check_Base(name,2)*2+17
Pl=self.XmlTree.xpath(self.BaseXpath+'div['+str(i+index*3)+']/div['+str(number)+']/text()')
while(Pl):
plug_out.append(Pl)
number=number+3
Pl=self.XmlTree.xpath(self.BaseXpath+'div['+str(i+index*3)+']/div['+str(number)+']/text()')
return plug_out
def LocalToCVE(self,name):
#根据漏洞名称定位其cve编号
begin=self.BaseLocation+8
cve_list=[]
index=0
index=self.get_url_Index(name)
number=self.Check_Base(name,1)*2+12
Pl=self.XmlTree.xpath(self.BaseXpath+'div['+str(begin+index*3)+']/div['+str(number)+']/table/tbody/tr[1]/td[2]/a/text()')
j=1
while(Pl):
Pl=self.XmlTree.xpath(self.BaseXpath+'div['+str(begin+index*3)+']/div['+str(number)+']/table/tbody/tr['+str(j)+']/td[2]/a/text()')
if 'CVE' in str(Pl):
cve_list.append(Pl)
j=j+1
return cve_list
def LocalToSolution(self,name):
#通过漏洞名称定位漏洞解决方案
index=self.get_vul_index(name)
number=self.Check_Base(name,0)*2+8
i=self.BaseLocation+8
Pl=self.XmlTree.xpath(self.BaseXpath+'div['+str(i+index*3)+']/div['+str(number)+']/text()')
return Pl
class Totall:
#该结构体主要是通过之前IP漏洞信息,定位到下一个漏洞信息,每个漏洞或者信息在报告中占据3个div
def __init__(self,path):
self.path=path #目标html地址
self.wordtask=[] #列表中的每一个元素,代表一个IP地址以及其对应的漏洞信息
self.BeginBaseLocation=1 #即第一个IP的基地址
def get_list(self):
one=ALocation(self.path,self.BeginBaseLocation)
IP=one.LocalToIP()
pattern =re.compile(r'(([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])\.){3}([1-9]?\d|1\d\d|2[0-4]\d|25[0-5])')
"因为报告中还有其他信息,通过正则判断定位到的是否为IP地址"
result=pattern.findall(IP)
one.LocalToVul()
if result!=[]:
self.wordtask.append(one)
while(result!=[]):
totallen=len(one.vul_Advance_list)+len(one.vul_Critical_list)+len(one.vul_midium_list)+len(one.vul_low_list)+len(one.Information)
self.BeginBaseLocation=self.BeginBaseLocation+7+totallen*3
one=ALocation(self.path,self.BeginBaseLocation)
IP=one.LocalToIP(self)
one.LocalToVul(self.BeginBaseLocation)
result=pattern.findall(IP)
if result!=[]:
self.wordtask.append(one)