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!") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Inspect why the
resp.json()
does not contain the keyscans
. I dont know if nessus changed their API and cannot provide further support.