Skip to content

Instantly share code, notes, and snippets.

@ErbaZZ
Created July 17, 2024 21:07
Show Gist options
  • Save ErbaZZ/8135b534953dcb7605320d019fdc012f to your computer and use it in GitHub Desktop.
Save ErbaZZ/8135b534953dcb7605320d019fdc012f to your computer and use it in GitHub Desktop.
Python script for exporting Nessus Professional scan result
# Nessus Scans Exporter
# Original by mindnew (https://github.com/mindnew/Nessus-API-Report-Exporter/blob/main/Nessus-API-Report-Exporter.py)
# PowerShell to Python conversion assisted by ChatGPT
import requests
import json
import time
from datetime import datetime
def trust_all_certs():
from requests.packages.urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
def get_current_epoch():
return int(time.mktime(datetime.utcnow().timetuple()))
def get_user_input():
base_url = input("Enter Nessus Scanner URL + Port (e.g. https://NessusServerFQDN:8834) [https://localhost:8834]: ") or "https://localhost:8834"
username = input("Enter login username (e.g. Administrator) [nessus]: ") or "nessus"
password = input("Enter Password: ")
epoch = int(input(f"Enter the date & time in EPOCH format or press ENTER to take the current timestamp [{get_current_epoch()}]: ") or get_current_epoch())
time_buffer = int(input("Enter the time buffer to collect the scan results (e.g. 8 to export the results started in last 8 hours) [8]: ") or 8)
return base_url, username, password, epoch, time_buffer
def get_session_token(base_url, username, password):
headers = {'Content-Type': 'application/json'}
session_url = f"{base_url}/session"
session_data = {'username': username, 'password': password}
session_response = requests.post(session_url, headers=headers, data=json.dumps(session_data), verify=False)
if session_response.status_code != 200:
print("Error obtaining session token. Ensure username and password are correct.")
return None
return session_response.json()['token']
def get_scans(base_url, headers):
scans_url = f"{base_url}/scans"
scans_response = requests.get(scans_url, headers=headers, verify=False)
return scans_response.json()['scans']
def filter_scans(scans_data, epoch, time_buffer):
completed_scans = [scan for scan in scans_data if scan['status'] == 'completed' and epoch >= scan['creation_date'] and (epoch - scan['creation_date']) / 3600 < time_buffer]
not_completed_scans = [scan for scan in scans_data if scan['status'] != 'completed' and epoch >= scan['creation_date'] and (epoch - scan['creation_date']) / 3600 < time_buffer]
return completed_scans, not_completed_scans
def get_report_types():
print("Available report types to select:")
print("nessus, db, csv, html, all")
report_format = input("Enter selection [all]: ") or "all"
report_types = {
"ALL": [1, 2, 3, 4, 5, 6, 7],
"NESSUS": [1],
"DB": [2],
"CSV": [3],
"HTML": [4, 5, 6, 7]
}.get(report_format.upper(), [0])
if not report_types:
print("Invalid input. Script is going to terminate.")
return None
return report_types
def get_export_body(report_type, nessus_db_password):
export_body = None
format_ext = ""
format_str = ""
if report_type == 1:
format_ext = ".nessus"
format_str = f"{report_type}.Nessus (XML)"
export_body = {"format": "nessus"}
elif report_type == 2:
format_ext = ".db"
format_str = f"{report_type}.Nessus DB (Password: {nessus_db_password})"
export_body = {"format": "db", "password": nessus_db_password}
elif report_type == 3:
format_ext = ".csv"
format_str = f"{report_type}.CSV"
export_body = {"format": "csv", "template_id": "", "reportContents": {"csvColumns": {"id": True, "cve": True, "cvss": True, "risk": True, "hostname": True, "protocol": True, "port": True, "plugin_name": True, "synopsis": True, "description": True, "solution": True, "see_also": True, "plugin_output": True, "stig_severity": True, "cvss3_base_score": True, "cvss_temporal_score": True, "cvss3_temporal_score": True, "risk_factor": True, "references": True, "plugin_information": True, "exploitable_with": True}, "extraFilters": {"host_ids": [], "plugin_ids": []}}}
elif report_type == 4:
format_ext = "_(Plugins).html"
format_str = f"{report_type}.HTML (Sort by Plugins)"
export_body = {"format": "html", "chapters": "vuln_by_plugin"}
elif report_type == 5:
format_ext = "_(Host).html"
format_str = f"{report_type}.HTML (Sort by Host)"
export_body = {"format": "html", "chapters": "vuln_by_host"}
elif report_type == 6:
format_ext = "_(compliance_Host).html"
format_str = f"{report_type}.HTML (compliance with Host details)"
export_body = {"format": "html", "chapters": "vuln_by_host,compliance"}
elif report_type == 7:
format_ext = "_(Detailed).html"
format_str = f"{report_type}.HTML (Contains Host and Plugins along with the compliance)"
export_body = {"format": "html", "chapters": "vuln_by_host,vuln_by_plugin,compliance"}
return export_body, format_ext, format_str
def export_scan(scan, report_types, headers, scans_url, nessus_db_password, count):
scan_id = scan['id']
scan_name = scan['name']
scan_name_file = scan_name.replace('[', '(').replace(']', ')').replace(' ', '_')
print(f"\nWorking on Scan {count} : {scan_name}")
for report_type in report_types:
export_body, format_ext, format_str = get_export_body(report_type, nessus_db_password)
if export_body is None:
print(f"Error: Unsupported report type {report_type}")
continue
print(f"\tReport Format: '{format_str}'")
print(f"\t\tScan {count} : Step 1 - Requesting for file to download")
export_uri = f"{scans_url}/{scan_id}/export"
export_response = requests.post(export_uri, headers=headers, data=json.dumps(export_body), verify=False)
if export_response.status_code != 200:
print(f"Error during export operation for {scan_name}.")
continue
export_data = export_response.json()
if 'file' in export_data:
status_uri = f"{scans_url}/{scan_id}/export/{export_data['file']}/status"
download_uri = f"{scans_url}/{scan_id}/export/{export_data['file']}/download"
else:
status_uri = f"{base_url}/tokens/{export_data['token']}/status"
download_uri = f"{base_url}/tokens/{export_data['token']}/download"
print(f"\t\tScan {count} : Step 2 - Waiting for Nessus scan to be ready", end="")
while True:
status_response = requests.get(status_uri, headers=headers, verify=False)
status_data = status_response.json()
if status_data.get('status') == "ready":
print("\tReady.")
break
time.sleep(2)
print(f"\t\tScan {count} : Step 3 - Downloading the report")
download_response = requests.get(download_uri, headers=headers, verify=False, stream=True)
with open(f"./{scan_name_file}{format_ext}", 'wb') as file:
for chunk in download_response.iter_content(chunk_size=8192):
file.write(chunk)
print(f"\t\tScan {count} : Step 4 - Operation completed. File name: './{scan_name_file}{format_ext}'")
def main():
trust_all_certs()
base_url, username, password, epoch, time_buffer = get_user_input()
token = get_session_token(base_url, username, password)
if not token:
return
headers = {'Content-Type': 'application/json', 'X-Cookie': f"token={token}"}
scans_data = get_scans(base_url, headers)
completed_scans, not_completed_scans = filter_scans(scans_data, epoch, time_buffer)
if not completed_scans:
print("No scans completed, hence can't be exported.")
return
for scan in completed_scans:
print(f"Scan Name: {scan['name']}, Scan Status: {scan['status']}, Id: {scan['id']}")
report_types = get_report_types()
if not report_types:
return
nessus_db_password = input("Enter password for Nessus DB format [my_db_password]: ") or "my_db_password"
count = 1
for scan in completed_scans:
export_scan(scan, report_types, headers, f"{base_url}/scans", nessus_db_password, count)
count += 1
print("All reports downloaded successfully.")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment