A network vulnerability scanner written in Python. PortRecon finds open TCP ports on a target, fingerprints the services on them from their banners, and checks the NIST National Vulnerability Database (NVD) for known CVEs. It writes the results to an HTML report color-coded by severity and to a JSON file.
β οΈ Legal and ethical use only. Read the Legal & ethical disclaimer at the end before you use this tool.
A real scan of intentionally vulnerable test services on localhost. The report follows the viewer's light or dark theme.
| Stage | What it does |
|---|---|
| Port scanning | Multi-threaded TCP connect scan (socket + ThreadPoolExecutor). The timeout and thread count can both be set. It accepts ranges, lists and a top100 shortcut (1-1000, 22,80,443, top100). |
| Multi-host scanning | Scan a whole subnet (192.168.56.0/24), an address range (10.0.0.1-20) or a list of hosts in one run. A quick host discovery sweep finds which hosts are up first, so dead addresses are skipped. Each host with open ports gets its own report, and a combined summary is printed at the end. A 1024-host cap guards against typos. |
| Banner grabbing | Reads passive banners (SSH, FTP, SMTP, MySQL handshake) and sends protocol probes when a service stays quiet (HTTP HEAD, over TLS for 443/8443). Regex signatures pull out the product and version, e.g. OpenSSH 7.4 or Apache httpd 2.4.29. |
| CVE lookup | Keyword search against the NVD CVE API 2.0. It returns the CVE ID, description, CVSS score and severity. A thread-safe sliding-window rate limiter and a 24-hour on-disk cache limit the number of API calls. |
| Reporting | A self-contained HTML report (light and dark mode, severity color-coding, links to NVD) and a raw JSON export, both timestamped under reports/. |
| False-positive detection | Before scanning, PortRecon checks whether this machine intercepts outbound connections, which antivirus web shields and proxies often do on ports like 80 and 8080. Intercepted ports would show as open on every host, so they are excluded and the report explains why. |
| Safety | Shows a legal warning and requires an explicit yes before any scan. All untrusted data (banners, CVE text) is HTML-escaped, so a malicious service can't inject script into the report. |
It uses only the standard library plus requests (for the NVD API). It doesn't depend on nmap or any other external scanning library.
Requires Python 3.10+.
git clone https://github.com/KrishivSharma45/portrecon.git
cd portrecon
pip install -r requirements.txtOptional: get a free NVD API key to raise the rate limit from 5 to 50 requests per 30 seconds:
export NVD_API_KEY="your-key" # Linux / macOS
setx NVD_API_KEY "your-key" # Windows (restart the terminal afterwards)python main.py --target <IP, hostname or subnet> --ports <range> [--threads N] [--output NAME]| Option | Default | Description |
|---|---|---|
-t, --target |
required | IP, hostname, CIDR subnet (192.168.56.0/24), last-octet range (10.0.0.1-20), or a comma-separated list of these |
-p, --ports |
1-1000 |
Ports to scan: 1-1000, 22,80,443, 1-100,8080-8090, or top100 for the 100 most common ports |
--threads |
100 |
Number of concurrent scanning threads (1β1000) |
--timeout |
1.0 |
Per-port connect timeout in seconds |
-o, --output |
portrecon_<ip> |
Base name for the report files (a timestamp is appended; multi-host scans also append the IP) |
--max-cves |
10 |
Maximum CVEs reported per service, highest CVSS score first |
--no-cve |
off | Skip the NVD lookup and only scan and fingerprint |
--no-discovery |
off | Multi-host scans: skip host discovery and port-scan every address (like nmap's -Pn) |
-v, --verbose |
off | Debug logging |
# Scan the top 1000 ports of a Metasploitable VM on a host-only network
python main.py --target 192.168.56.101 --ports 1-1000 --output metasploitable
# Quick scan of the 100 most common ports
python main.py -t 192.168.56.101 -p top100
# Sweep your whole lab subnet for common services
python main.py -t 192.168.56.0/24 -p top100 --timeout 0.5 --output homelab
# Scan a few hosts by range or list
python main.py -t 192.168.56.101-105 -p 1-1000
python main.py -t 192.168.56.101,192.168.56.120 -p 22,80,443
# Scan a few specific ports with more threads and a shorter timeout
python main.py -t 10.10.10.5 -p 21,22,80,443,3306,8080 --threads 200 --timeout 0.5
# Scan all ports, without CVE lookups
python main.py -t 192.168.1.50 -p 1-65535 --threads 500 --no-cveHow subnet scans work: first, every address gets a TCP probe on a few common ports (22, 80, 135, 443, 445, 3389, 8080). A host counts as up if any probe connects or is refused, because a refusal (TCP RST) can only come from a live machine. Only live hosts get the full port scan, so a /24 with a handful of machines takes seconds instead of minutes.
Hosts behind a firewall that silently drops every probe will look down. If you know a host is up but discovery misses it, add
--no-discovery.
[*] Target: 192.168.56.101 (192.168.56.101) | Ports: 1000 | Threads: 100 | Timeout: 1.0s
[*] Scanning... 1000/1000 ports checked (100.0%) 287 ports/s done in 3s
[+] Open ports: 21, 22, 80
[*] Grabbing service banners...
[+] 21/tcp ftp vsftpd 2.3.4
[+] 22/tcp ssh OpenSSH 7.4
[+] 80/tcp http Apache httpd 2.4.29
[*] Querying the NIST NVD for known vulnerabilities...
[+] Port 21: 1 CVE(s) found (1 critical)
[+] Port 22: 6 CVE(s) found (4 high, 2 medium)
[+] Port 80: 2 CVE(s) found (1 critical, 1 medium)
================================================================
SCAN SUMMARY
================================================================
PORT SERVICE PRODUCT CVEs
21/tcp ftp vsftpd 2.3.4 1
22/tcp ssh OpenSSH 7.4 6
80/tcp http Apache httpd 2.4.29 2
----------------------------------------------------------------
Open ports found: 3
Vulnerabilities found: 9
HTML report: reports/metasploitable_20260924_171956.html
JSON report: reports/metasploitable_20260924_171956.json
================================================================
Each stage is a standalone module that you can run or import on its own:
python -m scanner.port_scanner 127.0.0.1 1-1024 # port scan only
python -m scanner.banner_grabber 127.0.0.1 22,80 # fingerprint specific ports
python -m scanner.cve_lookup OpenSSH 7.4 # CVE lookup only
python -m scanner.report_generator # render a demo reportfrom scanner.port_scanner import scan_ports
from scanner.banner_grabber import grab_banners
from scanner.cve_lookup import CVELookup
open_ports = scan_ports("192.168.56.101", range(1, 1025), threads=200)
services = grab_banners("192.168.56.101", open_ports)
cves = CVELookup().search("OpenSSH", "7.4")The test suite covers every module. It uses local sockets and a stubbed NVD API, so it needs no network access and never scans anything external.
pip install -r requirements-dev.txt
pytest # run the tests
ruff check . # lintportrecon/
βββ scanner/
β βββ __init__.py
β βββ port_scanner.py # Multi-threaded TCP connect scan and port-range parsing
β βββ banner_grabber.py # Banner retrieval, protocol probes, service fingerprinting
β βββ cve_lookup.py # NVD API client with rate limiting and caching
β βββ report_generator.py # HTML and JSON report output
βββ tests/ # pytest suite (no network required)
βββ main.py # CLI entry point and pipeline orchestration
βββ requirements.txt
βββ requirements-dev.txt # requirements.txt + pytest
βββ pytest.ini
βββ LICENSE
βββ README.md
- Host discovery (multi-host scans only). Each address gets parallel TCP probes; any reply, including a connection refusal, marks the host as up.
- TCP connect scan. For each port, a worker thread calls
connect_ex(). A completed three-way handshake means the port is open. This scan type needs no root or raw-socket privileges. The trade-off is that it's noisier than a SYN scan, because the target logs full connections. - Banner grabbing. PortRecon reconnects to each open port and waits briefly for the service to speak first. If it stays silent, PortRecon sends an HTTP
HEADprobe, over TLS on HTTPS ports. The response is matched against an ordered list of regex signatures. - CVE lookup. The product and version are sent to the NVD as a keyword search. Results are ranked by CVSS score. PortRecon uses the newest CVSS version available (v4.0 β v3.1 β v3.0 β v2.0) and prefers NVD's own "Primary" score. Services without a detected version are skipped, because searching a bare product name returns thousands of irrelevant CVEs.
- Reporting. Findings are combined into one structured dict. It is written out as JSON and rendered as HTML.
- Intercepted ports are skipped. If security software or a proxy on your machine intercepts a port (usually 80/8080), PortRecon can't tell a real open port from the interceptor answering, so that port is excluded from the scan with a warning. To scan those ports, pause the antivirus "web shield" or proxy, or scan from another machine.
- Keyword matching is heuristic. It can report false positives, such as CVEs that mention a version only as the fix version ("before 7.4"). It can also miss CVEs that are described in different words. It also doesn't know about vendor backports: Ubuntu's
OpenSSH 7.4may already be patched. Treat the results as leads to verify, not confirmed vulnerabilities. - IPv4 and TCP only. There is no UDP scanning.
- Services that give no banner and don't answer HTTP are reported as
unknown.
Only scan systems you own or have explicit, written permission to test.
Unauthorized port scanning and vulnerability probing may be illegal in your jurisdiction. Relevant laws include the U.S. Computer Fraud and Abuse Act, the UK Computer Misuse Act and equivalent laws elsewhere. It may also violate your ISP's or cloud provider's terms of service. Permission to access a network is not the same as permission to scan it.
Good legal targets for practice:
- Your own home lab or virtual machines
- Intentionally vulnerable VMs such as Metasploitable 2/3, on a host-only network
- Lab machines on platforms such as TryHackMe and Hack The Box, within each platform's rules
scanme.nmap.org, which the Nmap project explicitly permits for light scanning
This tool is provided for educational and authorized security-testing purposes. The author accepts no liability for misuse or damage. You are responsible for your actions.
Krishiv Sharma
- π GitHub: @KrishivSharma45
- πΌ LinkedIn: Krishiv Sharma
If you found this project useful, consider giving it a β on GitHub!
