vulnerability management

Scanning Infrastructure with Nessus

Tenable Nessus is the industry-leading vulnerability scanner used to identify security weaknesses across network infrastructure including servers, workstations, network devices, and operating systems. This skill covers configuring scan policies, running authenticated and unauthenticated scans, interpreting results, and integrating Nessus into continuous vulnerability management workflows.

cveinfrastructure-scanningnessusrisktenablevulnerability-management
Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-Skills
Framework mappings

Overview

Tenable Nessus is the industry-leading vulnerability scanner used to identify security weaknesses across network infrastructure including servers, workstations, network devices, and operating systems. This skill covers configuring scan policies, running authenticated and unauthenticated scans, interpreting results, and integrating Nessus into continuous vulnerability management workflows.

When to Use

  • When conducting security assessments that involve scanning infrastructure with nessus
  • When following incident response procedures for related security events
  • When performing scheduled security testing or auditing activities
  • When validating security controls through hands-on testing

Prerequisites

  • Nessus Professional or Essentials license installed and activated
  • Network access to target systems (firewall rules allowing scanner IP)
  • Administrative credentials for authenticated scanning
  • Understanding of TCP/IP networking and common services
  • Written authorization for scanning target environments

Core Concepts

Nessus Architecture

Nessus operates as a client-server application where the Nessus scanner engine runs as a service (nessusd) on the host system. It uses a plugin-based architecture with over 200,000 plugins updated weekly by Tenable's research team. Each plugin tests for a specific vulnerability, misconfiguration, or compliance check.

Scan Types

  1. Host Discovery - Identifies live hosts using ICMP, TCP, UDP, and ARP
  2. Basic Network Scan - Default policy covering common vulnerabilities
  3. Advanced Scan - Custom policy with granular plugin selection
  4. Credentialed Patch Audit - Authenticated scan checking installed patches
  5. Web Application Tests - Scans for web-specific vulnerabilities
  6. Compliance Audit - Checks against CIS, DISA STIG, PCI DSS benchmarks

Plugin Families

Nessus organizes plugins into families including:

  • Operating Systems: Windows, Linux, macOS, Solaris
  • Network Devices: Cisco, Juniper, Palo Alto, Fortinet
  • Web Servers: Apache, Nginx, IIS, Tomcat
  • Databases: Oracle, MySQL, PostgreSQL, MSSQL
  • Services: DNS, SMTP, FTP, SSH, SNMP

Workflow

Step 1: Initial Configuration

# Start Nessus service
sudo systemctl start nessusd
sudo systemctl enable nessusd
 
# CLI management with nessuscli
/opt/nessus/sbin/nessuscli update --all
/opt/nessus/sbin/nessuscli fix --list
 
# Verify plugin count
/opt/nessus/sbin/nessuscli update --plugins-only

Step 2: Create Scan Policy

Configure a custom scan policy through the Nessus web UI at https://localhost:8834:

  1. Navigate to Policies > New Policy > Advanced Scan
  2. Configure General settings: name, description, targets
  3. Set Discovery settings:
    • Host Discovery: Ping methods (ICMP, TCP SYN on ports 22,80,443)
    • Port Scanning: SYN scan on common ports or all 65535 ports
    • Service Discovery: Probe all ports for services
  4. Configure Assessment settings:
    • Accuracy: Override normal accuracy (reduce false positives)
    • Web Applications: Enable if scanning web servers
  5. Select Plugin families relevant to target environment

Step 3: Configure Credentials

For authenticated scanning, configure credentials under the Credentials tab:

  • SSH: Username/password or SSH key pair
  • Windows: Domain credentials via SMB, WMI
  • SNMP: Community strings (v1/v2c) or USM credentials (v3)
  • Database: Oracle, MySQL, PostgreSQL connection strings
  • VMware: vCenter or ESXi credentials

Step 4: Run the Scan

# Using Nessus REST API via curl
# Authenticate and get token
curl -k -X POST https://localhost:8834/session \
  -d '{"username":"admin","password":"password"}' \
  -H "Content-Type: application/json"
 
# Create scan
curl -k -X POST https://localhost:8834/scans \
  -H "X-Cookie: token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "uuid": "<TEMPLATE_UUID>",
    "settings": {
      "name": "Infrastructure Scan Q1",
      "text_targets": "192.168.1.0/24",
      "enabled": true,
      "launch": "ON_DEMAND"
    }
  }'
 
# Launch scan
curl -k -X POST https://localhost:8834/scans/<SCAN_ID>/launch \
  -H "X-Cookie: token=<TOKEN>"
 
# Check scan status
curl -k -X GET https://localhost:8834/scans/<SCAN_ID> \
  -H "X-Cookie: token=<TOKEN>"

Step 5: Analyze Results

Nessus categorizes findings by severity:

  • Critical (CVSS 9.0-10.0): Immediate remediation required
  • High (CVSS 7.0-8.9): Remediate within 7-14 days
  • Medium (CVSS 4.0-6.9): Remediate within 30 days
  • Low (CVSS 0.1-3.9): Remediate during next maintenance window
  • Informational: No immediate action required

Step 6: Export and Report

# Export via REST API
curl -k -X POST "https://localhost:8834/scans/<SCAN_ID>/export" \
  -H "X-Cookie: token=<TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{"format":"nessus"}'
 
# Supported formats: nessus (XML), csv, html, pdf

Best Practices

  1. Schedule scans during maintenance windows to minimize production impact
  2. Use authenticated scanning for 45-60% more vulnerability detection
  3. Exclude fragile systems (medical devices, legacy SCADA) from aggressive scans
  4. Maintain separate scan policies for different network segments
  5. Update plugins before every scan to catch recently disclosed CVEs
  6. Validate critical findings manually before escalating to remediation teams
  7. Implement scan result trending to track remediation progress over time
  8. Store scan results in Tenable.sc or Tenable.io for centralized management

Common Pitfalls

  • Running unauthenticated scans only (misses 45-60% of vulnerabilities)
  • Scanning without written authorization (legal and ethical violations)
  • Ignoring scan performance impact on production systems
  • Failing to tune plugins leading to excessive false positives
  • Not validating scanner network connectivity before launching scans
  • Using default scan policies without customization for the environment
Source materials

References and resources

Everything below is rendered for inspection. Script files are read-only and never run.

References 3

api-reference.md1.9 KB

API Reference: Scanning Infrastructure with Nessus

Nessus REST API Endpoints

Method Endpoint Description
POST /session Authenticate and get token
GET /scans List all scans
POST /scans Create new scan
POST /scans/{id}/launch Launch a scan
GET /scans/{id} Get scan results
POST /scans/{id}/export Export scan results
GET /scans/{id}/export/{fid}/status Check export status
GET /scans/{id}/hosts/{hid} Get host details

Scan Types

Type Template UUID Use Case
Basic Network Scan ab4bacd2-... Standard vulnerability scan
Advanced Scan ad629e16-... Custom plugin selection
Credentialed Patch Audit 0c3a6b1f-... Authenticated patch check
Web Application Tests 1c35d5a5-... Web vulnerability scan
Compliance Audit bbd4f805-... CIS/STIG/PCI checks

Severity Levels

Level Value CVSS Range SLA
Critical 4 9.0 - 10.0 Immediate
High 3 7.0 - 8.9 7-14 days
Medium 2 4.0 - 6.9 30 days
Low 1 0.1 - 3.9 Next window
Info 0 N/A No action

Export Formats

Format Description
nessus XML format for import into other tools
csv Comma-separated for spreadsheet analysis
html Human-readable HTML report
pdf Formatted PDF report

Python Libraries

Library Version Purpose
requests >=2.28 Nessus REST API calls
json stdlib Parse API responses
urllib3 >=1.26 SSL warning suppression

References

standards.md1.9 KB

Standards and References - Scanning Infrastructure with Nessus

Industry Standards

  • NIST SP 800-115: Technical Guide to Information Security Testing and Assessment
  • NIST SP 800-53 RA-5: Vulnerability Monitoring and Scanning control family
  • PCI DSS v4.0 Requirement 11.3: Internal and external vulnerability scanning
  • CIS Controls v8 Control 7: Continuous Vulnerability Management
  • ISO 27001:2022 A.8.8: Management of technical vulnerabilities

Tenable Documentation

CVE and Vulnerability Databases

Compliance Audit Files

Scan Configuration Standards

Parameter Recommended Value Notes
Port Range 1-65535 (full) For comprehensive scanning
Scan Speed Normal Balance between speed and accuracy
Max Concurrent Hosts 30 Adjust based on network capacity
Max Concurrent Checks per Host 5 Prevent host overload
Network Timeout 5 seconds Increase for high-latency networks
Plugin Timeout 320 seconds Default; increase for slow targets
workflows.md4.4 KB

Workflows - Scanning Infrastructure with Nessus

Workflow 1: Initial Infrastructure Assessment

┌─────────────────┐     ┌──────────────────┐     ┌────────────────────┐
│  Asset Discovery │────>│  Policy Creation │────>│  Credential Config │
│  (Host Enum)     │     │  (Custom/Default)│     │  (SSH/WinRM/SNMP)  │
└─────────────────┘     └──────────────────┘     └────────────────────┘

        ┌────────────────────────────────────────────────┘
        v
┌──────────────────┐     ┌──────────────────┐     ┌────────────────────┐
│   Launch Scan    │────>│  Monitor Status  │────>│   Export Results   │
│   (On-Demand)    │     │  (API Polling)   │     │   (CSV/HTML/PDF)   │
└──────────────────┘     └──────────────────┘     └────────────────────┘

        ┌────────────────────────────────────────────────┘
        v
┌──────────────────┐     ┌──────────────────┐     ┌────────────────────┐
│ Analyze Findings │────>│ Prioritize Vulns │────>│  Create Tickets    │
│ (Severity/CVSS)  │     │ (Risk-Based)     │     │  (Jira/ServiceNow) │
└──────────────────┘     └──────────────────┘     └────────────────────┘

Workflow 2: Recurring Scheduled Scanning

  1. Weekly: Scan DMZ and internet-facing assets
  2. Bi-weekly: Scan internal production servers
  3. Monthly: Full infrastructure scan including workstations
  4. Quarterly: Comprehensive scan with compliance audits
  5. Ad-hoc: Post-patch verification scans

Workflow 3: Scan Result Processing Pipeline

Nessus Export (.nessus XML)

    ├──> Parse with Python (xml.etree / defusedxml)
    │        │
    │        ├──> Filter by severity (Critical/High)
    │        ├──> Deduplicate findings across hosts
    │        ├──> Enrich with EPSS scores
    │        └──> Map to MITRE ATT&CK techniques

    ├──> Import to Vulnerability Management Platform
    │        │
    │        ├──> Tenable.sc / Tenable.io
    │        ├──> DefectDojo
    │        └──> Faraday

    └──> Generate Executive Report

             ├──> Vulnerability count by severity
             ├──> Top 10 most critical findings
             ├──> Remediation progress trending
             └──> Risk score by business unit

Workflow 4: API Automation Flow

# Nessus API Workflow Steps:
# 1. POST /session -> Get auth token
# 2. GET /editor/scan/templates -> List available templates
# 3. POST /scans -> Create scan with template UUID
# 4. POST /scans/{id}/launch -> Start the scan
# 5. GET /scans/{id} -> Poll until status == "completed"
# 6. POST /scans/{id}/export -> Request export (format: nessus/csv/html)
# 7. GET /scans/{id}/export/{file_id}/status -> Poll export status
# 8. GET /scans/{id}/export/{file_id}/download -> Download results
# 9. DELETE /session -> Logout

Workflow 5: Multi-Scanner Coordination

For large enterprises with multiple Nessus scanners:

  1. Central Management: Use Tenable.sc to manage multiple scanners
  2. Zone Assignment: Assign scanners to specific network zones
  3. Scan Windowing: Stagger scans to prevent network saturation
  4. Result Aggregation: Consolidate results in central repository
  5. Deduplication: Merge findings from overlapping scan ranges

Scripts 2

agent.py6.1 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for scanning infrastructure with Tenable Nessus.

Interacts with the Nessus REST API to create scan policies,
launch scans, monitor progress, retrieve results, and generate
vulnerability reports with severity-based prioritization.
"""

import json
import os
import sys
import time
import urllib3
from pathlib import Path
from datetime import datetime

try:
    import requests
except ImportError:
    requests = None

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)


class NessusScanAgent:
    """Manages Nessus vulnerability scans via REST API."""

    def __init__(self, host=None, username="admin",
                 password="", output_dir="./nessus_scan"):
        self.base_url = (host or os.environ.get("NESSUS_URL", "https://localhost:8834")).rstrip("/")
        self.username = username
        self.password = password
        self.token = None
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(parents=True, exist_ok=True)

    def _req(self, method, path, data=None):
        """Make authenticated request to Nessus API."""
        if not requests:
            return {"error": "requests library required: pip install requests"}
        headers = {"Content-Type": "application/json"}
        if self.token:
            headers["X-Cookie"] = f"token={self.token}"
        url = f"{self.base_url}{path}"
        resp = requests.request(method, url, json=data, headers=headers,
                                verify=not os.environ.get("SKIP_TLS_VERIFY", "").lower() == "true", timeout=30)  # Set SKIP_TLS_VERIFY=true for self-signed certs in lab environments
        try:
            return resp.json()
        except (json.JSONDecodeError, ValueError):
            return {"status_code": resp.status_code, "text": resp.text[:200]}

    def authenticate(self):
        """Authenticate and obtain session token."""
        result = self._req("POST", "/session",
                           {"username": self.username, "password": self.password})
        self.token = result.get("token")
        return bool(self.token)

    def list_scans(self):
        """List all configured scans."""
        result = self._req("GET", "/scans")
        scans = []
        for s in result.get("scans", []):
            scans.append({
                "id": s.get("id"),
                "name": s.get("name"),
                "status": s.get("status"),
                "last_modification_date": s.get("last_modification_date"),
            })
        return scans

    def create_scan(self, name, targets, template_uuid=None):
        """Create a new scan configuration."""
        data = {
            "uuid": template_uuid or "ab4bacd2-05f6-425c-9d79-3f5a0a1f28b0",
            "settings": {
                "name": name,
                "text_targets": targets,
                "enabled": True,
                "launch": "ON_DEMAND",
            },
        }
        return self._req("POST", "/scans", data)

    def launch_scan(self, scan_id):
        """Launch a configured scan."""
        return self._req("POST", f"/scans/{scan_id}/launch")

    def get_scan_status(self, scan_id):
        """Get current scan status."""
        result = self._req("GET", f"/scans/{scan_id}")
        info = result.get("info", {})
        return {
            "status": info.get("status"),
            "host_count": info.get("hostcount", 0),
            "name": info.get("name"),
        }

    def get_scan_results(self, scan_id):
        """Retrieve scan results with vulnerability details."""
        result = self._req("GET", f"/scans/{scan_id}")
        hosts = []
        for h in result.get("hosts", []):
            hosts.append({
                "hostname": h.get("hostname"),
                "host_id": h.get("host_id"),
                "critical": h.get("critical", 0),
                "high": h.get("high", 0),
                "medium": h.get("medium", 0),
                "low": h.get("low", 0),
                "info": h.get("info", 0),
            })

        vulns = []
        for v in result.get("vulnerabilities", []):
            vulns.append({
                "plugin_id": v.get("plugin_id"),
                "plugin_name": v.get("plugin_name"),
                "severity": v.get("severity"),
                "count": v.get("count", 0),
                "family": v.get("plugin_family"),
            })

        return {
            "scan_id": scan_id,
            "hosts": hosts,
            "vulnerabilities": sorted(vulns, key=lambda x: x.get("severity", 0), reverse=True),
            "total_vulns": len(vulns),
        }

    def export_scan(self, scan_id, fmt="nessus"):
        """Export scan results in specified format."""
        result = self._req("POST", f"/scans/{scan_id}/export", {"format": fmt})
        file_id = result.get("file")
        if not file_id:
            return {"error": "Export failed"}

        for _ in range(60):
            status = self._req("GET", f"/scans/{scan_id}/export/{file_id}/status")
            if status.get("status") == "ready":
                break
            time.sleep(5)

        return {"file_id": file_id, "format": fmt, "status": "ready"}

    def generate_report(self, scan_id):
        results = self.get_scan_results(scan_id)
        report = {
            "report_date": datetime.utcnow().isoformat(),
            "scan_id": scan_id,
            "results": results,
        }
        out = self.output_dir / "nessus_report.json"
        with open(out, "w") as f:
            json.dump(report, f, indent=2)
        print(json.dumps(report, indent=2))
        return report


def main():
    if len(sys.argv) < 3:
        print("Usage: agent.py <nessus_url> <scan_id> [--user admin] [--pass password]")
        sys.exit(1)

    host = sys.argv[1]
    scan_id = int(sys.argv[2])
    user = "admin"
    password = ""
    if "--user" in sys.argv:
        user = sys.argv[sys.argv.index("--user") + 1]
    if "--pass" in sys.argv:
        password = sys.argv[sys.argv.index("--pass") + 1]

    agent = NessusScanAgent(host, user, password)
    if agent.authenticate():
        agent.generate_report(scan_id)
    else:
        print("Authentication failed")
        sys.exit(1)


if __name__ == "__main__":
    main()
process.py20.8 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Nessus Infrastructure Scanning Automation Script

Automates vulnerability scanning workflows using the Nessus REST API:
- Creates and launches scans
- Monitors scan progress
- Exports and parses results
- Generates summary reports with severity breakdown

Requirements:
    pip install requests defusedxml pandas jinja2

Usage:
    python process.py --host https://localhost:8834 --user admin --password <pass> --targets 192.168.1.0/24
"""

import argparse
import json
import os
import sys
import time
from datetime import datetime
from pathlib import Path

import requests
import defusedxml.ElementTree as ET
import pandas as pd

# Suppress SSL warnings for self-signed Nessus certs
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)


class NessusScanner:
    """Manages Nessus vulnerability scanning via REST API."""

    def __init__(self, host: str, username: str, password: str, verify_ssl: bool = False):
        self.host = host.rstrip("/")
        self.username = username
        self.password = password
        self.verify_ssl = verify_ssl
        self.token = None
        self.headers = {"Content-Type": "application/json"}

    def authenticate(self) -> bool:
        """Authenticate to Nessus and obtain session token."""
        url = f"{self.host}/session"
        payload = {"username": self.username, "password": self.password}
        response = requests.post(url, json=payload, verify=self.verify_ssl)
        if response.status_code == 200:
            self.token = response.json()["token"]
            self.headers["X-Cookie"] = f"token={self.token}"
            print(f"[+] Authenticated successfully to {self.host}")
            return True
        else:
            print(f"[-] Authentication failed: {response.status_code} {response.text}")
            return False

    def logout(self):
        """Destroy the current session."""
        if self.token:
            url = f"{self.host}/session"
            requests.delete(url, headers=self.headers, verify=self.verify_ssl)
            print("[+] Session destroyed")

    def list_scan_templates(self) -> list:
        """Retrieve available scan templates."""
        url = f"{self.host}/editor/scan/templates"
        response = requests.get(url, headers=self.headers, verify=self.verify_ssl)
        if response.status_code == 200:
            templates = response.json().get("templates", [])
            return [{"uuid": t["uuid"], "name": t["name"], "title": t["title"]} for t in templates]
        return []

    def get_template_uuid(self, template_name: str = "advanced") -> str:
        """Get UUID for a specific scan template."""
        templates = self.list_scan_templates()
        for t in templates:
            if t["name"] == template_name:
                return t["uuid"]
        if templates:
            return templates[0]["uuid"]
        raise ValueError(f"No template found matching '{template_name}'")

    def create_scan(self, name: str, targets: str, template_name: str = "advanced",
                    credentials: dict = None, policy_id: int = None) -> int:
        """Create a new scan configuration."""
        url = f"{self.host}/scans"
        template_uuid = self.get_template_uuid(template_name)

        settings = {
            "name": name,
            "text_targets": targets,
            "launch": "ON_DEMAND",
            "enabled": True,
            "description": f"Automated infrastructure scan created {datetime.now().isoformat()}"
        }

        if policy_id:
            settings["policy_id"] = policy_id

        payload = {"uuid": template_uuid, "settings": settings}

        if credentials:
            payload["credentials"] = credentials

        response = requests.post(url, headers=self.headers, json=payload, verify=self.verify_ssl)
        if response.status_code == 200:
            scan_id = response.json()["scan"]["id"]
            print(f"[+] Scan created: ID={scan_id}, Name='{name}'")
            return scan_id
        else:
            raise RuntimeError(f"Failed to create scan: {response.status_code} {response.text}")

    def launch_scan(self, scan_id: int) -> str:
        """Launch a scan and return the scan UUID."""
        url = f"{self.host}/scans/{scan_id}/launch"
        response = requests.post(url, headers=self.headers, verify=self.verify_ssl)
        if response.status_code == 200:
            scan_uuid = response.json().get("scan_uuid", "")
            print(f"[+] Scan {scan_id} launched (UUID: {scan_uuid})")
            return scan_uuid
        else:
            raise RuntimeError(f"Failed to launch scan: {response.status_code}")

    def get_scan_status(self, scan_id: int) -> dict:
        """Get current scan status and summary."""
        url = f"{self.host}/scans/{scan_id}"
        response = requests.get(url, headers=self.headers, verify=self.verify_ssl)
        if response.status_code == 200:
            data = response.json()
            info = data.get("info", {})
            return {
                "status": info.get("status", "unknown"),
                "host_count": info.get("hostcount", 0),
                "scanner_name": info.get("scanner_name", ""),
                "policy": info.get("policy", ""),
                "hosts": data.get("hosts", []),
                "vulnerabilities": data.get("vulnerabilities", [])
            }
        return {"status": "error"}

    def wait_for_scan(self, scan_id: int, poll_interval: int = 30, timeout: int = 7200) -> bool:
        """Poll scan status until completion or timeout."""
        start_time = time.time()
        print(f"[*] Waiting for scan {scan_id} to complete (timeout: {timeout}s)...")

        while time.time() - start_time < timeout:
            status = self.get_scan_status(scan_id)
            current = status["status"]
            elapsed = int(time.time() - start_time)

            if current == "completed":
                print(f"[+] Scan completed in {elapsed}s. Hosts scanned: {status['host_count']}")
                return True
            elif current in ("canceled", "aborted"):
                print(f"[-] Scan {current} after {elapsed}s")
                return False
            elif current == "running":
                print(f"[*] Scan running... ({elapsed}s elapsed, {status['host_count']} hosts)")
            else:
                print(f"[*] Scan status: {current} ({elapsed}s elapsed)")

            time.sleep(poll_interval)

        print(f"[-] Scan timed out after {timeout}s")
        return False

    def export_scan(self, scan_id: int, export_format: str = "nessus",
                    output_dir: str = ".") -> str:
        """Export scan results to file."""
        url = f"{self.host}/scans/{scan_id}/export"
        payload = {"format": export_format}

        if export_format == "csv":
            payload["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
                }
            }

        response = requests.post(url, headers=self.headers, json=payload, verify=self.verify_ssl)
        if response.status_code != 200:
            raise RuntimeError(f"Export request failed: {response.status_code}")

        file_id = response.json()["file"]
        print(f"[*] Export requested (file_id: {file_id}, format: {export_format})")

        # Poll export status
        status_url = f"{self.host}/scans/{scan_id}/export/{file_id}/status"
        for _ in range(60):
            status_resp = requests.get(status_url, headers=self.headers, verify=self.verify_ssl)
            if status_resp.status_code == 200 and status_resp.json().get("status") == "ready":
                break
            time.sleep(5)

        # Download file
        download_url = f"{self.host}/scans/{scan_id}/export/{file_id}/download"
        download_resp = requests.get(download_url, headers=self.headers, verify=self.verify_ssl)

        ext = {"nessus": "nessus", "csv": "csv", "html": "html", "pdf": "pdf"}.get(export_format, "xml")
        output_path = os.path.join(output_dir, f"scan_{scan_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.{ext}")

        with open(output_path, "wb") as f:
            f.write(download_resp.content)

        print(f"[+] Results exported to: {output_path}")
        return output_path


class NessusResultParser:
    """Parse and analyze Nessus scan results (.nessus XML format)."""

    SEVERITY_MAP = {0: "Informational", 1: "Low", 2: "Medium", 3: "High", 4: "Critical"}

    def __init__(self, nessus_file: str):
        self.nessus_file = nessus_file
        self.tree = ET.parse(nessus_file)
        self.root = self.tree.getroot()
        self.findings = []

    def parse(self) -> list:
        """Parse all findings from the .nessus XML file."""
        self.findings = []

        for report in self.root.findall(".//Report"):
            for host in report.findall("ReportHost"):
                hostname = host.get("name", "unknown")
                host_ip = ""
                host_os = ""

                # Extract host properties
                for tag in host.findall(".//HostProperties/tag"):
                    if tag.get("name") == "host-ip":
                        host_ip = tag.text or ""
                    elif tag.get("name") == "operating-system":
                        host_os = tag.text or ""

                for item in host.findall("ReportItem"):
                    severity = int(item.get("severity", "0"))
                    plugin_id = item.get("pluginID", "")
                    plugin_name = item.get("pluginName", "")
                    port = item.get("port", "0")
                    protocol = item.get("protocol", "")
                    svc_name = item.get("svc_name", "")

                    finding = {
                        "hostname": hostname,
                        "host_ip": host_ip,
                        "host_os": host_os,
                        "plugin_id": plugin_id,
                        "plugin_name": plugin_name,
                        "severity": severity,
                        "severity_name": self.SEVERITY_MAP.get(severity, "Unknown"),
                        "port": port,
                        "protocol": protocol,
                        "service": svc_name,
                        "cvss_base": item.findtext("cvss3_base_score", item.findtext("cvss_base_score", "0")),
                        "cve": item.findtext("cve", ""),
                        "synopsis": item.findtext("synopsis", ""),
                        "description": item.findtext("description", ""),
                        "solution": item.findtext("solution", ""),
                        "see_also": item.findtext("see_also", ""),
                        "plugin_output": item.findtext("plugin_output", ""),
                        "exploit_available": item.findtext("exploit_available", "false"),
                        "exploitability_ease": item.findtext("exploitability_ease", ""),
                    }
                    self.findings.append(finding)

        print(f"[+] Parsed {len(self.findings)} findings from {self.nessus_file}")
        return self.findings

    def to_dataframe(self) -> pd.DataFrame:
        """Convert findings to a pandas DataFrame."""
        if not self.findings:
            self.parse()
        return pd.DataFrame(self.findings)

    def severity_summary(self) -> dict:
        """Generate severity breakdown summary."""
        df = self.to_dataframe()
        summary = df["severity_name"].value_counts().to_dict()
        return {
            "Critical": summary.get("Critical", 0),
            "High": summary.get("High", 0),
            "Medium": summary.get("Medium", 0),
            "Low": summary.get("Low", 0),
            "Informational": summary.get("Informational", 0),
            "Total": len(df),
            "Unique Hosts": df["hostname"].nunique(),
            "Unique Plugins": df["plugin_id"].nunique(),
            "Exploitable": len(df[df["exploit_available"] == "true"])
        }

    def top_vulnerabilities(self, n: int = 20) -> pd.DataFrame:
        """Get top N vulnerabilities by severity and host count."""
        df = self.to_dataframe()
        vuln_df = df[df["severity"] >= 2].copy()

        top = (vuln_df.groupby(["plugin_id", "plugin_name", "severity_name", "cvss_base", "cve"])
               .agg(affected_hosts=("hostname", "nunique"))
               .reset_index()
               .sort_values(["severity_name", "affected_hosts"], ascending=[True, False])
               .head(n))

        return top

    def host_risk_scores(self) -> pd.DataFrame:
        """Calculate risk score per host based on vulnerability severity."""
        df = self.to_dataframe()
        severity_weights = {4: 10, 3: 5, 2: 2, 1: 0.5, 0: 0}

        df["weight"] = df["severity"].map(severity_weights)
        host_scores = (df.groupby(["hostname", "host_ip", "host_os"])
                       .agg(
                           risk_score=("weight", "sum"),
                           critical=("severity", lambda x: (x == 4).sum()),
                           high=("severity", lambda x: (x == 3).sum()),
                           medium=("severity", lambda x: (x == 2).sum()),
                           low=("severity", lambda x: (x == 1).sum()),
                           total_findings=("severity", "count")
                       )
                       .reset_index()
                       .sort_values("risk_score", ascending=False))

        return host_scores

    def generate_report(self, output_path: str):
        """Generate an HTML summary report."""
        summary = self.severity_summary()
        top_vulns = self.top_vulnerabilities()
        host_risks = self.host_risk_scores().head(20)

        html = f"""<!DOCTYPE html>
<html>
<head>
    <title>Nessus Scan Report - {datetime.now().strftime('%Y-%m-%d')}</title>
    <style>
        body {{ font-family: Arial, sans-serif; margin: 20px; background: #f5f5f5; }}
        .header {{ background: #1a1a2e; color: white; padding: 20px; border-radius: 8px; }}
        .summary {{ display: flex; gap: 15px; margin: 20px 0; }}
        .card {{ background: white; padding: 20px; border-radius: 8px; flex: 1;
                 box-shadow: 0 2px 4px rgba(0,0,0,0.1); text-align: center; }}
        .critical {{ border-top: 4px solid #e74c3c; }}
        .high {{ border-top: 4px solid #e67e22; }}
        .medium {{ border-top: 4px solid #f1c40f; }}
        .low {{ border-top: 4px solid #3498db; }}
        .card h3 {{ margin: 0; font-size: 2em; }}
        .card p {{ margin: 5px 0 0; color: #666; }}
        table {{ width: 100%; border-collapse: collapse; background: white;
                 border-radius: 8px; overflow: hidden; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }}
        th {{ background: #2c3e50; color: white; padding: 12px; text-align: left; }}
        td {{ padding: 10px 12px; border-bottom: 1px solid #eee; }}
        tr:hover {{ background: #f8f9fa; }}
    </style>
</head>
<body>
    <div class="header">
        <h1>Vulnerability Scan Report</h1>
        <p>Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} | Source: {self.nessus_file}</p>
    </div>

    <div class="summary">
        <div class="card critical"><h3>{summary['Critical']}</h3><p>Critical</p></div>
        <div class="card high"><h3>{summary['High']}</h3><p>High</p></div>
        <div class="card medium"><h3>{summary['Medium']}</h3><p>Medium</p></div>
        <div class="card low"><h3>{summary['Low']}</h3><p>Low</p></div>
    </div>

    <p><strong>Total Findings:</strong> {summary['Total']} |
       <strong>Unique Hosts:</strong> {summary['Unique Hosts']} |
       <strong>Exploitable:</strong> {summary['Exploitable']}</p>

    <h2>Top Vulnerabilities</h2>
    <table>
        <tr><th>Plugin ID</th><th>Name</th><th>Severity</th><th>CVSS</th><th>CVE</th><th>Affected Hosts</th></tr>
        {''.join(f'<tr><td>{r.plugin_id}</td><td>{r.plugin_name}</td><td>{r.severity_name}</td><td>{r.cvss_base}</td><td>{r.cve}</td><td>{r.affected_hosts}</td></tr>' for r in top_vulns.itertuples())}
    </table>

    <h2>Host Risk Scores (Top 20)</h2>
    <table>
        <tr><th>Host</th><th>IP</th><th>OS</th><th>Risk Score</th><th>Critical</th><th>High</th><th>Medium</th><th>Total</th></tr>
        {''.join(f'<tr><td>{r.hostname}</td><td>{r.host_ip}</td><td>{str(r.host_os)[:50]}</td><td>{r.risk_score:.0f}</td><td>{r.critical}</td><td>{r.high}</td><td>{r.medium}</td><td>{r.total_findings}</td></tr>' for r in host_risks.itertuples())}
    </table>
</body>
</html>"""

        with open(output_path, "w", encoding="utf-8") as f:
            f.write(html)
        print(f"[+] Report generated: {output_path}")


def main():
    parser = argparse.ArgumentParser(description="Nessus Infrastructure Scanning Automation")
    subparsers = parser.add_subparsers(dest="command", help="Available commands")

    # Scan command
    scan_parser = subparsers.add_parser("scan", help="Create and run a vulnerability scan")
    scan_parser.add_argument("--host", required=True, help="Nessus server URL (e.g., https://localhost:8834)")
    scan_parser.add_argument("--user", required=True, help="Nessus username")
    scan_parser.add_argument("--password", required=True, help="Nessus password")
    scan_parser.add_argument("--targets", required=True, help="Target IPs/ranges (comma-separated or CIDR)")
    scan_parser.add_argument("--name", default=None, help="Scan name")
    scan_parser.add_argument("--template", default="advanced", help="Scan template name")
    scan_parser.add_argument("--export-format", default="nessus", choices=["nessus", "csv", "html", "pdf"])
    scan_parser.add_argument("--output-dir", default=".", help="Directory for exported results")
    scan_parser.add_argument("--timeout", type=int, default=7200, help="Scan timeout in seconds")

    # Parse command
    parse_parser = subparsers.add_parser("parse", help="Parse and analyze .nessus scan results")
    parse_parser.add_argument("--file", required=True, help="Path to .nessus XML file")
    parse_parser.add_argument("--report", default=None, help="Output HTML report path")
    parse_parser.add_argument("--csv-output", default=None, help="Export findings to CSV")
    parse_parser.add_argument("--min-severity", type=int, default=0, choices=[0, 1, 2, 3, 4],
                              help="Minimum severity to include (0=Info, 4=Critical)")

    args = parser.parse_args()

    if args.command == "scan":
        scanner = NessusScanner(args.host, args.user, args.password)

        if not scanner.authenticate():
            sys.exit(1)

        try:
            scan_name = args.name or f"Infrastructure_Scan_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
            scan_id = scanner.create_scan(scan_name, args.targets, args.template)
            scanner.launch_scan(scan_id)

            if scanner.wait_for_scan(scan_id, timeout=args.timeout):
                os.makedirs(args.output_dir, exist_ok=True)
                export_path = scanner.export_scan(scan_id, args.export_format, args.output_dir)

                # Auto-parse if exported in nessus format
                if args.export_format == "nessus":
                    result_parser = NessusResultParser(export_path)
                    result_parser.parse()
                    summary = result_parser.severity_summary()
                    print("\n=== Scan Summary ===")
                    for key, value in summary.items():
                        print(f"  {key}: {value}")

                    report_path = os.path.join(args.output_dir, f"report_{scan_id}.html")
                    result_parser.generate_report(report_path)
            else:
                print("[-] Scan did not complete successfully")
                sys.exit(1)
        finally:
            scanner.logout()

    elif args.command == "parse":
        result_parser = NessusResultParser(args.file)
        result_parser.parse()

        summary = result_parser.severity_summary()
        print("\n=== Vulnerability Summary ===")
        for key, value in summary.items():
            print(f"  {key}: {value}")

        if args.min_severity > 0:
            df = result_parser.to_dataframe()
            filtered = df[df["severity"] >= args.min_severity]
            print(f"\n[*] Findings with severity >= {args.min_severity}: {len(filtered)}")

        if args.report:
            result_parser.generate_report(args.report)

        if args.csv_output:
            df = result_parser.to_dataframe()
            df.to_csv(args.csv_output, index=False)
            print(f"[+] CSV exported to: {args.csv_output}")

        print("\n=== Top 10 Vulnerabilities ===")
        top = result_parser.top_vulnerabilities(10)
        print(top.to_string(index=False))

        print("\n=== Top 10 Riskiest Hosts ===")
        hosts = result_parser.host_risk_scores().head(10)
        print(hosts.to_string(index=False))

    else:
        parser.print_help()


if __name__ == "__main__":
    main()

Assets 1

template.mdtext/markdown · 3.6 KB
Keep exploring