Created
July 17, 2024 21:07
-
-
Save ErbaZZ/8135b534953dcb7605320d019fdc012f to your computer and use it in GitHub Desktop.
Python script for exporting Nessus Professional scan result
This file contains 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
# 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