Last active
January 15, 2025 14:42
-
-
Save dreizehnutters/e01d3a466617ef958a29fa4300036edc to your computer and use it in GitHub Desktop.
export nessus reports via CLI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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!") |
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
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