Old Kit, New Kit Part 2: Port and Service Discovery — Rustscan vs Nmap
This is the first tool-pair post in a new series, testing classic, well-documented Kali tooling against its quieter modern replacements, with a genuine look at what a lab Wazuh deployment sees from each. The series’ own intro, covering the lab build in full, is Part 1. First pair: nmap against rustscan, both asked to do the same real job, find every open port and identify the service behind it, against two live targets on an isolated lab network.
How this was verified
Both tools were run for real against target-metasploitable2 (a deliberately vulnerable legacy Linux box, ~30 open services) and target-web (a minimal, current Debian host running just SSH and Apache), on an isolated lab bridge with a dedicated Wazuh manager watching target-web as an enrolled agent. Every timing number below is a real time measurement from an actual run, not an estimate from documentation.
The classic baseline: nmap
A full-range, version-detection sweep is the realistic classic baseline, not just a bare port list:
$ time nmap -sV -p- -T4 10.10.10.20 -oN nmap-metasploitable2.txt
Starting Nmap 7.99 ( https://nmap.org ) at 2026-08-29 16:20 +0100
Nmap scan report for 10.10.10.20
Host is up (0.00033s latency).
Not shown: 65505 closed tcp ports (reset)
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 2.3.4
22/tcp open ssh OpenSSH 4.7p1 Debian 8ubuntu1 (protocol 2.0)
23/tcp open telnet Linux telnetd
25/tcp open smtp Postfix smtpd
53/tcp open domain ISC BIND 9.4.2
80/tcp open http Apache httpd 2.2.8 ((Ubuntu) DAV/2)
111/tcp open rpcbind 2 (RPC #100000)
139/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
445/tcp open netbios-ssn Samba smbd 3.X - 4.X (workgroup: WORKGROUP)
...
8180/tcp open http Apache Tomcat/Coyote JSP engine 1.1
8787/tcp open drb Ruby DRb RMI (Ruby 1.8; path /usr/lib/ruby/1.8/drb)
Nmap done: 1 IP address (1 host up) scanned in 128.98 seconds
real 129.03s
29 open ports, every service and version identified, in 129.03 seconds. Against the much smaller target-web, the same command took 7.72 seconds and found exactly two open ports: SSH and Apache.
Installing the modern tool turned into its own small lesson
Kali’s own repos don’t carry rustscan, and the obvious fix, pulling a .deb straight from GitHub, failed on the first two attempts. The project’s GitHub org changed at some point (RustScan/RustScan now 404s and redirects to a bee-san/RustScan fork), and the current release doesn’t ship a plain .deb either, it’s zipped:
$ wget https://github.com/bee-san/RustScan/releases/download/2.4.1/rustscan.deb.zip
unzip rustscan.deb.zip
sudo dpkg -i rustscan*.deb
Archive: rustscan.deb.zip
inflating: rustscan.tmp0-stripped
inflating: rustscan_2.4.1-1_amd64.deb
Setting up rustscan (2.4.1-1)...
$ rustscan --version
rustscan 2.4.1
Small, but worth stating plainly given this whole series is about weighing classic tools against modern ones: “modern” also meant a changed upstream org, a release page that doesn’t render its own asset list in plain HTML, and a packaging format that changed between versions. Reliability isn’t only a runtime property.
The modern tool: rustscan
Rustscan’s real selling point is scanning all 65,535 ports with an async engine, then handing the discovered ports to nmap for service detection, so the fair comparison is rustscan -a <target> -- -sV, not a bare port sweep nobody would actually stop at:
$ time rustscan -a 10.10.10.20 -- -sV -oN rustscan-metasploitable2.txt
Open 10.10.10.20:21
Open 10.10.10.20:22
...
Open 10.10.10.20:56628
[~] Starting Script(s)
[>] Running script "nmap -vvv -p {{port}} -{{ipversion}} {{ip}} -sV -oN rustscan-metasploitable2.txt" on ip 10.10.10.20
Initiating SYN Stealth Scan at 16:45
Completed SYN Stealth Scan at 16:45, 0.01s elapsed (30 total ports)
Initiating Service scan at 16:45
Completed Service scan at 16:47, 126.16s elapsed (30 services on 1 host)
Nmap done: 1 IP address (1 host up) scanned in 127.38 seconds
real 132.33s
Rustscan’s own port sweep really is close to instant, that “0.01s elapsed” line and the full open-port list appear before nmap even starts. But the moment -sV is requested, it hands straight off to nmap for the actual service identification, and that phase alone took 126.16 seconds. Total wall clock: 132.33 seconds, against nmap’s 129.03 seconds run alone. Rustscan didn’t win. Against target-web’s two ports, the gap goes the other way more sharply:
$ time rustscan -a 10.10.10.30 -- -sV -oN rustscan-target-web.txt
Open 10.10.10.30:22
Open 10.10.10.30:80
Completed Service scan at 16:50, 6.03s elapsed (2 services on 1 host)
Nmap done: 1 IP address (1 host up) scanned in 6.88 seconds
real 11.14s
11.14 seconds against nmap’s 7.72. On a small target, rustscan’s fixed overhead, loading its own config, spawning nmap as a subprocess, that subprocess doing its own DNS and NSE warm-up, costs more than its async port sweep saves, because there’s almost nothing for the sweep to save time on.
| Ports found | nmap alone | rustscan + nmap handoff | |
|---|---|---|---|
| target-metasploitable2 | 29 | 129.03s | 132.33s |
| target-web | 2 | 7.72s | 11.14s |
The documented speed advantage is real, it’s just narrower than the marketing suggests: rustscan is genuinely fast at the raw port-discovery step in isolation. The instant a real recon workflow asks for service versions, which almost every real one does, that advantage gets absorbed by the same nmap service-detection engine running underneath either tool, and rustscan’s own startup overhead can put it behind plain nmap outright on a small target.
What ends up in the log either way
target-web is the one target in this pair with a Wazuh agent, so it’s also the one worth checking for an actual footprint. Both scans’ -sV phase left the identical signature in Apache’s access log:
10.10.10.5 - - [29/Aug/2026:16:49:55 +0100] "GET / HTTP/1.0" 200 767 "-" "-"
10.10.10.5 - - [29/Aug/2026:16:49:55 +0100] "GET /nmaplowercheck1788018595 HTTP/1.1" 404 558 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
10.10.10.5 - - [29/Aug/2026:16:49:55 +0100] "POST /sdk HTTP/1.1" 404 558 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
10.10.10.5 - - [29/Aug/2026:16:49:55 +0100] "GET /evox/about HTTP/1.1" 404 558 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
10.10.10.5 - - [29/Aug/2026:16:49:55 +0100] "GET /HNAP1 HTTP/1.1" 404 558 "-" "Mozilla/5.0 (compatible; Nmap Scripting Engine; https://nmap.org/book/nse.html)"
The literal nmaplowercheck<timestamp> probe path and the Nmap Scripting Engine user-agent string showed up identically whether nmap ran on its own or rustscan called it. That’s the honest answer to “does the modern tool look quieter here”: no, not once service detection is involved, because it’s the same NSE engine doing the probing underneath either way. The only way to actually be quieter with rustscan is to stop at its raw port list and skip -sV entirely, which means trading away exactly the service-version data most recon actually needs.
And despite that obvious a log signature, checking the Wazuh manager’s alert log for anything tied to either scan came back empty:
$ ssh -i ~/.ssh/wazuh-lab michealg@10.10.10.10 "sudo grep -A2 'target-web' /var/ossec/logs/alerts/alerts.log | tail -60"
2026 Aug 29 15:52:04 (target-web) any->journald
Rule: 5715 (level 3) -> 'sshd: authentication success.'
Src IP: 10.10.10.5
...
Rule: 5402 (level 3) -> 'Successful sudo to ROOT executed.'
Every alert in that log came from checking the log, our own SSH login and sudo tail command, not from either scan. Zero alerts fired for the actual port scans, not weak signal, none. Wazuh’s default web-log ruleset doesn’t watch for a burst of 404s or an anomalous user-agent string out of the box, so this pairing really is what it looked like going in: a NIDS or packet-capture question, not something a host agent’s default configuration is built to catch. A defender relying on this Wazuh deployment as configured wouldn’t see either tool coming.
Verdict
Nmap remains the more dependable choice for a recon workflow that actually wants service versions, both scans it ran matched or beat rustscan’s total time, and it didn’t need a same-day workaround just to get installed. Rustscan’s real advantage, a genuinely fast async port sweep, is worth having as its own separate step when a bare port list is all that’s needed, or when feeding a very large target range into something else. Paired with -sV, the two tools converge on the same total cost and the exact same log fingerprint, because underneath the modern wrapper, the actual service identification is the same classic tool either way. Detection-wise, neither one showed up in this lab’s default Wazuh configuration at all, confirming this pair sits outside host-based detection’s default reach regardless of which tool does the scanning.