Skip to content

Instantly share code, notes, and snippets.

@dreizehnutters
Last active January 15, 2025 14:42
Show Gist options
  • Save dreizehnutters/e01d3a466617ef958a29fa4300036edc to your computer and use it in GitHub Desktop.
Save dreizehnutters/e01d3a466617ef958a29fa4300036edc to your computer and use it in GitHub Desktop.
export nessus reports via CLI
#!/usr/bin/env python3
__version__ = "1.0"
__about__ = "nessus report exporter"
from time import sleep
from os import environ
import requests
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
# DEFAULTS
AK = environ.get('NESSUS_AK') # CHANGE ME
SK = environ.get('NESSUS_SK') # CHANGE ME
TEMPLATE_ID = 1321 # CHANGE ME
CHUNK_SIZE = 1024
NESSUS_INSTANCE = "localhost:8834"
OUT_DIR= "~/Desktop"
def trigger_download(scan_id, export_format='html'):
"""
return file_id
"""
burp0_url = f"https://{NESSUS_INSTANCE}/scans/{scan_id}/export"
burp0_headers = {"Content-Type": "application/json",\
"X-Requested-With": "XMLHttpRequest",\
"X-ApiKeys": f"accessKey={AK}; secretKey={SK}", "Connection": "close"}
burp0_json={"format": export_format, "template_id": TEMPLATE_ID}
resp = requests.post(burp0_url, headers=burp0_headers, json=burp0_json, verify=False, timeout=10)
if resp.status_code == 200:
try:
file_id = resp.json()['file']
except Exception as err:
raise Exception(err)
else:
raise Exception(f"[!] {resp.text} (scan_id: {scan_id})")
return file_id
def download_report(scan_id, file_id, filename):
"""
return True if downloaded else False
"""
burp0_url = f"https://{NESSUS_INSTANCE}/scans/{scan_id}/export/{file_id}/download"
burp0_headers = {"Content-Type": "application/json",\
"X-Requested-With": "XMLHttpRequest",\
"X-ApiKeys": f"accessKey={AK}; secretKey={SK};",\
"Connection": "close"}
resp = requests.get(burp0_url, headers=burp0_headers, verify=False, timeout=10)
if resp.status_code != 200:
return False
with open(f"{OUT_DIR}/{filename}", 'wb') as fd:
for chunk in resp.iter_content(CHUNK_SIZE):
fd.write(chunk)
return True
def select_scan_id():
"""
return scan_id from user input
"""
choices = get_all_scan_ids()
for idx, name in enumerate(choices):
print(f"[{idx}] {name[0]}")
choice = -1
while choice not in range(len(choices)):
try:
choice = int(input(f"[?] select a scan: 0-{len(choices)-1}: "))
print("")
except Exception as err:
continue
if choice in range(len(choices)):
return (choices[choice][0], choices[choice][1])
def get_all_scan_ids():
"""
return list of scan ids that are ready to be exported (not in trash and completed)
"""
burp0_url = f"https://{NESSUS_INSTANCE}/scans"
burp0_headers = {"Content-Type": "application/json",
"X-Requested-With": "XMLHttpRequest",
"X-ApiKeys": f"accessKey={AK}; secretKey={SK};",
"Connection": "close"}
resp = requests.get(burp0_url, headers=burp0_headers, verify=False, timeout=10)
if resp.json()['scans'] is None:
exit("[!] no scans is ready to be exported")
trash_folder_id = [x['id'] for x in resp.json()['folders'] if x['type'] == 'trash'][-1]
scan_ids = [(x['name'], x['id']) for x in resp.json()['scans'] if x['status'] in ('completed', 'canceled', 'imported') and x['folder_id'] != trash_folder_id]
if len(scan_ids) > 0:
return scan_ids
exit("[!] no scans is ready to be exported")
def do_download(scan_id, file_id, filename):
"""
download file if status is ready
"""
code = False
n_sek = 0
while code is not True:
print(f"\t[z] the report is being generated since {n_sek:02d} sec", end='\r')
code = download_report(scan_id, file_id, filename)
sleep(1)
n_sek+=1
print(f"{' '*80}",end='\r')
if __name__ == '__main__':
option = input("[?] export all scans (y/n): ")
print("")
if (option.lower() == 'y') or (option.lower() == 'j'):
scan_ids = get_all_scan_ids()
else:
scan_ids = [select_scan_id()]
for scan_tuple in scan_ids:
print(f"[*] downloading report: '{scan_tuple[0]}'")
scan_id = scan_tuple[1]
for export_format in ['html', 'nessus']:
print(f"\t[.] getting the .{export_format} version")
try:
file_id = trigger_download(scan_id, export_format)
except Exception as err:
print(err)
continue
do_download(scan_id, file_id, filename=scan_tuple[0].replace(' ','_').replace('\\','_').replace('/','_')+f'.{export_format}')
print(f"{'-'*10}")
print("[$] done!")
@fnickelsen
Copy link

hey there,
I tried using your script but failed doing so.
all the commands from your script are working fine, if executed one by one. but that is not what i want. ;)
do you have any idea, why i receive these following errors?
any input is highly appreciated

Traceback (most recent call last):
  File "/Users/xyz/Downloads/e01d3a466617ef958a29fa4300036edc-c595390ce15bbbae8fb8f50551e82edc186c326a/nessus_report_exporter.py", line 117, in <module>
    scan_ids = get_all_scan_ids()
               ^^^^^^^^^^^^^^^^^^
  File "/Users/xyz/Downloads/e01d3a466617ef958a29fa4300036edc-c595390ce15bbbae8fb8f50551e82edc186c326a/nessus_report_exporter.py", line 89, in get_all_scan_ids
    if resp.json()['scans'] is None:
       ~~~~~~~~~~~^^^^^^^^^
KeyError: 'scans'

@dreizehnutters
Copy link
Author

Inspect why the resp.json() does not contain the key scans. I dont know if nessus changed their API and cannot provide further support.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment