Nmap

Nmap turns an unknown host into a clear picture: which systems are up, which TCP and UDP ports are open, what software and versions sit behind them, and what safe scripts can tell you before you touch anything by hand. Reach for it first on almost any box, because nearly every next step depends on knowing what is actually listening.

Mental model: nmap [scan type] [options] TARGET. Everything else is one flag away.

New to Nmap? Core concepts
  • Host discovery: find which hosts answer before scanning ports (-sn ping sweep, -Pn skips discovery entirely)
  • Port state: open, closed, filtered, or open|filtered - the core of every report
  • Scan type: how packets go out - -sS SYN (needs root), -sT full connect, -sU UDP, -sA ACK
  • Service/version detection (-sV): probe an open port to name the software and version
  • NSE: the Nmap Scripting Engine - -sC runs the safe default set, --script NAME picks specific ones
  • Timing template (-T0 to -T5): trade stealth for speed; -T4 is a sane lab default
  • Ports: -p 80,443 a list, -p- all 65535, --top-ports N the most common
  • Output: -oN normal, -oG greppable, -oX XML, -oA writes all three at once
When to reach for Nmap (and when not)

Reach for it as the opening move on a host or subnet: enumerate open ports, name the services and versions behind them, run safe scripts for quick context, confirm a firewall change really closed a port, or check whether a service is reachable from another host.

Reach for something else when you already know the port and just need to talk to one service (curl, nc, smbclient), when you need to sweep huge ranges fast (masscan, rustscan, then hand the port list back to Nmap), or when you need a real vulnerability assessment (a dedicated scanner, since Nmap only hints at issues).

Install, update, verify#

sudo apt install -y nmap                   # Debian / Ubuntu / Kali
sudo dnf install -y nmap                   # Fedora / RHEL
brew install nmap                          # macOS
sudo apt install --only-upgrade -y nmap    # update
sudo nmap --script-updatedb                # refresh the NSE script database

nmap --version        # version + build (OpenSSL, libpcap, NSE support)
command -v nmap       # confirm it is on PATH
nmap --iflist         # interfaces and routes Nmap can see
nmap 127.0.0.1        # self-test: scans loopback, touches nothing external

On Windows, install the official package from nmap.org; it bundles the Npcap driver needed for raw-packet scans like -sS, -sU, and -O. Prefer the package manager on Linux and macOS so dependencies and NSE scripts update together.

Flags you’ll actually use#

Skim these first; the cheat sheet below is these flags combined.

FlagDoesFlagDoes
-snPing sweep, no port scan-sVDetect service and version
-PnSkip discovery, assume up-sCRun default safe scripts
-sSSYN scan (needs root)--scriptRun specific NSE scripts
-sTTCP connect scan (no root)-OOS detection (needs root)
-sUUDP scan (needs root)-AAggressive: -sV -sC -O --traceroute
-pPorts: -p 80,443, -p- = all-T4Faster timing (lab default)
--top-portsThe N most common ports--min-rateFloor on packets per second
-FFast: top 100 ports--reasonWhy a port is in its state
-nNo DNS resolution (faster)--openShow only open ports
-oASave normal + grep + XML-iLRead targets from a file

Cheat sheet#

Where it helps, a block starts simple and adds one flag at a time, so you can stop at the line that does the job.

# Build up a scan: stop at the line you need
nmap TARGET_IP                            # 1. default: top 1000 TCP ports
nmap -sV TARGET_IP                        # 2. + service/version detection
nmap -sC -sV TARGET_IP                    # 3. + default safe scripts
nmap -A TARGET_IP                         # 4. + OS detect + traceroute (loud)

# Host discovery: who is alive
nmap -sn TARGET_RANGE                     # ping sweep, no port scan
nmap -sn -PS22,80,443 TARGET_RANGE        # TCP SYN ping to specific ports
nmap -Pn TARGET_IP                        # skip discovery, assume the host is up

# Choose ports
nmap -p 22,80,443 TARGET_IP               # a specific list
nmap -p 1-1000 TARGET_IP                  # a range
nmap -p- TARGET_IP                        # all 65535 TCP ports
nmap --top-ports 100 TARGET_IP            # the 100 most common
nmap -F TARGET_IP                         # fast mode: top 100

# Scan types
sudo nmap -sS TARGET_IP                   # SYN (default when root)
nmap -sT TARGET_IP                        # TCP connect (no root needed)
sudo nmap -sU --top-ports 20 TARGET_IP    # UDP, top 20 ports
sudo nmap -sS -sU TARGET_IP               # TCP and UDP in one run

# Fingerprint services and OS
nmap -sV TARGET_IP                        # name the software + version
nmap -sV --version-intensity 9 TARGET_IP  # probe harder for a version
sudo nmap -O TARGET_IP                    # guess the operating system

# NSE scripts
nmap -sC TARGET_IP                        # default safe set
nmap --script http-title,http-headers -p 80,443 TARGET_IP
nmap --script vuln -p 80,443 TARGET_IP    # known-vuln checks (loud, verify)
nmap --script "smb-enum-*" -p 445 TARGET_IP   # every matching SMB script

# Speed and stealth
nmap -T4 TARGET_IP                        # faster timing (healthy lab net)
nmap -p- --min-rate 5000 TARGET_IP        # push the packet rate on a full sweep
nmap -T2 TARGET_IP                        # quieter, gentler on fragile targets

# Output and notes
nmap -oA scans/box TARGET_IP              # normal + greppable + XML at once
nmap -oN scan.txt TARGET_IP               # one human-readable file
nmap -v --reason --open TARGET_IP         # verbose, why each state, open only
nmap -p- TARGET_IP -oG - | grep '/open/'  # sweep, then pull the open ports inline

Command breakdowns#

Each block below explains one situation, the exact command, and what to expect back.

Discover live hosts on a subnet#

nmap -sn TARGET_RANGE -oA scans/hosts

-sn sends discovery probes but no port scan, so you get a fast list of “Host is up” lines. Feed those IPs into everything below.

Do a fast first-pass triage scan#

nmap -T4 --top-ports 100 TARGET_IP -oA scans/fast

The 100 most common ports at brisk timing. Triages a box in seconds so you know where to look before committing to a full sweep.

Scan every TCP port#

sudo nmap -p- --min-rate 5000 -T4 TARGET_IP -oA scans/all-tcp

-p- covers all 65535 ports and --min-rate floors the packet rate so it actually finishes. This is the scan that catches a service hiding on a high port.

Fingerprint service versions#

nmap -sV -p 22,80,445 TARGET_IP -oA scans/versions

Probes each open port and names the software and version, for example OpenSSH 8.2p1 or Apache httpd 2.4.41. Run it on just the open ports from the sweep, never all 65535.

Run the default safe scripts for quick context#

nmap -sC -sV TARGET_IP -oA scans/focused

-sC runs the default NSE category (banner grabs, page titles, safe enumeration) alongside version detection. The fastest way to add context without picking scripts by hand.

Scan UDP services#

sudo nmap -sU --top-ports 20 TARGET_IP -oA scans/udp

UDP is slow and needs root, so keep it to the top ports. DNS (53), SNMP (161), and TFTP (69) are the usual finds. Treat open|filtered as “check this by hand”.

Scan when ping is blocked#

nmap -Pn TARGET_IP -oA scans/no-ping

-Pn skips host discovery and treats the target as up. Use it when the host is really there but ICMP is filtered and a normal scan reports “host seems down”.

Detect the operating system#

sudo nmap -O TARGET_IP

Fingerprints the TCP/IP stack to guess the OS and version. Needs root and works best with at least one open and one closed port; treat the result as a lead, not a fact.

Run vulnerability-check scripts#

nmap --script vuln -p 80,443 TARGET_IP -oA scans/vuln

Runs the vuln NSE category against the given ports. It is loud and slower, and it reports possibilities to confirm, not confirmed findings.

Enumerate a specific service with NSE#

nmap --script "smb-enum-shares,smb-os-discovery" -p 445 TARGET_IP
nmap --script http-title,http-headers -p 80,443 TARGET_IP

Pick scripts by service to go deeper than -sC. Wildcards work too: --script "smb-*" runs every SMB script at once.

Scan many targets from a file#

nmap -sV -iL targets.txt -oA scans/multi

-iL reads one IP or hostname per line, all in scope. Handy after a -sn sweep: save the live hosts to a file, then feed them straight back in.

Save output in every format at once#

nmap -sC -sV TARGET_IP -oA scans/deep

-oA writes deep.nmap (readable), deep.gnmap (greppable), and deep.xml (for tooling) in one shot. Do this every time so you never re-run a noisy scan just to recover lost output.

See why a port is in its state#

nmap --reason --open -v TARGET_IP

--reason shows the packet that decided each state (syn-ack, reset, no-response); --open hides closed and filtered noise; -v streams results as they land.

Pull just the open ports from a sweep#

nmap -p- TARGET_IP -oG - | grep '/open/'

-oG - writes greppable output to stdout and the grep leaves you a one-line port list, ready to paste into a focused -sV scan.

Reading the output#

Port states, in order of how often they matter:

  • open - something is listening. These are your action items.
  • filtered - a firewall or filter is dropping probes; you cannot tell from here. Try -Pn and other scan types.
  • open|filtered - common on UDP; the probe got no reply either way. Investigate by hand.
  • closed - the host answered but nothing is on that port. Usually not interesting.
  • unfiltered - reachable but the state is undetermined (seen with ACK scans).

The SERVICE and VERSION columns are strong clues, not proof, so verify anything you act on. The closing Nmap done: 1 IP address (1 host up) line confirms the scan actually reached the target and ran.

Troubleshooting#

ProblemCauseFix
command not foundNot installed / stale PATHReinstall; open a fresh shell
-sS/-sU/-O refuse to runRaw packets need privilegesPrefix sudo, or use -sT for TCP
“Host seems down … try -Pn”ICMP / discovery blockedAdd -Pn to skip host discovery
Scan crawls for minutesUDP or -p- on a slow linkShrink scope, add --min-rate, use -T4
No open ports at allWrong target or all filteredRecheck the IP; try -Pn and -p-
SYN scan is slow and noisyNot root, fell back to -sTRun as root to get real -sS
Results differ from a guideFlag or script renamed between versionsCheck nmap --version and --script-help

Gotchas#

  • The default scan is only the top 1000 TCP ports. A service on a high port stays invisible until you run -p-; stopping at the default is the most expensive Nmap mistake.
  • -sS silently needs root. Without privileges Nmap falls back to -sT (full connect), which is slower and lands in the target’s logs. Run as root, or confirm the scan type with --reason.
  • filtered is not closed. A dropped probe means a firewall answered for the port, not that nothing is there; a different scan type or -Pn can still reach it.
  • open|filtered on UDP means “no idea”. UDP has no handshake, so no reply reads as both open and filtered. Confirm with a real client before trusting it.
  • -A is four loud scans in one. It bundles -sV -sC -O --traceroute; do not lead with it on a fragile box or when noise matters.
  • -T5 can drop ports. On high-latency or fragile targets, aggressive timing loses responses and hides real services; -T4 (or -T3) is the safe default.

Pairs with#

masscan and rustscan sweep huge ranges far faster, then hand their port list to nmap -sV -sC for the accurate follow-up. Once a web port shows up, gobuster and ffuf discover paths and vhosts, whatweb and nikto fingerprint the server, and curl inspects headers and responses cleanly. smbclient browses shares when 139/445 are open, and nc talks to any single service you have already found. For a real vulnerability assessment reach for a dedicated scanner; Nmap points at issues but is not one.

Find us elsewhere

Merch, stickers, and moreSupport the work at the Solvere Labs shop