Nmap is the first tool any network security professional learns. But running Nmap manually and reading raw output is slow. This project automates the entire workflow: scan a target range, parse the XML output, flag interesting findings, and generate a clean report — all in Python.
Legal reminder: Only run Nmap against systems you own or have explicit written permission to test. Running this against external targets without authorisation is illegal under the Australian Criminal Code Act 1995.
Why Automate Nmap?
In a real penetration test or vulnerability assessment, you might scan hundreds of hosts. Automating the scan and report means:
- Consistent output format every time
- Automatic flagging of high-risk ports (22, 23, 3389, 445)
- Easy integration with ticketing systems or SIEM
- A deliverable you can show clients or hiring managers
Prerequisites
- Nmap installed:
sudo apt install nmap(Linux) or from nmap.org (Windows) - Python 3.8+
- python-nmap library:
pip install python-nmap
The Scanner Script
import nmap
import json
import datetime
RISKY_PORTS = {
21: "FTP - plaintext, often misconfigured",
22: "SSH - check for weak credentials",
23: "Telnet - plaintext, should not be open",
80: "HTTP - check for missing HTTPS redirect",
443: "HTTPS",
445: "SMB - check for EternalBlue (MS17-010)",
3389: "RDP - high-value target for brute force",
3306: "MySQL - should not be internet-exposed",
5900: "VNC - often no auth by default",
}
def scan_target(target, ports="1-1024"):
nm = nmap.PortScanner()
print(f"[*] Scanning {target} ...")
nm.scan(hosts=target, ports=ports, arguments="-sV -O --open")
results = []
for host in nm.all_hosts():
host_data = {
"ip": host,
"hostname": nm[host].hostname(),
"state": nm[host].state(),
"os_guess": "",
"open_ports": [],
"risk_flags": []
}
# OS detection
if "osmatch" in nm[host] and nm[host]["osmatch"]:
host_data["os_guess"] = nm[host]["osmatch"][0]["name"]
# Port enumeration
for proto in nm[host].all_protocols():
for port in nm[host][proto]:
svc = nm[host][proto][port]
port_info = {
"port": port,
"protocol": proto,
"state": svc["state"],
"service": svc["name"],
"version": svc.get("version", "")
}
host_data["open_ports"].append(port_info)
if port in RISKY_PORTS:
host_data["risk_flags"].append(
f"Port {port} ({RISKY_PORTS[port]})"
)
results.append(host_data)
return results
def generate_report(results, output_file="scan_report.json"):
report = {
"scan_date": datetime.datetime.utcnow().isoformat(),
"hosts_scanned": len(results),
"total_risk_flags": sum(len(h["risk_flags"]) for h in results),
"hosts": results
}
with open(output_file, "w") as f:
json.dump(report, f, indent=2)
print(f"[+] Report saved to {output_file}")
return report
if __name__ == "__main__":
target = "192.168.1.0/24" # Your lab network
results = scan_target(target)
report = generate_report(results)
print(f"\n{'='*50}")
print(f"Scan complete — {report['hosts_scanned']} hosts")
print(f"Risk flags: {report['total_risk_flags']}")
for host in report["hosts"]:
if host["risk_flags"]:
print(f"\n[!] {host['ip']} ({host['hostname']})")
for flag in host["risk_flags"]:
print(f" - {flag}")
What the Output Looks Like
Running this against my VirtualBox lab network (192.168.56.0/24) produced output like:
[*] Scanning 192.168.56.0/24 ...
[+] Report saved to scan_report.json
==================================================
Scan complete — 4 hosts
Risk flags: 7
[!] 192.168.56.101 (metasploitable)
- Port 21 (FTP - plaintext, often misconfigured)
- Port 23 (Telnet - plaintext, should not be open)
- Port 445 (SMB - check for EternalBlue)
- Port 3306 (MySQL - should not be internet-exposed)
Extending the Scanner
Add HTML Report Output
Replace the JSON output with a Jinja2 template to generate a styled HTML report — much more impressive to show clients or include in a portfolio write-up.
CVSS Scoring Integration
Cross-reference open ports against a local CVE database (NVD provides daily XML dumps) and append known vulnerabilities to each finding automatically.
Scheduled Scanning
Wrap the scanner in a cron job (Linux) or Task Scheduler (Windows) to run nightly and email you a diff of changes — new open ports, new hosts, closed services.
Why This Project Matters for Interviews
This project demonstrates three things that entry-level cybersecurity employers want to see: Python scripting ability, understanding of network reconnaissance, and awareness of why certain ports are risky. The GitHub repo for this project has become one of the most-discussed items in my interviews.