Skip to content

Instantly share code, notes, and snippets.

@nopslider
Last active October 14, 2018 08:06
Show Gist options
  • Save nopslider/0beb1f2f45cb2cf2c0f96d27e5204717 to your computer and use it in GitHub Desktop.
Save nopslider/0beb1f2f45cb2cf2c0f96d27e5204717 to your computer and use it in GitHub Desktop.
Qualys Compare
#!/usr/bin/python3
import xml.etree.ElementTree as ET
import argparse
import ipaddress
parser = argparse.ArgumentParser()
parser.add_argument("firstscan")
parser.add_argument("secondscan")
parser.add_argument("-s","--severitylevel",type=int,choices=[0,1,2,3],default=1,help="Filter vulnerabilities of this severity level and lower")
args = parser.parse_args()
severitydesc = ["INFO","LOW","MEDIUM","HIGH","CRITICAL"]
firsttree = ET.parse(args.firstscan)
firstroot = firsttree.getroot()
secondtree = ET.parse(args.secondscan)
secondroot = secondtree.getroot()
firstscanhosts = {}
secondscanhosts = {}
newips = set()
lostips = set()
countofcurrentvulns = [0,0,0,0,0]
countofpreviousvulns = [0,0,0,0,0]
firsttargets = set()
secondtargets = set()
countfirsttargets = 0
countsecondtargets = 0
firstuniqvulns = [{},{},{},{},{}]
countfirstuniqvulns = 0
seconduniqvulns = [{},{},{},{},{}]
countseconduniqvulns = 0
for ip in firstroot.iter('IP'):
address = ip.attrib["value"]
firstscanhosts[address] = set()
vulns = ip.find('VULNS')
if not vulns:
continue
for cat in vulns.iter('CAT'):
try:
proto = cat.attrib["protocol"]
except KeyError:
proto = "Unknown"
try:
port = cat.attrib["port"]
except KeyError:
port = "Unknown"
for vuln in cat.iter('VULN'):
title = vuln.find('TITLE').text
severity = int(vuln.attrib["severity"]) - 1
countofpreviousvulns[severity] = countofpreviousvulns[severity] + 1
if severity >= args.severitylevel:
firstscanhosts[address].add((title,severity,proto,port))
if title not in firstuniqvulns[severity]:
firstuniqvulns[severity][title] = 1
countfirstuniqvulns = countfirstuniqvulns + 1
else:
firstuniqvulns[severity][title] = firstuniqvulns[severity][title] + 1
for ip in secondroot.iter('IP'):
address = ip.attrib["value"]
secondscanhosts[address] = set()
vulns = ip.find('VULNS')
if address not in firstscanhosts:
newips.add(address)
if not vulns:
continue
for cat in vulns.iter('CAT'):
try:
proto = cat.attrib["protocol"]
except KeyError:
proto = "Unknown"
try:
port = cat.attrib["port"]
except KeyError:
port = "Unknown"
for vuln in cat.iter('VULN'):
title = vuln.find('TITLE').text
severity = int(vuln.attrib["severity"]) - 1
countofcurrentvulns[severity] = countofcurrentvulns[severity] + 1
if severity >= args.severitylevel:
secondscanhosts[address].add((title,severity,proto,port))
if title not in seconduniqvulns[severity]:
seconduniqvulns[severity][title] = 1
countseconduniqvulns = countseconduniqvulns + 1
else:
seconduniqvulns[severity][title] = seconduniqvulns[severity][title] + 1
for ip in firstscanhosts:
if ip not in secondscanhosts:
lostips.add(ip)
print("\nNumber of live hosts:")
print("\n {} - previously {}\n".format(len(secondscanhosts),len(firstscanhosts)))
print("Number of vulnerabilities:\n")
for i,count in enumerate(countofcurrentvulns):
if i >= args.severitylevel:
print(" {}\t{} (previously {})".format(count,severitydesc[i],countofpreviousvulns[i]))
print("\nNumber of unique vulnerabilities:\n")
for severity in range(4,args.severitylevel-1,-1):
print(" {} {}, previously {}".format(severitydesc[severity],len(seconduniqvulns[severity]),len(firstuniqvulns[severity])))
print("\nUnique vulnerabilities ({}, previously {}):\n".format(countseconduniqvulns,countfirstuniqvulns))
for severity in range(4,args.severitylevel-1,-1):
for vuln in seconduniqvulns[severity]:
if vuln in firstuniqvulns[severity]:
print(" {} ({}) - {} hosts (previously {} hosts)".format(vuln,severitydesc[severity],seconduniqvulns[severity][vuln],firstuniqvulns[severity][vuln]))
else:
print(" + {} ({}) - {} hosts (NEW VULNERABILITY)".format(vuln,severitydesc[severity],seconduniqvulns[severity][vuln]))
print("\nUnique vulnerabilities TSV ({}, previously {}):\n".format(countseconduniqvulns,countfirstuniqvulns))
for severity in range(4,args.severitylevel-1,-1):
for vuln in seconduniqvulns[severity]:
if vuln in firstuniqvulns[severity]:
print("{}|{}|{}|{}".format(vuln,severitydesc[severity],seconduniqvulns[severity][vuln],firstuniqvulns[severity][vuln]))
else:
print("{}|{}|{}|0".format(vuln,severitydesc[severity],seconduniqvulns[severity][vuln]))
found = False
for severity in range(4,args.severitylevel-1,-1):
for vuln in firstuniqvulns[severity]:
if vuln not in seconduniqvulns[severity]:
if not found:
print("\nRemediated Vulnerabilities:\n")
found = True
print(" - {} no longer present (was previously reported on {} hosts)".format(vuln,firstuniqvulns[severity][vuln]))
found = False
for severity in range(4,args.severitylevel-1,-1):
for vuln in firstuniqvulns[severity]:
if vuln not in seconduniqvulns[severity]:
if not found:
print("\nRemediated Vulnerabilities TSV:\n")
found = True
print("{}|{}|0|{}".format(vuln,severitydesc[severity],firstuniqvulns[severity][vuln]))
if len(newips) > 0:
print("\nThese are new IPs seen in the second scan ({}):\n".format(len(newips)))
for ip in newips:
vulns = sorted(secondscanhosts[ip], key = lambda element : element[1],reverse=True)
if len(vulns) > 0:
print("{} ({} vulnerabilities):".format(ip,len(vulns)))
for vuln in vulns:
print(" + {} ({})".format(vuln[0],vuln[1]))
else:
if args.severitylevel == 0:
print("{}\t(No vulnerabilities)".format(ip))
else:
print("{}\t(No vulnerabilities reported - NB: excluding vulnerabilities rated below {})".format(ip,severitydesc[args.severitylevel]))
print("\n")
if len(lostips) > 0:
print("These are IPs that were previously seen, but are not present in the second scan ({}):\n".format(len(lostips)))
print("\n".join(lostips))
print("\n")
change = False
for host in secondscanhosts:
if (host not in newips) and (host not in lostips):
if firstscanhosts[host] != secondscanhosts[host]:
if not change:
change = True
print("The following hosts have changed between scans:")
print("\n{}\tFirst scan: {}\tSecond scan: {}".format(host,len(firstscanhosts[host]),len(secondscanhosts[host])))
print("----------------------------------------------------")
firstvulns = sorted(firstscanhosts[host], key = lambda element : element[1],reverse=True)
secondvulns = sorted(secondscanhosts[host], key = lambda element : element[1],reverse=True)
for vuln in firstvulns:
if vuln not in secondvulns:
print(" - {} ({}/{}) ({})".format(vuln[0],vuln[3],vuln[2],severitydesc[vuln[1]]))
for vuln in secondvulns:
if vuln not in firstvulns:
print(" + {} ({}/{}) ({})".format(vuln[0],vuln[3],vuln[2],severitydesc[vuln[1]]))
if not change:
print("No changes between scans.")
for host in secondscanhosts:
secondvulns = sorted(secondscanhosts[host], key = lambda element : element[1],reverse=True)
if len(secondvulns) > 0:
print("\nCurrent risk profile for {}".format(host))
print("----------------------------------------------------")
for vuln in secondvulns:
print(" {} ({}/{}) ({})".format(vuln[0],vuln[3],vuln[2],severitydesc[vuln[1]]))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment