Nmap Mastery: The Complete Guide to Network Reconnaissance
From basic port scanning to advanced NSE scripting and IDS evasion—everything you need to master the most essential tool in penetration testing.
Nmap (Network Mapper) has been the cornerstone of network reconnaissance since 1997. Despite being nearly three decades old, it remains the most powerful and flexible network scanning tool available. Whether you're conducting a penetration test, performing asset discovery, or auditing firewall rules, Nmap is almost certainly part of your workflow.
This guide covers Nmap from the ground up—starting with fundamentals and progressing to advanced techniques that separate amateur scans from professional reconnaissance.
Only scan networks and systems you own or have explicit written authorization to test. Unauthorized port scanning may violate computer crime laws in your jurisdiction.
Understanding Port States
Before diving into scan types, you must understand how Nmap classifies port states. Nmap reports six possible states:
| State | Meaning | Typical Cause |
|---|---|---|
| open | Application actively accepting connections | Service listening on port |
| closed | Port accessible but no application listening | No service bound; host is up |
| filtered | Packet filtering prevents probe from reaching port | Firewall dropping packets |
| unfiltered | Port accessible but open/closed undetermined | ACK scan result |
| open|filtered | Cannot determine if open or filtered | UDP/IP protocol scans with no response |
| closed|filtered | Cannot determine if closed or filtered | IP ID idle scan |
The distinction between filtered and closed is critical. A closed port confirms the host exists and is reachable—valuable information. A filtered port tells you a firewall is in play but reveals nothing about whether a service is behind it.
Scan Types Explained
TCP SYN Scan (-sS): The Default
The TCP SYN scan—also called "half-open" scanning—is Nmap's default when running as root. It sends a SYN packet and analyzes the response:
- SYN/ACK response: Port is open
- RST response: Port is closed
- No response / ICMP unreachable: Port is filtered
# Default SYN scan (requires root)
sudo nmap -sS 192.168.1.1
# SYN scan with version detection on top 1000 ports
sudo nmap -sS -sV 192.168.1.1
# SYN scan all 65535 ports
sudo nmap -sS -p- 192.168.1.1
The SYN scan never completes the TCP handshake, making it faster and slightly stealthier than a full connect scan. However, modern IDS/IPS systems easily detect SYN scans.
TCP Connect Scan (-sT): When You're Not Root
Without root privileges, Nmap cannot craft raw packets and must use the operating system's connect() call. This completes the full TCP handshake:
# Connect scan (works without root)
nmap -sT 192.168.1.1
# This is what happens under the hood:
# 1. SYN →
# 2. ← SYN/ACK
# 3. ACK →
# 4. RST → (Nmap terminates connection)
Connect scans are slower and more likely to be logged by the target since they establish complete connections. Use SYN scans when possible.
UDP Scan (-sU): The Forgotten Protocol
Many critical services run on UDP: DNS (53), SNMP (161), TFTP (69), NTP (123), and more. UDP scanning is slow and tricky because UDP is connectionless:
# Basic UDP scan (slow - be patient)
sudo nmap -sU 192.168.1.1
# UDP scan on specific ports
sudo nmap -sU -p 53,161,123,500 192.168.1.1
# Combined TCP and UDP scan
sudo nmap -sS -sU -p T:80,443,U:53,161 192.168.1.1
# Speed up with version intensity reduction
sudo nmap -sU --version-intensity 0 192.168.1.1
UDP has no handshake. If a port is open, the service may or may not respond. If closed, the host should send ICMP Port Unreachable—but many firewalls block ICMP. No response could mean open, filtered, or rate-limited. Nmap must wait for timeouts and retry, making UDP scans 100x slower than TCP.
Stealth Scans: FIN, NULL, and Xmas
These scan types exploit a quirk in the TCP RFC: packets without SYN, ACK, or RST flags sent to a closed port should receive a RST, but open ports should ignore them silently:
# FIN scan - sets only FIN flag
sudo nmap -sF 192.168.1.1
# NULL scan - no flags set
sudo nmap -sN 192.168.1.1
# Xmas scan - sets FIN, PSH, URG (lights up like a Christmas tree)
sudo nmap -sX 192.168.1.1
Important caveat: These scans don't work against Windows systems. Microsoft's TCP stack sends RST regardless of port state. They're also detected by any modern IDS. Their primary use today is firewall rule testing and edge cases.
Service and Version Detection
Knowing a port is open is just the beginning. You need to know what's running on that port. Nmap's version detection (-sV) probes open ports to identify services:
# Basic version detection
nmap -sV 192.168.1.1
# Aggressive version detection (more probes, more accurate)
nmap -sV --version-intensity 5 192.168.1.1
# Maximum intensity (slow but thorough)
nmap -sV --version-all 192.168.1.1
# Light version detection (faster, less accurate)
nmap -sV --version-light 192.168.1.1
Version detection sends service-specific probes and matches responses against Nmap's database of 11,000+ signatures. This reveals:
- Service name and version (e.g., "Apache httpd 2.4.51")
- Operating system hints
- Hostname and device type
- CPE (Common Platform Enumeration) identifiers for CVE correlation
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.1 (Ubuntu Linux; protocol 2.0)
80/tcp open http nginx 1.18.0 (Ubuntu)
443/tcp open ssl/http nginx 1.18.0
3306/tcp open mysql MySQL 8.0.32-0ubuntu0.22.04.2
5432/tcp open postgresql PostgreSQL DB 14.7 - 14.8
8080/tcp open http Apache Tomcat 9.0.65
Operating System Detection
Nmap can fingerprint the target's operating system by analyzing TCP/IP stack behavior—TTL values, window sizes, TCP options ordering, and responses to malformed packets:
# OS detection (requires at least one open and one closed port)
sudo nmap -O 192.168.1.1
# OS detection with version detection
sudo nmap -O -sV 192.168.1.1
# Limit OS detection to promising targets
sudo nmap -O --osscan-limit 192.168.1.0/24
# Aggressive OS guessing when uncertain
sudo nmap -O --osscan-guess 192.168.1.1
OS CPE: cpe:/o:linux:linux_kernel:5.4
OS details: Linux 5.4 - 5.10
Network Distance: 2 hops
OS detection performed. Please report any incorrect results at https://nmap.org/submit/
The Nmap Scripting Engine (NSE)
NSE transforms Nmap from a port scanner into a comprehensive vulnerability assessment platform. With 600+ scripts included, NSE can detect vulnerabilities, enumerate services, and even perform brute-force attacks.
Script Categories
| Category | Purpose | Example Scripts |
|---|---|---|
| auth | Authentication and credential testing | ssh-auth-methods, ftp-anon |
| broadcast | Discover hosts via broadcast traffic | broadcast-dhcp-discover |
| brute | Brute-force credential attacks | ssh-brute, http-brute |
| default | Safe, useful scripts (run with -sC) | ssh-hostkey, http-title |
| discovery | Service and host discovery | dns-brute, smb-enum-shares |
| exploit | Active exploitation attempts | http-shellshock, smb-vuln-ms17-010 |
| fuzzer | Fuzz testing for vulnerabilities | dns-fuzz, http-form-fuzzer |
| safe | Non-intrusive information gathering | Most default scripts |
| vuln | Vulnerability detection | vulners, vulscan, http-vuln-* |
Running NSE Scripts
# Run default scripts (-sC is shorthand for --script=default)
nmap -sC 192.168.1.1
# Run a specific script
nmap --script http-title 192.168.1.1
# Run multiple scripts
nmap --script "http-title,http-headers,http-methods" 192.168.1.1
# Run all scripts in a category
nmap --script vuln 192.168.1.1
# Run scripts matching a pattern
nmap --script "http-*" 192.168.1.1
# Combine categories with boolean operators
nmap --script "(default or vuln) and not brute" 192.168.1.1
Essential NSE Scripts for Pentesting
# Vulnerability scanning with vulners (CVE correlation)
nmap -sV --script vulners 192.168.1.1
# SMB vulnerability check (EternalBlue, etc.)
nmap --script smb-vuln* -p 445 192.168.1.1
# HTTP enumeration
nmap --script http-enum -p 80,443,8080 192.168.1.1
# SSL/TLS analysis
nmap --script ssl-enum-ciphers -p 443 192.168.1.1
# DNS zone transfer attempt
nmap --script dns-zone-transfer -p 53 ns1.target.com
# SMB share enumeration
nmap --script smb-enum-shares -p 445 192.168.1.1
# Default credentials check
nmap --script http-default-accounts -p 80 192.168.1.1
# Banner grabbing on all ports
nmap --script banner -p- 192.168.1.1
Passing Arguments to Scripts
# HTTP brute force with custom wordlist
nmap --script http-brute \
--script-args userdb=/path/users.txt,passdb=/path/pass.txt \
-p 80 192.168.1.1
# SMB enumeration with credentials
nmap --script smb-enum-shares \
--script-args smbusername=admin,smbpassword=password123 \
-p 445 192.168.1.1
# HTTP path brute with custom settings
nmap --script http-enum \
--script-args http-enum.basepath='/api/',http-enum.displayall=true \
-p 8080 192.168.1.1
Timing and Performance
Nmap's timing controls balance speed against accuracy and stealth. The -T flag provides presets from T0 (paranoid) to T5 (insane):
| Template | Name | Use Case |
|---|---|---|
| -T0 | Paranoid | IDS evasion, extremely slow |
| -T1 | Sneaky | IDS evasion |
| -T2 | Polite | Reduce network load |
| -T3 | Normal | Default behavior |
| -T4 | Aggressive | Fast, reliable networks |
| -T5 | Insane | Very fast networks, may miss results |
# Aggressive timing for fast networks
nmap -T4 192.168.1.0/24
# Custom timing for large scans
nmap --min-rate 1000 --max-retries 2 192.168.1.0/24
# Parallel host scanning
nmap --min-hostgroup 64 --max-hostgroup 256 192.168.1.0/24
# Control probe parallelization
nmap --min-parallelism 10 --max-parallelism 100 192.168.1.1
# Adjust timeouts
nmap --host-timeout 30m --scan-delay 500ms 192.168.1.1
Firewall and IDS Evasion
While Nmap cannot make you invisible, it offers techniques to complicate detection and bypass simple filters:
Packet Fragmentation
# Fragment packets into 8-byte chunks
nmap -f 192.168.1.1
# Use 16-byte fragments
nmap -ff 192.168.1.1
# Specify MTU (must be multiple of 8)
nmap --mtu 24 192.168.1.1
Decoys and Spoofing
# Use decoy IP addresses (makes you one of many scanners)
nmap -D 10.0.0.1,10.0.0.2,10.0.0.3,ME 192.168.1.1
# Generate random decoys
nmap -D RND:10 192.168.1.1
# Spoof source IP (won't receive responses)
nmap -S 10.0.0.100 -e eth0 192.168.1.1
# Spoof source port (bypass lazy firewall rules)
nmap --source-port 53 192.168.1.1
nmap -g 53 192.168.1.1
Timing Evasion
# Paranoid timing (5 minutes between probes)
nmap -T0 192.168.1.1
# Add random delay between probes
nmap --scan-delay 5s --max-scan-delay 30s 192.168.1.1
# Randomize target order
nmap --randomize-hosts 192.168.1.0/24
Modern IDS/IPS and SIEM systems detect Nmap regardless of evasion techniques. Fragmentation, decoys, and slow timing worked against 2005-era detection. Today, they may bypass basic firewalls but not enterprise security stacks. The most effective "evasion" is having authorization and coordinating with the blue team.
Output Formats
Nmap supports multiple output formats for different use cases. Always save scan results:
# Normal output (human-readable)
nmap -oN scan_results.txt 192.168.1.1
# XML output (for parsing and tools)
nmap -oX scan_results.xml 192.168.1.1
# Grepable output (one host per line, easy to parse)
nmap -oG scan_results.gnmap 192.168.1.1
# All formats at once
nmap -oA scan_results 192.168.1.1
# Append to existing file
nmap --append-output -oN ongoing_scan.txt 192.168.1.2
Parsing Nmap Output
# Extract all open ports
grep "open" scan.gnmap | cut -d' ' -f2 | sort -u
# Extract hosts with specific port open
grep "80/open" scan.gnmap | cut -d' ' -f2
# Extract all open port numbers
grep -oP '\d+/open' scan.gnmap | cut -d'/' -f1 | sort -u
# One-liner for quick host:port list
grep "Ports:" scan.gnmap | \
awk '{print $2}' | \
while read host; do
grep "$host" scan.gnmap | \
grep -oP '\d+/open/[^/]+' | \
sed "s|^|$host:|"
done
import xml.etree.ElementTree as ET
def parse_nmap_xml(filename):
tree = ET.parse(filename)
root = tree.getroot()
results = []
for host in root.findall('host'):
ip = host.find('address').get('addr')
for port in host.findall('.//port'):
port_id = port.get('portid')
protocol = port.get('protocol')
state = port.find('state').get('state')
service = port.find('service')
if service is not None:
svc_name = service.get('name', '')
svc_version = service.get('version', '')
else:
svc_name, svc_version = '', ''
results.append({
'ip': ip,
'port': port_id,
'protocol': protocol,
'state': state,
'service': svc_name,
'version': svc_version
})
return results
# Usage
findings = parse_nmap_xml('scan_results.xml')
for f in findings:
if f['state'] == 'open':
print(f"{f['ip']}:{f['port']} - {f['service']} {f['version']}")
Real-World Scanning Methodology
Here's a practical methodology for comprehensive network reconnaissance:
Phase 1: Host Discovery
# Ping sweep to find live hosts
nmap -sn 192.168.1.0/24 -oG live_hosts.gnmap
# If ICMP is blocked, use TCP discovery
nmap -sn -PS22,80,443 192.168.1.0/24
# ARP scan on local network (most reliable)
nmap -sn -PR 192.168.1.0/24
# Extract live hosts for next phase
grep "Up" live_hosts.gnmap | cut -d' ' -f2 > targets.txt
Phase 2: Port Scanning
# Quick scan: Top 1000 ports with service detection
nmap -sS -sV -T4 -iL targets.txt -oA quick_scan
# Full TCP scan on interesting hosts
nmap -sS -p- -T4 --min-rate 1000 192.168.1.50 -oA full_tcp
# UDP scan on common ports
nmap -sU -p 53,67,68,69,123,161,500,514 -iL targets.txt -oA udp_scan
Phase 3: Enumeration
# Full enumeration on discovered services
nmap -sV -sC -O -p 22,80,443,3306,8080 192.168.1.50 -oA full_enum
# Vulnerability scanning
nmap -sV --script vuln -p 22,80,443 192.168.1.50 -oA vulns
# Service-specific enumeration
nmap --script "smb-*" -p 445 192.168.1.50
nmap --script "http-*" -p 80,443,8080 192.168.1.50
nmap --script "ssh-*" -p 22 192.168.1.50
Common Mistakes to Avoid
- Scanning without saving output: Always use -oA. You will need to reference results later.
- Skipping UDP: Critical services like DNS, SNMP, and VPN run on UDP. Don't ignore it.
- Using -T5 everywhere: Insane timing drops packets and misses open ports. Use -T4 maximum.
- Running as non-root: Non-root scans use connect() which is slower and more detectable.
- Ignoring filtered ports: Filtered ports indicate firewalls—useful information for the engagement.
- Not using version detection: Open ports without service identification are incomplete findings.
Quick Reference Card
# === Scan Types ===
-sS TCP SYN (default, needs root)
-sT TCP Connect (no root needed)
-sU UDP scan
-sV Version detection
-sC Default scripts
-O OS detection
-A Aggressive: -sV -sC -O --traceroute
# === Port Specification ===
-p 22 Single port
-p 22,80,443 Multiple ports
-p 1-1000 Port range
-p- All 65535 ports
--top-ports 100 Top 100 common ports
# === Timing ===
-T0 to -T5 Timing templates
--min-rate N Minimum packets/second
--max-retries N Maximum probe retries
# === Output ===
-oN file.txt Normal output
-oX file.xml XML output
-oG file.gnmap Grepable output
-oA basename All formats
# === Evasion ===
-f Fragment packets
-D decoy1,ME Use decoys
-S IP Spoof source IP
-g PORT Spoof source port
# === Target Specification ===
192.168.1.1 Single host
192.168.1.0/24 CIDR notation
192.168.1.1-100 Range
-iL targets.txt File input
--exclude 192.168.1.1 Exclude host
Conclusion
Nmap mastery is fundamental to penetration testing. The tool's depth means there's always more to learn—custom NSE scripts, integration with other tools, creative scan combinations. Start with the basics, practice the methodology, and gradually incorporate advanced techniques as you understand their purpose.
The key insight is that reconnaissance quality directly determines exploitation success. A thorough Nmap scan reveals the attack surface. A hasty scan misses critical entry points. Take the time to scan properly.
For professional penetration testing services, contact Brickell Technologies. Our assessments include comprehensive reconnaissance with findings you can act on.
- Nmap - Official site with downloads and documentation
- Nmap Network Scanning - Free online book by Gordon Lyon
- NSE Script Documentation - Complete script reference
- Wireshark - Analyze Nmap packet captures
- Kali Linux - Security distro with Nmap pre-installed