CVE-Scan: Automating the Nmap-to-Vulnerability Pipeline
Hook
Security scanners find open ports. Vulnerability databases catalog exploits. But between these two systems lies a manual, error-prone translation layer that most security teams handle with spreadsheets and grep.
Context
Before automated vulnerability correlation tools, penetration testers faced a tedious workflow: run Nmap to discover services, manually cross-reference each service version against CVE databases, look up associated weaknesses in CWE, and check for exploit availability in DPE. For a network with dozens of hosts running various service versions, this process consumed hours of billable time that could be spent on actual exploitation and remediation.
CVE-Scan emerged from this pain point in 2015, when NorthernSec built a pipeline to automatically enrich Nmap XML output with vulnerability intelligence from CVE-Search. The tool recognized that Nmap already does excellent service fingerprinting—extracting exact version strings from SSH banners, HTTP headers, and protocol responses—but stops short of answering the critical question: "Which of these versions have known exploits?" By creating a structured workflow from reconnaissance to vulnerability enumeration, CVE-Scan attempted to eliminate the manual lookup phase entirely, letting security professionals focus on verifying and exploiting findings rather than catalog management.
Technical Insight
CVE-Scan implements a three-stage pipeline architecture that separates concerns cleanly: conversion, analysis, and visualization. This modular design is its greatest strength, allowing each component to be swapped, extended, or run independently depending on your workflow requirements.
The first stage (converter.py) parses Nmap's XML output and transforms it into a normalized JSON structure. Nmap's XML format is notoriously verbose and nested, with service information buried in elements that contain children with attributes for product, version, and extrainfo. The converter extracts these service tuples and flattens them into a more query-friendly format. Here's a simplified example of what the transformation looks like:
# Nmap XML structure (conceptual)
<host>
<address addr="192.168.1.10"/>
<ports>
<port protocol="tcp" portid="22">
<service name="ssh" product="OpenSSH" version="7.2p2" />
</port>
</ports>
</host>
# CVE-Scan normalized JSON output
{
"host": "192.168.1.10",
"services": [
{
"port": 22,
"protocol": "tcp",
"service": "ssh",
"product": "OpenSSH",
"version": "7.2p2"
}
]
}
The second stage (analyzer.py) is where the real intelligence happens. It takes each service tuple and queries the CVE-Search API, which maintains a searchable database of CVE entries indexed by product and version. The analyzer constructs queries like "OpenSSH 7.2p2" and retrieves matching CVE records, complete with CVSS scores, CWE classifications (like CWE-119 for buffer errors), and links to exploit databases. The critical architectural decision here was to rely on an external CVE-Search instance rather than bundling vulnerability data. This keeps the tool lightweight and ensures access to up-to-date CVE information, but it also introduces a hard dependency on network connectivity and API availability.
The query logic follows a version-matching strategy that handles exact matches but struggles with version ranges. For instance, if CVE-2016-0777 affects OpenSSH versions 5.4 through 7.2p2, the analyzer will match 7.2p2 exactly, but might miss vulnerabilities affecting "7.2 and earlier" if the version string varies slightly (like "7.2" vs "7.2p2"). This is where Nmap's version detection precision becomes critical—the more specific the version string, the more accurate the CVE correlation.
The final stage (visualizer.py) presents results through either a Flask-based web interface or terminal output. The web interface groups findings by severity and allows filtering by CVE, CWE, or affected host, which is invaluable during client reporting or team collaboration. Terminal output works better for scripting and CI/CD integration:
# Example pipeline execution
python Nmap2CVE-Search.py --nmap-xml scan-results.xml --output report.json
# Output structure includes enriched vulnerability data
{
"192.168.1.10": {
"OpenSSH_7.2p2": [
{
"cve": "CVE-2016-0777",
"cvss": 7.5,
"cwe": "CWE-200",
"summary": "Information disclosure vulnerability...",
"references": ["https://www.exploit-db.com/..."]
}
]
}
}
The architecture's modularity means you can inject custom processing between stages. Want to filter out low-severity CVEs before visualization? Parse the analyzer output and strip entries below a CVSS threshold. Need to correlate findings with your asset management database? The JSON intermediate format makes it straightforward to join host data with CMDB records. This flexibility transforms CVE-Scan from a single-purpose tool into a building block for custom security workflows.
One underappreciated design choice is the tool's stateless operation—it doesn't maintain a database of historical scans or track remediation status. While this seems limiting compared to full-featured vulnerability management platforms, it actually makes CVE-Scan easier to integrate into existing workflows. You control persistence and state management, whether that's committing JSON reports to Git for change tracking or feeding results into Elasticsearch for time-series analysis.
Gotcha
CVE-Scan's reliance on an external CVE-Search instance is both its modularity strength and its operational weakness. You have two options: use CIRCL's public CVE-Search API at https://cve.circl.lu, which requires no setup but subjects you to rate limits, potential downtime, and sends your scan data to a third party; or deploy your own CVE-Search instance, which solves privacy and availability concerns but requires maintaining a MongoDB database and keeping vulnerability feeds updated. The latter option adds significant operational overhead—CVE-Search pulls from multiple data sources (NVD, MITRE, etc.) and requires periodic synchronization jobs to stay current.
The tool's accuracy is fundamentally bounded by Nmap's service detection capabilities. Nmap version detection works by sending protocol-specific probes and pattern-matching responses against signature databases, but it frequently produces incomplete version strings ("Apache 2.4.x" instead of "Apache 2.4.18") or misidentifies services entirely when facing custom banners or application-layer proxies. CVE-Scan has no mechanism to handle version uncertainty—it queries exactly what Nmap reports, meaning ambiguous version strings produce incomplete vulnerability results. You'll often find yourself re-running Nmap with aggressive version detection flags (-sV --version-intensity 9) to squeeze out more precise version information, which increases scan time and network noise.
Finally, the project's age shows in its code. Last updated in 2015, it uses Python 2 patterns (though likely Python 3 compatible with minor fixes), lacks modern dependency management (no requirements.txt with pinned versions), and doesn't handle API errors gracefully. If CIRCL's API changes its response format or rate-limiting behavior, you'll be debugging HTTP requests manually. The small community (280 stars, limited issues/PRs) means you're largely on your own for troubleshooting and enhancements.
Verdict
Use CVE-Scan if you're conducting time-boxed penetration tests where you need quick vulnerability enumeration from existing Nmap scans, already have CVE-Search infrastructure deployed, or want a learning reference for building custom security automation pipelines. Its modular design and JSON-based workflow make it valuable for integrating into broader security toolchains, particularly in environments where you control data processing and don't need enterprise support. Skip it if you require production-grade reliability, active maintenance, or work in regulated environments where tool provenance matters. The project's dormancy since 2015 makes it unsuitable for ongoing security operations—invest instead in actively maintained alternatives like Vulners NSE scripts for Nmap integration or Nuclei for broader vulnerability scanning. Also skip if you lack CVE-Search infrastructure and can't justify deploying it; the dependency overhead outweighs the benefits for most use cases when commercial scanners like Nessus provide similar correlation out of the box.