From fbd26ffbd4539796c6667fb89f2924a39cc7a6e2 Mon Sep 17 00:00:00 2001 From: Tommy Bohde Date: Mon, 17 Dec 2018 09:28:55 -0500 Subject: [PATCH 1/6] initial commit --- analyzers/SecurityCenter/SecurityCenter.json | 69 +++++++++ analyzers/SecurityCenter/SecurityCenter.py | 145 +++++++++++++++++++ analyzers/SecurityCenter/requirements.txt | 2 + 3 files changed, 216 insertions(+) create mode 100644 analyzers/SecurityCenter/SecurityCenter.json create mode 100644 analyzers/SecurityCenter/SecurityCenter.py create mode 100644 analyzers/SecurityCenter/requirements.txt diff --git a/analyzers/SecurityCenter/SecurityCenter.json b/analyzers/SecurityCenter/SecurityCenter.json new file mode 100644 index 000000000..249fa7505 --- /dev/null +++ b/analyzers/SecurityCenter/SecurityCenter.json @@ -0,0 +1,69 @@ +{ + "name": "SecurityCenter", + "version": "1.0", + "author": "Tommy Bohde", + "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "license": "AGPL-V3", + "description": "Launch a scan through Tenable SecurityCenter", + "dataTypeList": ["ip", "fqdn"], + "baseConfig": "SecurityCenter", + "command": "SecurityCenter/SecurityCenter.py", + "configurationItems": [ + { + "name": "host", + "description": "IP address of the host running the SecurityCenter API", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "login", + "description": "Login for SecurityCenter API", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "password", + "description": "Password for SecurityCenter API", + "type": "string", + "multi": false, + "required": true + }, + { + "name": "policy", + "description": "Scan policy ID", + "type": "number", + "multi": false, + "required": true + }, + { + "name": "credential", + "description": "Scan credential ID", + "type": "number", + "multi": false, + "required": true + }, + { + "name": "emailOnLaunch", + "description": "Send an email when the scan is launched", + "type": "boolean", + "multi": false, + "required": true + }, + { + "name": "emailOnComplete", + "description": "Send an email when the scan is completed", + "type": "boolean", + "multi": false, + "required": true + }, + { + "name": "report", + "description": "Report ID to run post-scan", + "type": "number", + "multi": false, + "required": true + } + ] +} diff --git a/analyzers/SecurityCenter/SecurityCenter.py b/analyzers/SecurityCenter/SecurityCenter.py new file mode 100644 index 000000000..68c1b6774 --- /dev/null +++ b/analyzers/SecurityCenter/SecurityCenter.py @@ -0,0 +1,145 @@ +#!/usr/bin/env python3 +# encoding: utf-8 + +import re +import time +import socket + +from cortexutils.analyzer import Analyzer +from tenable.sc import TenableSC + + +class SCAnalyzer(Analyzer): + + def __init__(self): + Analyzer.__init__(self) + base = 'config.' + + # using getParam(name, default=None, message=None) + self.ip = self.get_param( + base + 'host', None, 'Missing SecurityCenter IP') + self.login = self.get_param( + base + 'login', None, 'Missing SecurityCenter login') + self.password = self.get_param( + base + 'password', None, 'Missing SecurityCenter password') + self.policyID = self.get_param( + base + 'policy', None, 'Missing policy ID') + self.credentialID = self.get_param( + base + 'credential', None, 'Missing scan credential ID') + self.reportID = self.get_param( + base + 'report', None, 'Missing report ID') + self.email_on_launch = self.get_param( + base + 'emailOnLaunch', False, None) + self.email_on_complete = self.get_param( + base + 'emailOnComplete', False, None) + +# def summary(self, raw): +# summary = {} +# if "vulnerabilities" in raw: +# count = [0, 0, 0, 0, 0] +# for vuln in raw["vulnerabilities"]: +# count[vuln["severity"]] += 1 +# summary["info"] = count[0] +# summary["low"] = count[1] +# summary["medium"] = count[2] +# summary["high"] = count[3] +# summary["critical"] = count[4] +# +# taxonomies = [] +# level = "info" +# namespace = "Nessus" +# predicate = "Info" +# +# if summary["info"] > 0: +# value = summary["info"] +# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) +# if summary["low"] > 0: +# value = summary["low"] +# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) +# if summary["medium"] > 0: +# value = summary["medium"] +# level = "suspicious" +# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) +# if summary["high"] > 0: +# value = summary["high"] +# level = "suspicious" +# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) +# if summary["critical"] > 0: +# value = summary["critical"] +# level = "malicious" +# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) +# +# return {"taxonomies": taxonomies} + + def run(self): + Analyzer.run(self) + + if self.data_type != 'ip' and self.data_type != 'fqdn': + self.error('Invalid data type') + + try: + sc = TenableSC(self.ip) + sc.login(self.login, self.password) + + results = self._run_scan(sc) + + sc.logout() + + except Exception as ex: + self.error('Error: %s' % ex) + + self.report(results) + + def _run_scan(self, sc): + target_hosts = re.split(r'[,\s]+', self.get_param('data')) + name = 'CORTEX | ' + for i, h in enumerate(target_hosts): + if (self.data_type == 'ip'): + try: + hostname = socket.gethostbyaddr(h)[0].split('.')[0].upper() + name += hostname + '\u00A0(' + h + ')' + except Exception: + name += h + elif (self.data_type == 'fqdn'): + try: + hostname = h.split('.')[0].upper() + name += hostname + '\u00A0(' + socket.gethostbyname(h) + ')' + except Exception: + name += h.split('0')[0].upper() + if (i != len(target_hosts) - 1): + name += ', ' + + r = sc.scans.create(name = name, + repo = 1, + policy_id = int(self.policyID), + email_launch = self.email_on_launch, + email_complete = self.email_on_complete, + credentials = [{'id': self.credentialID}], + reports = [{'id': self.reportID, 'reportSource': 'individual'}], + schedule = {'type': 'now'}, + targets = target_hosts + ) + + resultID = r['scanResultID'] + + return self._get_scan_results(sc, resultID) + + def _get_scan_results(self, sc, resultID): + results = {} + while True: + results = sc.scan_instances.details(int(resultID)) + running = (results['running'].lower() == 'true') + status = results['status'].lower() + if (not running and status == 'completed'): + break + elif (status == 'error'): + self.error("Error: " + results['errorDetails']) + time.sleep(60) + return results + +# def _delete_scan(self, scanner): +# scanner.action( +# "scans/" + str(scanner.scan_id), method="DELETE") + +if __name__ == '__main__': + SCAnalyzer().run() diff --git a/analyzers/SecurityCenter/requirements.txt b/analyzers/SecurityCenter/requirements.txt new file mode 100644 index 000000000..57dfc53c5 --- /dev/null +++ b/analyzers/SecurityCenter/requirements.txt @@ -0,0 +1,2 @@ +cortexutils +pytenable==0.3.3 From 81c9a142d8031ecee046eb8025b365d8aa92bd82 Mon Sep 17 00:00:00 2001 From: Tommy Bohde Date: Mon, 17 Dec 2018 10:19:16 -0500 Subject: [PATCH 2/6] remove unused code --- analyzers/SecurityCenter/SecurityCenter.py | 42 ---------------------- 1 file changed, 42 deletions(-) diff --git a/analyzers/SecurityCenter/SecurityCenter.py b/analyzers/SecurityCenter/SecurityCenter.py index 68c1b6774..9f25779a4 100644 --- a/analyzers/SecurityCenter/SecurityCenter.py +++ b/analyzers/SecurityCenter/SecurityCenter.py @@ -33,44 +33,6 @@ def __init__(self): self.email_on_complete = self.get_param( base + 'emailOnComplete', False, None) -# def summary(self, raw): -# summary = {} -# if "vulnerabilities" in raw: -# count = [0, 0, 0, 0, 0] -# for vuln in raw["vulnerabilities"]: -# count[vuln["severity"]] += 1 -# summary["info"] = count[0] -# summary["low"] = count[1] -# summary["medium"] = count[2] -# summary["high"] = count[3] -# summary["critical"] = count[4] -# -# taxonomies = [] -# level = "info" -# namespace = "Nessus" -# predicate = "Info" -# -# if summary["info"] > 0: -# value = summary["info"] -# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) -# if summary["low"] > 0: -# value = summary["low"] -# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) -# if summary["medium"] > 0: -# value = summary["medium"] -# level = "suspicious" -# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) -# if summary["high"] > 0: -# value = summary["high"] -# level = "suspicious" -# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) -# if summary["critical"] > 0: -# value = summary["critical"] -# level = "malicious" -# taxonomies.append(self.build_taxonomy(level, namespace, predicate, value)) -# -# return {"taxonomies": taxonomies} - def run(self): Analyzer.run(self) @@ -137,9 +99,5 @@ def _get_scan_results(self, sc, resultID): time.sleep(60) return results -# def _delete_scan(self, scanner): -# scanner.action( -# "scans/" + str(scanner.scan_id), method="DELETE") - if __name__ == '__main__': SCAnalyzer().run() From 71a0c165ff88dcd4c2bbf42b5fbbcbc41cafb0fc Mon Sep 17 00:00:00 2001 From: Tommy Bohde Date: Mon, 17 Dec 2018 16:47:36 -0500 Subject: [PATCH 3/6] fix GitHub URL --- analyzers/SecurityCenter/SecurityCenter.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/SecurityCenter/SecurityCenter.json b/analyzers/SecurityCenter/SecurityCenter.json index 249fa7505..5dd4f38fc 100644 --- a/analyzers/SecurityCenter/SecurityCenter.json +++ b/analyzers/SecurityCenter/SecurityCenter.json @@ -2,7 +2,7 @@ "name": "SecurityCenter", "version": "1.0", "author": "Tommy Bohde", - "url": "https://github.com/TheHive-Project/Cortex-Analyzers", + "url": "https://github.com/InsanesTheName/Cortex-Analyzers", "license": "AGPL-V3", "description": "Launch a scan through Tenable SecurityCenter", "dataTypeList": ["ip", "fqdn"], From 1189ed3d4fc4fc5ebe2d9ad66a675bc4b58107d8 Mon Sep 17 00:00:00 2001 From: Tommy Bohde Date: Tue, 18 Dec 2018 14:33:33 -0500 Subject: [PATCH 4/6] decrease wait time from 60 seconds to 5 seconds --- analyzers/SecurityCenter/SecurityCenter.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/SecurityCenter/SecurityCenter.py b/analyzers/SecurityCenter/SecurityCenter.py index 9f25779a4..926eac546 100644 --- a/analyzers/SecurityCenter/SecurityCenter.py +++ b/analyzers/SecurityCenter/SecurityCenter.py @@ -96,7 +96,7 @@ def _get_scan_results(self, sc, resultID): break elif (status == 'error'): self.error("Error: " + results['errorDetails']) - time.sleep(60) + time.sleep(5) return results if __name__ == '__main__': From 8fa8cdbd502eec6cc9d56b898606f98a3a26724e Mon Sep 17 00:00:00 2001 From: Tommy Bohde Date: Wed, 19 Dec 2018 09:58:15 -0500 Subject: [PATCH 5/6] fix formatting --- analyzers/SecurityCenter/SecurityCenter.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/analyzers/SecurityCenter/SecurityCenter.py b/analyzers/SecurityCenter/SecurityCenter.py index 926eac546..51ea56cde 100644 --- a/analyzers/SecurityCenter/SecurityCenter.py +++ b/analyzers/SecurityCenter/SecurityCenter.py @@ -8,14 +8,13 @@ from cortexutils.analyzer import Analyzer from tenable.sc import TenableSC - class SCAnalyzer(Analyzer): def __init__(self): Analyzer.__init__(self) base = 'config.' - # using getParam(name, default=None, message=None) + # using getParam(name, default=None, message=None) self.ip = self.get_param( base + 'host', None, 'Missing SecurityCenter IP') self.login = self.get_param( @@ -59,6 +58,7 @@ def _run_scan(self, sc): if (self.data_type == 'ip'): try: hostname = socket.gethostbyaddr(h)[0].split('.')[0].upper() + # \u00A0 is   (to make the report look nicer) name += hostname + '\u00A0(' + h + ')' except Exception: name += h From 715b2cd33d00b6a4b94b721df660fe23c44f4b88 Mon Sep 17 00:00:00 2001 From: Tommy Bohde Date: Fri, 21 Dec 2018 09:49:18 -0500 Subject: [PATCH 6/6] remove pytenable version number tested with 0.3.6 --- analyzers/SecurityCenter/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/analyzers/SecurityCenter/requirements.txt b/analyzers/SecurityCenter/requirements.txt index 57dfc53c5..2d5ed4d88 100644 --- a/analyzers/SecurityCenter/requirements.txt +++ b/analyzers/SecurityCenter/requirements.txt @@ -1,2 +1,2 @@ cortexutils -pytenable==0.3.3 +pytenable