#!/usr/bin/env python
"""
Faraday Penetration Test IDE
Copyright (C) 2016 Infobyte LLC (http://www.infobytesec.com/)
See the file 'doc/LICENSE' for the license information
"""
import xml.etree.ElementTree as ET
from urllib.parse import urlparse
from dateutil.parser import parse
from faraday_plugins.plugins.plugin import PluginXMLFormat
__author__ = 'Blas Moyano'
__copyright__ = 'Copyright 2020, Faraday Project'
__credits__ = ['Blas Moyano']
__license__ = ''
__version__ = '1.0.0'
__status__ = 'Development'
class QualysWebappParser:
def __init__(self, xml_output):
self.tree = self.parse_xml(xml_output)
if self.tree:
if self.tree.find('RESULTS/WEB_APPLICATION'):
self.info_results = self.get_results_vul(self.tree.find('RESULTS/WEB_APPLICATION'))
else:
self.info_results = self.get_results_vul(self.tree.find('RESULTS'))
self.info_glossary = self.get_glossary_qid(self.tree.find('GLOSSARY'))
self.info_appendix = self.get_appendix(self.tree.find('APPENDIX'))
else:
self.tree = None
def parse_xml(self, xml_output):
try:
tree = ET.fromstring(xml_output)
except SyntaxError as err:
print(f'SyntaxError In xml: {err} {xml_output}')
return None
return tree
@staticmethod
def get_appendix(tree):
for appendix_tags in tree:
yield Appendix(appendix_tags)
@staticmethod
def get_glossary_qid(tree):
for glossary_tags in tree.find('QID_LIST'):
yield Glossary(glossary_tags)
@staticmethod
def get_results_vul(tree):
for results_tags in tree.find('VULNERABILITY_LIST'):
yield Results(results_tags)
class Appendix:
def __init__(self, appendix_tags):
if appendix_tags.tag == 'SCAN_LIST':
self.lista_scan = self.get_scan(appendix_tags.find('SCAN'))
elif appendix_tags.tag == 'WEBAPP':
self.lista_webapp = self.get_webapp(appendix_tags)
@staticmethod
def get_scan(appendix_tags):
result_scan = {}
for scan in appendix_tags:
result_scan[scan.tag] = scan.text
return result_scan
@staticmethod
def get_webapp(appendix_tags):
result_webapp = {}
for webapp in appendix_tags:
result_webapp[webapp.tag] = webapp.text
return result_webapp
class Glossary:
def __init__(self, glossary_tags):
self.lista_qid = self.get_qid_list(glossary_tags)
def get_qid_list(self, qid_list_tags):
self.dict_result_qid = {}
for qid in qid_list_tags:
self.dict_result_qid[qid.tag] = qid.text
return self.dict_result_qid
class Results:
def __init__(self, glossary_tags):
self.lista_vul = self.get_qid_list(glossary_tags)
def get_qid_list(self, vul_list_tags):
self.dict_result_vul = {}
for vul in vul_list_tags:
self.dict_result_vul[vul.tag] = vul.text
return self.dict_result_vul
class QualysWebappPlugin(PluginXMLFormat):
def __init__(self, *arg, **kwargs):
super().__init__(*arg, **kwargs)
self.identifier_tag = ["WAS_WEBAPP_REPORT", "WAS_SCAN_REPORT"]
self.id = 'QualysWebapp'
self.name = 'QualysWebapp XML Output Plugin'
self.plugin_version = '1.0.0'
self.version = '1.0.0'
self.framework_version = '1.0.0'
self.options = None
self.protocol = None
self.port = '80'
self.address = None
def parseOutputString(self, output):
hostnames = []
parser = QualysWebappParser(output)
if not parser.info_appendix:
return
self.scan_list_result = []
for host_create in parser.info_appendix:
self.scan_list_result.append(host_create)
operating_system = ""
for k in self.scan_list_result:
if 'result_scan' in k.__dict__:
self.credential = k.lista_scan.get('AUTHENTICATION_RECORD')
elif 'result_webapp' in k.__dict__:
operating_system = k.lista_webapp.get('OPERATING_SYSTEM')
if k.lista_webapp.get('URL'):
initial_url = k.lista_webapp.get('URL')
parsed_url = urlparse(initial_url)
hostnames = [parsed_url.netloc]
glossary = []
for glossary_qid in parser.info_glossary:
glossary.append(glossary_qid.dict_result_qid)
for v in parser.info_results:
url = urlparse(v.dict_result_vul.get('URL'))
host_id = self.createAndAddHost(name=url.netloc, os=operating_system, hostnames=hostnames)
vuln_scan_id = v.dict_result_vul.get('QID')
# Data in the xml is in different parts, we look into the glossary
vuln_data = next((item for item in glossary if item["QID"] == vuln_scan_id), None)
vuln_name = vuln_data.get('TITLE')
vuln_desc = vuln_data.get('DESCRIPTION')
vuln_CWE = [vuln_data.get('CWE', '')]
raw_severity = int(vuln_data.get('SEVERITY', 0))
vuln_severity = raw_severity - 1
if not v.dict_result_vul.get('FIRST_TIME_DETECTED'):
run_date = ''
else:
run_date = parse(v.dict_result_vul.get('FIRST_TIME_DETECTED'))
vuln_resolution = vuln_data.get('SOLUTION')
cvss3 = {}
vuln_data_add = f"ID: {v.dict_result_vul.get('ID')}, DETECTION_ID: {v.dict_result_vul.get('DETECTION_ID')}" \
f", CATEGORY: {vuln_data.get('CATEGORY')}, GROUP: {vuln_data.get('GROUP')}" \
f", URL: {v.dict_result_vul.get('URL')}, IMPACT: {vuln_data.get('IMPACT')}"
self.createAndAddVulnToHost(host_id=host_id, name=vuln_name, desc=vuln_desc,
severity=vuln_severity, resolution=vuln_resolution, run_date=run_date,
external_id="QUALYS-"+vuln_scan_id, data=vuln_data_add, cwe=vuln_CWE,
cvss3=cvss3)
def createPlugin(*args, **kwargs):
return QualysWebappPlugin(*args, **kwargs)