Support tenable API keys (#176)

* support tenable API keys

* more flexible config support

* add nessus API key support

* fix whitespace
This commit is contained in:
pemontto
2019-05-02 09:26:51 +01:00
committed by Quim Montal
parent 162636e60f
commit 6cf2a94431
7 changed files with 103 additions and 72 deletions

View File

@ -2,6 +2,8 @@
enabled=true enabled=true
hostname=localhost hostname=localhost
port=8834 port=8834
access_key=
secret_key=
username=nessus_username username=nessus_username
password=nessus_password password=nessus_password
write_path=/opt/VulnWhisperer/data/nessus/ write_path=/opt/VulnWhisperer/data/nessus/
@ -13,6 +15,8 @@ verbose=true
enabled=true enabled=true
hostname=cloud.tenable.com hostname=cloud.tenable.com
port=443 port=443
access_key=
secret_key=
username=tenable.io_username username=tenable.io_username
password=tenable.io_password password=tenable.io_password
write_path=/opt/VulnWhisperer/data/tenable/ write_path=/opt/VulnWhisperer/data/tenable/

View File

@ -2,6 +2,8 @@
enabled=true enabled=true
hostname=nessus hostname=nessus
port=443 port=443
access_key=
secret_key=
username=nessus_username username=nessus_username
password=nessus_password password=nessus_password
write_path=/opt/VulnWhisperer/data/nessus/ write_path=/opt/VulnWhisperer/data/nessus/
@ -13,6 +15,8 @@ verbose=true
enabled=true enabled=true
hostname=tenable hostname=tenable
port=443 port=443
access_key=
secret_key=
username=tenable.io_username username=tenable.io_username
password=tenable.io_password password=tenable.io_password
write_path=/opt/VulnWhisperer/data/tenable/ write_path=/opt/VulnWhisperer/data/tenable/

View File

@ -24,15 +24,19 @@ class NessusAPI(object):
EXPORT_STATUS = EXPORT + '/{file_id}/status' EXPORT_STATUS = EXPORT + '/{file_id}/status'
EXPORT_HISTORY = EXPORT + '?history_id={history_id}' EXPORT_HISTORY = EXPORT + '?history_id={history_id}'
def __init__(self, hostname=None, port=None, username=None, password=None, verbose=True): def __init__(self, hostname=None, port=None, username=None, password=None, verbose=True, profile=None, access_key=None, secret_key=None):
self.logger = logging.getLogger('NessusAPI') self.logger = logging.getLogger('NessusAPI')
if verbose: if verbose:
self.logger.setLevel(logging.DEBUG) self.logger.setLevel(logging.DEBUG)
if username is None or password is None: if not all((username, password)) and not all((access_key, secret_key)):
raise Exception('ERROR: Missing username or password.') raise Exception('ERROR: Missing username, password or API keys.')
self.profile = profile
self.user = username self.user = username
self.password = password self.password = password
self.api_keys = False
self.access_key = access_key
self.secret_key = secret_key
self.base = 'https://{hostname}:{port}'.format(hostname=hostname, port=port) self.base = 'https://{hostname}:{port}'.format(hostname=hostname, port=port)
self.verbose = verbose self.verbose = verbose
@ -52,7 +56,13 @@ class NessusAPI(object):
'X-Cookie': None 'X-Cookie': None
} }
if all((self.access_key, self.secret_key)):
self.logger.debug('Using {} API keys'.format(self.profile))
self.api_keys = True
self.session.headers['X-ApiKeys'] = 'accessKey={}; secretKey={}'.format(self.access_key, self.secret_key)
else:
self.login() self.login()
self.scans = self.get_scans() self.scans = self.get_scans()
self.scan_ids = self.get_scan_ids() self.scan_ids = self.get_scan_ids()
@ -78,8 +88,10 @@ class NessusAPI(object):
if url == self.base + self.SESSION: if url == self.base + self.SESSION:
break break
try: try:
self.login()
timeout += 1 timeout += 1
if self.api_keys:
continue
self.login()
self.logger.info('Token refreshed') self.logger.info('Token refreshed')
except Exception as e: except Exception as e:
self.logger.error('Could not refresh token\nReason: {}'.format(str(e))) self.logger.error('Could not refresh token\nReason: {}'.format(str(e)))
@ -114,7 +126,7 @@ class NessusAPI(object):
data = self.request(self.SCAN_ID.format(scan_id=scan_id), method='GET', json_output=True) data = self.request(self.SCAN_ID.format(scan_id=scan_id), method='GET', json_output=True)
return data['history'] return data['history']
def download_scan(self, scan_id=None, history=None, export_format="", profile=""): def download_scan(self, scan_id=None, history=None, export_format=""):
running = True running = True
counter = 0 counter = 0
@ -127,6 +139,7 @@ class NessusAPI(object):
req = self.request(query, data=json.dumps(data), method='POST', json_output=True) req = self.request(query, data=json.dumps(data), method='POST', json_output=True)
try: try:
file_id = req['file'] file_id = req['file']
if self.profile == 'nessus':
token_id = req['token'] if 'token' in req else req['temp_token'] token_id = req['token'] if 'token' in req else req['temp_token']
except Exception as e: except Exception as e:
self.logger.error('{}'.format(str(e))) self.logger.error('{}'.format(str(e)))
@ -143,7 +156,7 @@ class NessusAPI(object):
if counter % 60 == 0: if counter % 60 == 0:
self.logger.info("Completed: {}".format(counter)) self.logger.info("Completed: {}".format(counter))
self.logger.info("Done: {}".format(counter)) self.logger.info("Done: {}".format(counter))
if profile == 'tenable': if self.profile == 'tenable' or self.api_keys:
content = self.request(self.EXPORT_FILE_DOWNLOAD.format(scan_id=scan_id, file_id=file_id), method='GET', download=True) content = self.request(self.EXPORT_FILE_DOWNLOAD.format(scan_id=scan_id, file_id=file_id), method='GET', download=True)
else: else:
content = self.request(self.EXPORT_TOKEN_DOWNLOAD.format(token_id=token_id), method='GET', download=True) content = self.request(self.EXPORT_TOKEN_DOWNLOAD.format(token_id=token_id), method='GET', download=True)

View File

@ -55,8 +55,12 @@ class vulnWhispererBase(object):
except: except:
self.enabled = False self.enabled = False
self.hostname = self.config.get(self.CONFIG_SECTION, 'hostname') self.hostname = self.config.get(self.CONFIG_SECTION, 'hostname')
try:
self.username = self.config.get(self.CONFIG_SECTION, 'username') self.username = self.config.get(self.CONFIG_SECTION, 'username')
self.password = self.config.get(self.CONFIG_SECTION, 'password') self.password = self.config.get(self.CONFIG_SECTION, 'password')
except:
self.username = None
self.password = None
self.write_path = self.config.get(self.CONFIG_SECTION, 'write_path') self.write_path = self.config.get(self.CONFIG_SECTION, 'write_path')
self.db_path = self.config.get(self.CONFIG_SECTION, 'db_path') self.db_path = self.config.get(self.CONFIG_SECTION, 'db_path')
self.verbose = self.config.getbool(self.CONFIG_SECTION, 'verbose') self.verbose = self.config.getbool(self.CONFIG_SECTION, 'verbose')
@ -274,6 +278,8 @@ class vulnWhispererNessus(vulnWhispererBase):
self.develop = True self.develop = True
self.purge = purge self.purge = purge
self.access_key = None
self.secret_key = None
if config is not None: if config is not None:
try: try:
@ -283,19 +289,30 @@ class vulnWhispererNessus(vulnWhispererBase):
'trash') 'trash')
try: try:
self.logger.info('Attempting to connect to nessus...') self.access_key = self.config.get(self.CONFIG_SECTION,'access_key')
self.secret_key = self.config.get(self.CONFIG_SECTION,'secret_key')
except:
pass
try:
self.logger.info('Attempting to connect to {}...'.format(self.CONFIG_SECTION))
self.nessus = \ self.nessus = \
NessusAPI(hostname=self.hostname, NessusAPI(hostname=self.hostname,
port=self.nessus_port, port=self.nessus_port,
username=self.username, username=self.username,
password=self.password) password=self.password,
profile=self.CONFIG_SECTION,
access_key=self.access_key,
secret_key=self.secret_key
)
self.nessus_connect = True self.nessus_connect = True
self.logger.info('Connected to nessus on {host}:{port}'.format(host=self.hostname, self.logger.info('Connected to {} on {host}:{port}'.format(self.CONFIG_SECTION, host=self.hostname,
port=str(self.nessus_port))) port=str(self.nessus_port)))
except Exception as e: except Exception as e:
self.logger.error('Exception: {}'.format(str(e))) self.logger.error('Exception: {}'.format(str(e)))
raise Exception( raise Exception(
'Could not connect to nessus -- Please verify your settings in {config} are correct and try again.\nReason: {e}'.format( 'Could not connect to {} -- Please verify your settings in {config} are correct and try again.\nReason: {e}'.format(
self.CONFIG_SECTION,
config=self.config.config_in, config=self.config.config_in,
e=e)) e=e))
except Exception as e: except Exception as e:
@ -435,7 +452,7 @@ class vulnWhispererNessus(vulnWhispererBase):
try: try:
file_req = \ file_req = \
self.nessus.download_scan(scan_id=scan_id, history=history_id, self.nessus.download_scan(scan_id=scan_id, history=history_id,
export_format='csv', profile=self.CONFIG_SECTION) export_format='csv')
except Exception as e: except Exception as e:
self.logger.error('Could not download {} scan {}: {}'.format(self.CONFIG_SECTION, scan_id, str(e))) self.logger.error('Could not download {} scan {}: {}'.format(self.CONFIG_SECTION, scan_id, str(e)))
self.exit_code += 1 self.exit_code += 1
@ -643,8 +660,7 @@ class vulnWhispererQualys(vulnWhispererBase):
if cleanup: if cleanup:
self.logger.info('Removing report {} from Qualys Database'.format(generated_report_id)) self.logger.info('Removing report {} from Qualys Database'.format(generated_report_id))
cleaning_up = \ cleaning_up = self.qualys_scan.qw.delete_report(generated_report_id)
self.qualys_scan.qw.delete_report(generated_report_id)
os.remove(self.path_check(str(generated_report_id) + '.csv')) os.remove(self.path_check(str(generated_report_id) + '.csv'))
self.logger.info('Deleted report from local disk: {}'.format(self.path_check(str(generated_report_id)))) self.logger.info('Deleted report from local disk: {}'.format(self.path_check(str(generated_report_id))))
else: else:
@ -1259,9 +1275,6 @@ class vulnWhisperer(object):
if self.profile == 'nessus': if self.profile == 'nessus':
vw = vulnWhispererNessus(config=self.config, vw = vulnWhispererNessus(config=self.config,
username=self.username,
password=self.password,
verbose=self.verbose,
profile=self.profile) profile=self.profile)
self.exit_code += vw.whisper_nessus() self.exit_code += vw.whisper_nessus()
@ -1275,9 +1288,6 @@ class vulnWhisperer(object):
elif self.profile == 'tenable': elif self.profile == 'tenable':
vw = vulnWhispererNessus(config=self.config, vw = vulnWhispererNessus(config=self.config,
username=self.username,
password=self.password,
verbose=self.verbose,
profile=self.profile) profile=self.profile)
self.exit_code += vw.whisper_nessus() self.exit_code += vw.whisper_nessus()