Nmap

Nmap Version Detection (-sV): Banner Grabbing and Service Fingerprinting

Nmap version detection - terminal showing -sV naming Cisco SSH, IOS telnetd, and nginx 1.29.8
Table of Contents
In: Nmap, Network Security, Cisco Modeling Labs

A port number tells you almost nothing on its own. "Port 80 open" could be nginx, Apache, an embedded management interface, or a printer's web UI. "Port 22 open" could be OpenSSH, a Cisco IOS SSH stack, or a locked-down appliance. Nmap's service and version detection (-sV) is the flag that turns an open port into an actionable fact: nginx 1.29.8, Cisco SSH 1.25, OpenSSH 10.0p2 Debian. That single upgrade in fidelity is what separates a port list from an inventory, and it feeds nearly everything else you do with the tool. This guide is part of the complete Nmap guide, and it focuses on what -sV actually sends, how to read what comes back, and how to tune it when it guesses wrong.

What -sV actually does

A plain TCP scan learns one thing about a port: is it accepting connections. Service detection goes a step further. Once Nmap knows a port is open, it opens a real connection and runs the port through its probe database (nmap-service-probes). Some services announce themselves the moment you connect - an FTP daemon greets you with a banner, an SSH server sends its protocol string - so Nmap reads that first. For services that stay quiet, Nmap sends a sequence of carefully crafted probe strings and matches the reply against thousands of signature patterns until something fits.

The result is the VERSION column you see next to each open port. It is not magic and it is not always exact, but on well-known services it is remarkably good. Here is a live run from the PingLabz Nmap lab, scanning a Cisco router, an nginx web server, and a Linux host in one pass:

$ nmap -sV -T4 10.10.10.1 10.10.10.21 10.10.10.24
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-28 05:52 UTC
Nmap scan report for 10.10.10.1
Host is up (0.013s latency).
Not shown: 998 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     Cisco SSH 1.25 (protocol 2.0)
23/tcp open  telnet  Cisco IOS telnetd
MAC Address: AA:BB:CC:00:5B:00 (Unknown)
Service Info: OS: IOS; Device: switch; CPE: cpe:/o:cisco:ios

Nmap scan report for 10.10.10.21
Host is up (0.0051s latency).
Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
80/tcp open  http    nginx 1.29.8
MAC Address: 52:54:00:A2:2F:84 (QEMU virtual NIC)

Nmap scan report for 10.10.10.24
Host is up (0.0068s latency).
Not shown: 999 closed tcp ports (reset)
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 10.2 (protocol 2.0)
MAC Address: 52:54:00:BB:DC:36 (QEMU virtual NIC)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 3 IP addresses (3 hosts up) scanned in 10.51 seconds

Look at what those three lines of output give you. The router at .1 is running Cisco SSH 1.25 and Cisco IOS telnetd, and Nmap has inferred from those responses that the OS is IOS and the device is a switch. The web server at .21 is running nginx 1.29.8 - not just "a web server," the exact build. The host at .24 is running OpenSSH 10.2. None of that was in the port list before you added -sV.

Turning ports into services

The same flag behaves identically on a real Linux box. This capture is from a Debian VM running the usual trio of daemons:

$ nmap -sV -p21,22,80 127.0.0.1
Starting Nmap 7.95 ( https://nmap.org ) at 2026-07-05 15:05 PDT
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00010s latency).

PORT   STATE SERVICE VERSION
21/tcp open  ftp     vsftpd 3.0.5
22/tcp open  ssh     OpenSSH 10.0p2 Debian 7+deb13u4 (protocol 2.0)
80/tcp open  http    nginx
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel

Notice the range of detail here. On FTP, Nmap pulls the full product and version: vsftpd 3.0.5. On SSH it goes further and reads the distribution packaging string: OpenSSH 10.0p2 Debian 7+deb13u4, which tells you not only the OpenSSH release but that it is the Debian-packaged build with a specific patch revision. On HTTP, though, it reports plain nginx with no version. That is not a bug: this nginx was configured to suppress its version token, so Nmap correctly reports the service without inventing a number. Compare that with the lab's WEB01, which happily advertised nginx 1.29.8. Whether you get a version often comes down to how the admin (maybe you) configured the banner.

Why the exact version matters

"nginx" is trivia. "nginx 1.29.8" is a lead. Once you have an exact product and version, you can cross-reference it against published advisories and your own patch baseline. An auditor uses that to answer "is anything on this segment running a build with a known CVE?" A network engineer uses the same output to answer a quieter but just as useful question: "did the config management actually roll out the version I think it did?" If your standard is nginx 1.29.x across the fleet and a host reports 1.24, you have found configuration drift with a single scan.

This is the network-engineer case for -sV that gets lost in the security framing. You are not always hunting an intruder. Often you are verifying your own environment: confirming a firmware upgrade landed, confirming a decommissioned service is actually gone, confirming that the box you think is a jump host is running the SSH build your policy requires. The Service Info line - OS: IOS; Device: switch on the router, OSs: Unix, Linux on the Debian host - is a bonus inference Nmap draws from the same responses, and it is the bridge into full OS detection with -O.

Controlling version intensity

Service detection is a trade-off between how many probes Nmap sends and how long the scan takes. The --version-intensity knob (0 to 9) sets how many probes from the database Nmap is willing to try. Higher intensity means more probes, better odds of identifying an obscure or deliberately quiet service, and a slower scan. Lower intensity keeps things fast at the cost of coverage. There are two named shortcuts for the extremes.

--version-intensity 0-9
Sets how many probes Nmap tries against each open port.
Default is 7. Higher means more probes, more accuracy, more time.
--version-light
Alias for intensity 2.
Fast, only the most common probes. Good for wide sweeps where speed wins.
--version-all
Alias for intensity 9.
Every probe, no shortcuts. Use when a service refuses to identify at lower intensity.

In practice, the default of 7 identifies the vast majority of services on a normal network. Reach for --version-light when you are scanning a large range and just want a fast pass. Reach for --version-all when a specific port stubbornly comes back as tcpwrapped or unidentified and you want to throw everything at it.

Version detection's polite cousin is banner grabbing. Many services volunteer a text banner the instant you connect, and the banner NSE script simply captures and prints it verbatim. Pairing it with -sV gives you both the parsed version and the raw greeting, which is often more revealing than the tidy summary. Here is the lab's Cisco router:

$ nmap -sV --script banner -p 22,23 10.10.10.1
Starting Nmap 7.95 ( https://nmap.org ) at 2026-06-28 07:03 UTC
Nmap scan report for 10.10.10.1
Host is up (0.0035s latency).

PORT   STATE SERVICE VERSION
22/tcp open  ssh     Cisco SSH 1.25 (protocol 2.0)
|_banner: SSH-2.0-Cisco-1.25
23/tcp open  telnet  Cisco IOS telnetd
| banner: \xFF\xFB\x01\xFF\xFB\x03\xFF\xFD\x18\xFF\xFD\x1F\x0D\x0A\x0D\x0
|_AUser Access Verification\x0D\x0A\x0D\x0AUsername:
MAC Address: AA:BB:CC:00:5B:00 (Unknown)
Service Info: OS: IOS; Device: switch; CPE: cpe:/o:cisco:ios
Nmap done: 1 IP address (1 host up) scanned in 0.88 seconds

On SSH the banner is the clean protocol string SSH-2.0-Cisco-1.25, which is exactly where the "Cisco SSH 1.25" version came from. On telnet the banner is far chattier. Those \xFF\xFB and \xFF\xFD sequences are the telnet IAC (interpret-as-command) negotiation bytes the protocol exchanges before anything human-readable. Read past them and the router leaks its own login prompt: User Access Verification / Username:. That is the standard Cisco IOS login banner, captured straight off the wire, confirming both the platform and that telnet is live and unauthenticated at the greeting.

The same technique on the Debian host pulls equally clean strings, plus SSH algorithm enumeration when you add the right scripts:

$ nmap --script ssh2-enum-algos,ssh-hostkey,banner -p21,22 127.0.0.1
...
PORT   STATE SERVICE
21/tcp open  ftp
|_banner: 220 (vsFTPd 3.0.5)
22/tcp open  ssh
|_banner: SSH-2.0-OpenSSH_10.0p2 Debian-7+deb13u4

The FTP banner 220 (vsFTPd 3.0.5) is the daemon's own greeting - the same string that let -sV report vsftpd 3.0.5. The SSH banner SSH-2.0-OpenSSH_10.0p2 Debian-7+deb13u4 carries the full Debian packaging detail. If you want to go deeper on SSH specifically - key exchange algorithms, host keys, cipher suites - the NSE scripting engine has purpose-built scripts like ssh2-enum-algos, which is how you audit whether a box still negotiates weak algorithms.

CPE strings and when detection is wrong

You will have noticed the CPE: tokens in the output - cpe:/o:cisco:ios on the router, cpe:/o:linux:linux_kernel on the Linux hosts. CPE (Common Platform Enumeration) is a standardized naming scheme for operating systems, hardware, and applications. Those strings are machine-readable identifiers you can feed straight into vulnerability tooling or asset databases, which is why they matter more in an audit pipeline than to a human reading the terminal.

Every service-detection run ends with the same line: Service detection performed. Please report any incorrect results at https://nmap.org/submit/. That is not boilerplate to ignore. It is Nmap reminding you that version detection is a best-effort match against a signature database, and databases have gaps. A deliberately altered banner, a rare build, or a custom appliance can all produce a wrong or missing version. When the result looks off, the honest move is to grab the raw banner (as above) and read it yourself rather than trusting the parsed summary, and if you have confirmed a genuine miss, submitting it back improves the database for everyone.

How -sV feeds -O and -A

Service detection rarely runs alone. It is the middle piece of a larger fingerprinting picture. The Service Info: OS: IOS line you saw on the router is already an OS hint drawn purely from application banners, and it dovetails with TCP/IP stack fingerprinting, which is a different technique entirely - see the full walkthrough of OS detection for how -O works and why the two together are stronger than either alone. When you want all of it in one command, -A bundles version detection, OS detection, default NSE scripts, and traceroute. It is the "tell me everything" switch, and -sV is one of its core ingredients.

A sensible progression is to start with a fast port scan to find what is open, add -sV to learn what those ports are actually running, then layer on -O and selected scripts once you know where to dig. Nmap's own reference guide documents every intensity level and probe-database detail if you want to go under the hood.

Key takeaways

Service and version detection is the flag that makes an Nmap scan worth reading. It turns "22 open" into "Cisco SSH 1.25" and "80 open" into "nginx 1.29.8," and that exactness is what feeds CVE lookups, drift checks, and inventory alike. Tune --version-intensity when you need speed or coverage, add --script banner to read the raw greeting when the parsed version looks wrong, and remember that a missing version usually means a hardened banner rather than a failed scan. Trust the output, but verify the surprising results against the raw banner, and let -sV hand off cleanly into OS detection and full -A profiling. For the whole picture - discovery, scanning, fingerprinting, and scripting in one place - work through the complete Nmap guide.

Written by
More from Ping Labz
Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to Ping Labz.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.