diff --git a/configs/test.ini b/configs/test.ini index ff5abb2..fdaa38a 100755 --- a/configs/test.ini +++ b/configs/test.ini @@ -24,13 +24,13 @@ db_path=/opt/VulnWhisperer/data/database trash=false verbose=false -[qualys_web] +[qualys_was] #Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API enabled=true -hostname=qualys_web +hostname=qualys_was username=exampleuser password=examplepass -write_path=/opt/VulnWhisperer/data/qualys_web/ +write_path=/opt/VulnWhisperer/data/qualys_was/ db_path=/opt/VulnWhisperer/data/database verbose=false @@ -40,13 +40,13 @@ max_retries=10 # Template ID will need to be retrieved for each document. Please follow the reference guide above for instructions on how to get your template ID. template_id=289109 -[qualys_vuln] +[qualys_vm] #Reference https://www.qualys.com/docs/qualys-was-api-user-guide.pdf to find your API enabled=true -hostname=qualys_vuln +hostname=qualys_vm username=exampleuser password=examplepass -write_path=/opt/VulnWhisperer/data/qualys_vuln/ +write_path=/opt/VulnWhisperer/data/qualys_vm/ db_path=/opt/VulnWhisperer/data/database verbose=false @@ -83,8 +83,8 @@ verbose=false dns_resolv=False #Sample jira report scan, will automatically be created for existent scans -#[jira.qualys_vuln.test_scan] -#source=qualys_vuln +#[jira.qualys_vm.test_scan] +#source=qualys_vm #scan_name=Test Scan #jira_project=PROJECT ; if multiple components, separate by ","=None diff --git a/resources/elk6/pipeline/2000_qualys_web_scans.conf b/resources/elk6/pipeline/2000_qualys_web_scans.conf index ce28f17..a7226be 100644 --- a/resources/elk6/pipeline/2000_qualys_web_scans.conf +++ b/resources/elk6/pipeline/2000_qualys_web_scans.conf @@ -6,19 +6,19 @@ input { file { - path => [ "/opt/VulnWhisperer/data/qualys_vuln/*.json" ] + path => [ "/opt/VulnWhisperer/data/qualys_vm/*.json" ] codec => json start_position => "beginning" - tags => [ "qualys_vuln" ] + tags => [ "qualys_vm" ] mode => "read" start_position => "beginning" file_completed_action => "delete" } file { - path => [ "/opt/VulnWhisperer/data/qualys_web/*.json" ] + path => [ "/opt/VulnWhisperer/data/qualys_was/*.json" ] codec => json start_position => "beginning" - tags => [ "qualys_web" ] + tags => [ "qualys_was" ] mode => "read" start_position => "beginning" file_completed_action => "delete" @@ -26,7 +26,7 @@ input { } filter { - if "qualys_vuln" in [tags] or "qualys_web" in [tags] { + if "qualys_vm" in [tags] or "qualys_was" in [tags] { date { match => [ "scan_time", "UNIX" ] target => "@timestamp" @@ -82,7 +82,7 @@ filter { } } output { - if "qualys_vuln" in [tags] or "qualys_web" in [tags] { + if "qualys_vm" in [tags] or "qualys_was" in [tags] { if [@metadata][id] { elasticsearch { hosts => [ "elasticsearch:9200" ] diff --git a/tests/test-docker.sh b/tests/test-docker.sh index ff9be5d..92e6239 100755 --- a/tests/test-docker.sh +++ b/tests/test-docker.sh @@ -89,20 +89,20 @@ else fi # Test Qualys signature:OpenSSL Multiple Remote Security Vulnerabilities -qualys_vuln_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-*/_search?q=tags:qualys_vuln%20AND%20ip:%22176.28.50.164%22%20AND%20signature:%22OpenSSL%20Multiple%20Remote%20Security%20Vulnerabilities%22%20AND%20port:465" | jq '.hits.hits[]._source') +qualys_vm_doc=$(curl -s "$elasticsearch_url/logstash-vulnwhisperer-*/_search?q=tags:qualys_vm%20AND%20ip:%22176.28.50.164%22%20AND%20signature:%22OpenSSL%20Multiple%20Remote%20Security%20Vulnerabilities%22%20AND%20port:465" | jq '.hits.hits[]._source') # Test @timestamp -if echo $qualys_vuln_doc | jq '.["@timestamp"]' | grep -q '2019-03-30T10:17:41.000Z'; then +if echo $qualys_vm_doc | jq '.["@timestamp"]' | grep -q '2019-03-30T10:17:41.000Z'; then green "✅ Passed: Qualys VM @timestamp == 2019-03-30T10:17:41.000Z" else - red "❌ Failed: Qualys VM @timestamp == 2019-03-30T10:17:41.000Z was: $(echo $qualys_vuln_doc | jq '.["@timestamp"]') instead" + red "❌ Failed: Qualys VM @timestamp == 2019-03-30T10:17:41.000Z was: $(echo $qualys_vm_doc | jq '.["@timestamp"]') instead" ((return_code = return_code + 1)) fi # Test @XXXX -if echo $qualys_vuln_doc | jq '.cvss' | grep -q '5.6'; then +if echo $qualys_vm_doc | jq '.cvss' | grep -q '5.6'; then green "✅ Passed: Qualys VM cvss == 5.6" else - red "❌ Failed: Qualys VM cvss == 5.6 was: $(echo $qualys_vuln_doc | jq '.cvss') instead" + red "❌ Failed: Qualys VM cvss == 5.6 was: $(echo $qualys_vm_doc | jq '.cvss') instead" ((return_code = return_code + 1)) fi diff --git a/tests/test-vuln_whisperer.sh b/tests/test-vuln_whisperer.sh index 05d49f0..407915b 100755 --- a/tests/test-vuln_whisperer.sh +++ b/tests/test-vuln_whisperer.sh @@ -59,8 +59,8 @@ yellow "\n*********************************************" yellow "* Test two failed scans *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* -yellow "Removing ${TEST_PATH}/qualys_vuln/scan_1553941061.87241" -mv "${TEST_PATH}/qualys_vuln/scan_1553941061.87241"{,.bak} +yellow "Removing ${TEST_PATH}/qualys_vm/scan_1553941061.87241" +mv "${TEST_PATH}/qualys_vm/scan_1553941061.87241"{,.bak} if vuln_whisperer -F -c configs/test.ini --mock --mock_dir "${TEST_PATH}"; [[ $? -eq 2 ]]; then green "\n✅ Passed: Test two failed scans" else @@ -83,7 +83,7 @@ yellow "\n*********************************************" yellow "* Test only Qualys VM with one failed scan *" yellow "*********************************************" rm -rf /opt/VulnWhisperer/* -if vuln_whisperer -F -c configs/test.ini -s qualys_vuln --mock --mock_dir "${TEST_PATH}"; [[ $? -eq 1 ]]; then +if vuln_whisperer -F -c configs/test.ini -s qualys_vm --mock --mock_dir "${TEST_PATH}"; [[ $? -eq 1 ]]; then green "\n✅ Passed: Test only Qualys VM with one failed scan" else red "\n❌ Failed: Test only Qualys VM with one failed scan" @@ -91,7 +91,7 @@ else fi # Restore the removed files -mv "${TEST_PATH}/qualys_vuln/scan_1553941061.87241.bak" "${TEST_PATH}/qualys_vuln/scan_1553941061.87241" +mv "${TEST_PATH}/qualys_vm/scan_1553941061.87241.bak" "${TEST_PATH}/qualys_vm/scan_1553941061.87241" mv "${TEST_PATH}/nessus/GET_scans_exports_164_download.bak" "${TEST_PATH}/nessus/GET_scans_exports_164_download" exit $return_code diff --git a/vulnwhisp/frameworks/qualys_vuln.py b/vulnwhisp/frameworks/qualys_vm.py similarity index 98% rename from vulnwhisp/frameworks/qualys_vuln.py rename to vulnwhisp/frameworks/qualys_vm.py index 004e83a..eb420ba 100644 --- a/vulnwhisp/frameworks/qualys_vuln.py +++ b/vulnwhisp/frameworks/qualys_vm.py @@ -18,7 +18,7 @@ class qualysWhisperAPI(object): self.logger = logging.getLogger('qualysWhisperAPI') self.config = config try: - self.qgc = qualysapi.connect(config, 'qualys_vuln') + self.qgc = qualysapi.connect(config, 'qualys_vm') # Fail early if we can't make a request or auth is incorrect self.qgc.request('about.php') self.logger.info('Connected to Qualys at {}'.format(self.qgc.server)) diff --git a/vulnwhisp/frameworks/qualys_web.py b/vulnwhisp/frameworks/qualys_was.py similarity index 99% rename from vulnwhisp/frameworks/qualys_web.py rename to vulnwhisp/frameworks/qualys_was.py index 9634f70..ab918fd 100644 --- a/vulnwhisp/frameworks/qualys_web.py +++ b/vulnwhisp/frameworks/qualys_was.py @@ -38,7 +38,7 @@ class qualysWhisperAPI(object): self.logger = logging.getLogger('qualysWhisperAPI') self.config = config try: - self.qgc = qualysapi.connect(config, 'qualys_web') + self.qgc = qualysapi.connect(config, 'qualys_was') self.logger.info('Connected to Qualys at {}'.format(self.qgc.server)) except Exception as e: self.logger.error('Could not connect to Qualys: {}'.format(str(e))) @@ -46,7 +46,7 @@ class qualysWhisperAPI(object): #"content-type": "text/xml"} "Accept" : "application/json", "Content-Type": "application/json"} - self.config_parse = qcconf.QualysConnectConfig(config, 'qualys_web') + self.config_parse = qcconf.QualysConnectConfig(config, 'qualys_was') try: self.template_id = self.config_parse.get_template_id() except: diff --git a/vulnwhisp/test/mock.py b/vulnwhisp/test/mock.py index 15af41c..ce2d16e 100644 --- a/vulnwhisp/test/mock.py +++ b/vulnwhisp/test/mock.py @@ -37,16 +37,16 @@ class mockAPI(object): body=open('{}/{}/{}'.format(self.mock_dir, framework, filename)).read() ) - def qualys_vuln_callback(self, request, uri, response_headers): + def qualys_vm_callback(self, request, uri, response_headers): self.logger.info('Simulating response for {} ({})'.format(uri, request.body)) if 'list' in request.parsed_body['action']: return [200, response_headers, - open(self.qualys_vuln_path + '/scans').read()] + open(self.qualys_vm_path + '/scans').read()] elif 'fetch' in request.parsed_body['action']: try: response_body = open('{}/{}'.format( - self.qualys_vuln_path, + self.qualys_vm_path, request.parsed_body['scan_ref'][0].replace('/', '_')) ).read() except: @@ -54,7 +54,7 @@ class mockAPI(object): response_body = '' return [200, response_headers, response_body] - def create_qualys_vuln_resource(self, framework): + def create_qualys_vm_resource(self, framework): # Create health check endpoint self.logger.info('Adding mocked {} endpoint GET msp/about.php'.format(framework)) httpretty.register_uri( @@ -65,15 +65,15 @@ class mockAPI(object): self.logger.info('Adding mocked {} endpoint {} {}'.format(framework, 'POST', 'api/2.0/fo/scan')) httpretty.register_uri( httpretty.POST, 'https://{}:443/api/2.0/fo/scan/'.format(framework), - body=self.qualys_vuln_callback) + body=self.qualys_vm_callback) - def qualys_web_callback(self, request, uri, response_headers): + def qualys_was_callback(self, request, uri, response_headers): self.logger.info('Simulating response for {} ({})'.format(uri, request.body)) report_id = request.parsed_body.split('')[1].split('<')[0] - response_body = open('{}/create_{}'.format(self.qualys_web_path, report_id)).read() + response_body = open('{}/create_{}'.format(self.qualys_was_path, report_id)).read() return [200, response_headers, response_body] - def create_qualys_web_resource(self, framework): + def create_qualys_was_resource(self, framework): for filename in self.get_files('{}/{}'.format(self.mock_dir, framework)): if filename.startswith('POST') or filename.startswith('GET'): method, resource = filename.split('_', 1) @@ -87,7 +87,7 @@ class mockAPI(object): self.logger.info('Adding mocked {} endpoint {} {}'.format(framework, 'POST', 'qps/rest/3.0/create/was/report')) httpretty.register_uri( httpretty.POST, 'https://{}:443/qps/rest/3.0/create/was/report'.format(framework), - body=self.qualys_web_callback) + body=self.qualys_was_callback) def openvas_callback(self, request, uri, response_headers): self.logger.info('Simulating response for {} ({})'.format(uri, request.body)) @@ -116,12 +116,12 @@ class mockAPI(object): for framework in self.get_directories(self.mock_dir): if framework in ['nessus', 'tenable']: self.create_nessus_resource(framework) - elif framework == 'qualys_vuln': - self.qualys_vuln_path = self.mock_dir + '/' + framework - self.create_qualys_vuln_resource(framework) - elif framework == 'qualys_web': - self.qualys_web_path = self.mock_dir + '/' + framework - self.create_qualys_web_resource(framework) + elif framework == 'qualys_vm': + self.qualys_vm_path = self.mock_dir + '/' + framework + self.create_qualys_vm_resource(framework) + elif framework == 'qualys_was': + self.qualys_was_path = self.mock_dir + '/' + framework + self.create_qualys_was_resource(framework) elif framework == 'openvas': self.openvas_path = self.mock_dir + '/' + framework self.create_openvas_resource(framework) diff --git a/vulnwhisp/vulnwhisp.py b/vulnwhisp/vulnwhisp.py index f695570..28ce12c 100755 --- a/vulnwhisp/vulnwhisp.py +++ b/vulnwhisp/vulnwhisp.py @@ -18,8 +18,8 @@ from lxml import objectify from base.config import vwConfig from frameworks.nessus import NessusAPI from frameworks.openvas import OpenVAS_API -from frameworks.qualys_vuln import qualysVulnScan -from frameworks.qualys_web import qualysScanReport +from frameworks.qualys_vm import qualysVulnScan +from frameworks.qualys_was import qualysScanReport from reporting.jira_api import JiraAPI @@ -544,9 +544,9 @@ class vulnWhispererNessus(vulnWhispererBase): return self.exit_code -class vulnWhispererQualys(vulnWhispererBase): +class vulnWhispererQualysWAS(vulnWhispererBase): - CONFIG_SECTION = 'qualys_web' + CONFIG_SECTION = 'qualys_was' def __init__( self, config=None, @@ -556,8 +556,8 @@ class vulnWhispererQualys(vulnWhispererBase): debug=False, ): - super(vulnWhispererQualys, self).__init__(config=config, verbose=verbose, debug=debug) - self.logger = logging.getLogger('vulnWhispererQualys') + super(vulnWhispererQualysWAS, self).__init__(config=config, verbose=verbose, debug=debug) + self.logger = logging.getLogger('vulnWhispererQualysWAS') if not verbose: verbose = self.config.getbool(self.CONFIG_SECTION, 'verbose') self.logger.setLevel(logging.DEBUG if debug else logging.INFO if verbose else logging.WARNING) @@ -583,7 +583,7 @@ class vulnWhispererQualys(vulnWhispererBase): try: if 'Z' in launched_date: launched_date = self.qualys_scan.utils.iso_to_epoch(launched_date) - report_name = 'qualys_web_' + str(report_id) \ + report_name = 'qualys_was_' + str(report_id) \ + '_{last_updated}'.format(last_updated=launched_date) \ + '.{extension}'.format(extension=output_format) @@ -843,7 +843,7 @@ class vulnWhispererOpenVAS(vulnWhispererBase): class vulnWhispererQualysVuln(vulnWhispererBase): - CONFIG_SECTION = 'qualys_vuln' + CONFIG_SECTION = 'qualys_vm' def __init__( self, @@ -873,7 +873,7 @@ class vulnWhispererQualysVuln(vulnWhispererBase): cleanup=True): if 'Z' in launched_date: launched_date = self.qualys_scan.utils.iso_to_epoch(launched_date) - report_name = 'qualys_vuln_' + report_id.replace('/','_') \ + report_name = 'qualys_vm_' + report_id.replace('/','_') \ + '_{last_updated}'.format(last_updated=launched_date) \ + '.{extension}'.format(extension=output_format) @@ -1122,7 +1122,7 @@ class vulnWhispererJIRA(vulnWhispererBase): return vulnerabilities - def parse_qualys_vuln_vulnerabilities(self, fullpath, source, scan_name, min_critical, dns_resolv = False): + def parse_qualys_vm_vulnerabilities(self, fullpath, source, scan_name, min_critical, dns_resolv = False): #parsing of the qualys vulnerabilities schema #parse json vulnerabilities = [] @@ -1230,8 +1230,8 @@ class vulnWhispererJIRA(vulnWhispererBase): vulnerabilities = self.parse_nessus_vulnerabilities(fullpath, source, scan_name, min_critical) #***Qualys VM parsing*** - if source == "qualys_vuln": - vulnerabilities = self.parse_qualys_vuln_vulnerabilities(fullpath, source, scan_name, min_critical, dns_resolv) + if source == "qualys_vm": + vulnerabilities = self.parse_qualys_vm_vulnerabilities(fullpath, source, scan_name, min_critical, dns_resolv) #***JIRA sync*** if vulnerabilities: @@ -1286,8 +1286,8 @@ class vulnWhisperer(object): debug=self.debug) self.exit_code += vw.whisper_nessus() - elif self.profile == 'qualys_web': - vw = vulnWhispererQualys(config=self.config, + elif self.profile == 'qualys_was': + vw = vulnWhispererQualysWAS(config=self.config, verbose=self.verbose, debug=self.debug) self.exit_code += vw.process_web_assets() @@ -1305,7 +1305,7 @@ class vulnWhisperer(object): debug=self.debug) self.exit_code += vw.whisper_nessus() - elif self.profile == 'qualys_vuln': + elif self.profile == 'qualys_vm': vw = vulnWhispererQualysVuln(config=self.config, verbose=self.verbose, debug=self.debug)