Loading
Cybersecurity undergraduate and web developer based in Sri Lanka, passionate about penetration testing, VAPT, and ethical hacking.
Python script that wraps Nmap for automated recon — service version detection, OS fingerprinting, and outputs a formatted HTML/JSON report.
This writeup documents the development of a custom Python-based Nmap wrapper tool built to streamline penetration testing workflows. The tool automates common Nmap scan profiles, parses XML output into structured reports, flags known vulnerable service versions, and exports results as HTML and CSV. It was built for personal use during CTF competitions and lab engagements to reduce repetitive manual scanning steps.
The project was structured as a single CLI tool with modular functions for scanning, parsing, and reporting:
/nmap-wrapper
scanner.py -- main entry point
profiles.py -- predefined scan profiles
parser.py -- XML output parser
reporter.py -- HTML and CSV report generator
vulns.py -- known vulnerable version database
templates/
report.html -- Jinja2 HTML report template
requirements.txt
Keeping parsing and reporting separate from the core scanner makes it easy to add new output formats or scan profiles without touching the main logic.
Common scan types were defined as named profiles so the user does not need to remember Nmap flags for each scenario:
# profiles.py
PROFILES = {
"quick": "-T4 -F --open",
"full": "-T4 -p- --open --min-rate 3000",
"stealth": "-sS -T2 -p- --open",
"version": "-sV -sC -p- --open -T4",
"udp": "-sU -T4 --top-ports 200",
"vuln": "-sV --script vuln -T4"
}
Profiles are selected at runtime via the --profile
argument — removing the need to remember flag combinations
for every different scan scenario.
The scanner uses the python-nmap library to execute
scans programmatically and capture structured output:
# scanner.py
import nmap
import argparse
from profiles import PROFILES
from parser import parse_results
from reporter import generate_report
def run_scan(target, profile, output):
nm = nmap.PortScanner()
flags = PROFILES.get(profile, PROFILES["version"])
print(f"[*] Scanning {target} with profile: {profile}")
print(f"[*] Nmap flags: {flags}")
nm.scan(hosts=target, arguments=flags)
results = parse_results(nm)
generate_report(results, output)
return results
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-t", "--target", required=True)
parser.add_argument("-p", "--profile", default="version")
parser.add_argument("-o", "--output", default="report")
args = parser.parse_args()
run_scan(args.target, args.profile, args.output)
Running the tool from the command line:
# Quick scan
python3 scanner.py -t 10.10.10.40 -p quick
# Full version scan with report output
python3 scanner.py -t 10.10.10.0/24 \
-p version -o lab_scan
# Vulnerability script scan
python3 scanner.py -t 192.168.1.10 -p vuln
The parser extracts host, port, service, version, and state data from the python-nmap result object into a clean structured dictionary for downstream processing:
# parser.py
def parse_results(nm):
hosts = []
for host in nm.all_hosts():
host_data = {
"ip": host,
"hostname": nm[host].hostname(),
"state": nm[host].state(),
"ports": []
}
for proto in nm[host].all_protocols():
for port in nm[host][proto].keys():
svc = nm[host][proto][port]
host_data["ports"].append({
"port": port,
"proto": proto,
"state": svc["state"],
"service": svc["name"],
"version": svc.get("version",""),
"product": svc.get("product",""),
"vuln": check_vuln(svc)
})
hosts.append(host_data)
return hosts
A lightweight local database of known vulnerable service versions is checked against each discovered service. Matches are flagged directly in the scan results:
# vulns.py
KNOWN_VULNS = {
"vsftpd 2.3.4": "CVE-2011-2523 — Backdoor RCE",
"ProFTPD 1.3.3c": "CVE-2010-4221 — Remote Buffer Overflow",
"Samba 3.0.20": "CVE-2007-2447 — Username Map RCE",
"OpenSSH 7.2p2": "CVE-2016-6515 — DoS / Auth Bypass",
"Apache 2.4.49": "CVE-2021-41773 — Path Traversal RCE",
"Apache 2.4.50": "CVE-2021-42013 — Path Traversal RCE",
"IIS 6.0": "CVE-2017-7269 — Buffer Overflow RCE",
"Elastix 2.2.0": "CVE-2012-4869 — LFI / RCE"
}
def check_vuln(svc):
key = f"{svc.get('product','')} \
{svc.get('version','')}".strip()
for vuln_key, cve in KNOWN_VULNS.items():
if vuln_key.lower() in key.lower():
return cve
return None
Flagged vulnerabilities are highlighted in red in the
HTML report and marked with a VULN column
in the CSV export for quick triage.
Scan results are exported as a formatted HTML report using Jinja2 templates and a flat CSV for import into spreadsheets or vulnerability trackers:
# reporter.py
from jinja2 import Environment, FileSystemLoader
import csv, datetime
def generate_report(results, output):
# HTML report
env = Environment(loader=FileSystemLoader("templates"))
tmpl = env.get_template("report.html")
html = tmpl.render(
results=results,
generated=datetime.datetime.now()
)
with open(f"{output}.html", "w") as f:
f.write(html)
# CSV export
with open(f"{output}.csv","w",newline="") as f:
w = csv.writer(f)
w.writerow(["IP","Port","Proto",
"Service","Version","Vuln"])
for host in results:
for p in host["ports"]:
w.writerow([
host["ip"], p["port"],
p["proto"], p["service"],
p["version"], p["vuln"] or ""
])
The HTML report uses colour coding — open ports in green, vulnerable services highlighted in red, and a summary table at the top showing total hosts, open ports, and vulnerability count at a glance.
The tool supports single IPs, CIDR ranges, and hyphenated ranges — passed directly to Nmap:
# Single host
python3 scanner.py -t 10.10.10.40 -p version
# CIDR subnet scan
python3 scanner.py -t 192.168.1.0/24 -p quick
# Hyphenated range
python3 scanner.py -t 10.10.10.1-50 -p full
# Multiple targets from file
python3 scanner.py -t targets.txt -p version
Target file support reads one IP or range per line and passes them to Nmap as a space-separated list — allowing large scope engagements to be managed from a single target file prepared during scoping.
The tool is installable in any Python 3 environment with a single pip command:
# Clone the repository
git clone https://github.com/user/nmap-wrapper
cd nmap-wrapper
# Install dependencies
pip3 install -r requirements.txt
# requirements.txt
python-nmap==0.7.1
Jinja2==3.1.2
argparse
# Verify Nmap is installed
nmap --version
# Run your first scan
python3 scanner.py -t 10.10.10.3 -p version -o lame_scan
# Output: lame_scan.html and lame_scan.csv
Python 3 — core language for the wrapper toolpython-nmap — Nmap Python bindings for programmatic scanningNmap — underlying scan engineJinja2 — HTML report templatingargparse — CLI argument parsingcsv — flat file export for spreadsheet importVS Code — development environment