If you’re diving into penetration testing or working on securing networks, you’ve probably heard about Nmap. And for good reason, it’s hands-down one of the most powerful reconnaissance tools out there.
Throughout this guide, I’m going to walk you through everything from the basics to some pretty advanced techniques. But here’s what makes this different from other tutorials you might’ve seen: every single example uses a real lab environment that you can spin up on your own machine.
Setting Up Your Own Nmap Lab
Alright, let’s talk about the lab. I’ve put together a Docker-based environment that simulates a small network with different services and vulnerabilities. Think of it as your personal playground for learning Nmap without worrying about accidentally scanning something you shouldn’t.
What’s Inside the Lab?
The lab runs on the 172.20.100.0/24 subnet and includes six different targets, each with its own purpose:
| IP Address | Operating System | Running Services | What You’ll Learn |
|---|---|---|---|
172.20.100.10 | Ubuntu | HTTP (80), SSH (22) | Web server reconnaissance |
172.20.100.25 | Alpine | FTP (21), SSH (22) | FTP enumeration techniques |
172.20.100.33 | Rocky Linux | SSH (22) | OS fingerprinting |
172.20.100.42 | Debian | HTTP (80), Telnet (23) | Finding insecure protocols |
172.20.100.55 | Custom | DNS (53/tcp), Custom (8080/udp) | UDP scanning methods |
172.20.100.68 | Custom | HTTP (80) | Additional practice target |
What You’ll Need
Before we get started, make sure you’ve got these installed:
- Docker - This handles all the containers
- Docker Compose - Manages multiple containers at once
- Nmap - Obviously, you’ll need the tool itself
- Sudo access - Nmap needs elevated privileges for certain scan types
Grabbing the Lab Files
I’ve hosted everything on GitHub for easy access:
Getting It Running
Once you’ve downloaded the files, here’s how to get everything up and running by running the run.sh script :
- Check your system:
Select an option [1-5]: 1
--- Checking Prerequisites ---
[OK] Docker is installed.
[OK] Docker Compose found: docker-compose
[OK] Nmap is installed.
------------------------------
Press Enter to continue...- Fire up the lab:
Select an option [1-5]: 2
--- Starting Nmap Lab ---
Running: docker-compose up -d --build
[+] Running 7/7
Lab is UP! Subnet: 172.20.100.0/24
Press Enter to continue...- Make sure it’s working:
Select an option [1-5]: 3
--- Verifying Nmap Lab ---
Note: Using sudo for Nmap scans...
1. Scanning for hosts...
Hosts found: 7 (Expected ~7)
2. Checking Services...
Checking Ubuntu HTTP (172.20.100.10:80/tcp)... OK
Checking Ubuntu SSH (172.20.100.10:22/tcp)... OK
Checking Alpine FTP (172.20.100.25:21/tcp)... OK
Checking Alpine SSH (172.20.100.25:22/tcp)... OK
Checking Rocky SSH (172.20.100.33:22/tcp)... OK
Checking Debian HTTP (172.20.100.42:80/tcp)... OK
Checking Debian Telnet (172.20.100.42:23/tcp)... OK
Checking Misc HTTP (172.20.100.68:80/tcp)... OK
Checking UDP Host DNS (172.20.100.55:53/tcp)... OK
Checking UDP Host 8080 (172.20.100.55:8080/udp)... OK
--------------------------
Press Enter to continue...Quick Test
Want to verify manually? Just run:
# Quick check to see all hosts
destny@destinyfkr[/destinyfkr]$ sudo nmap -sn 172.20.100.0/24
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:42 +0530
Nmap scan report for 172.20.100.10
Host is up (0.000013s latency).
MAC Address: 02:42:AC:14:64:0A (Unknown)
Nmap scan report for 172.20.100.25
Host is up (0.000015s latency).
MAC Address: 02:42:AC:14:64:19 (Unknown)
Nmap scan report for 172.20.100.33
Host is up (0.000012s latency).
MAC Address: 02:42:AC:14:64:21 (Unknown)
Nmap scan report for 172.20.100.42
Host is up (0.000013s latency).
MAC Address: 02:42:AC:14:64:2A (Unknown)
Nmap scan report for 172.20.100.55
Host is up (0.000011s latency).
MAC Address: 02:42:AC:14:64:37 (Unknown)
Nmap scan report for 172.20.100.68
Host is up (0.000030s latency).
MAC Address: 02:42:AC:14:64:44 (Unknown)
Nmap scan report for 172.20.100.1
Host is up.
Nmap done: 256 IP addresses (7 hosts up) scanned in 1.93 secondsHeads up: All the commands in this guide assume your lab is running. Make sure you’ve started it before trying any scans!
Inside the Nmap Architecture
The power of Nmap comes from its modular architecture, allowing for diverse types of network reconnaissance. The tool operates through five primary scanning techniques. Host discovery is the first step, determining which hosts are active on the network to avoid wasting time on offline targets. Port scanning follows, identifying which ports are open, closed, or filtered. Service enumeration goes deeper to determine exactly what services are running and their specific versions. Operating system detection uses TCP/IP fingerprinting to identify the target OS. Finally, the Nmap Scripting Engine (NSE) allows for advanced functionality like vulnerability detection and exploitation using Lua scripts.
Mastering Host Discovery
Before we can scan for ports or services, we need to know what is actually alive on the network. Nmap provides several methods to find these active hosts.
Simple Network Sweeps
The most basic form of host discovery is the ping sweep. You use the sn option to tell Nmap to disable port scanning and just check for live hosts. This effectively checks if a computer is online without probing for specific services.
sudo nmap 172.20.100.0/24 -snUnderstanding the Options
The sn flag disables port scanning, so Nmap only performs host discovery. Keep in mind that this method only works if the target firewalls allow ICMP echo requests or other discovery probes.
Using Host Lists
If you already have a list of targets you want to check, you can feed that file directly into Nmap using the iL option.
destny@destinyfkr[/destinyfkr]$ sudo nmap -sn -oA tnet -iL hosts.lst | grep for | cut -d" " -f5
172.20.100.10
172.20.100.25
172.20.100.33The iL option tells Nmap to perform scans against the targets listed in your provided file. We also use oA to save our output in all formats, ensuring we have the data for later.
Targeting Specific IPs
Sometimes you might only need to scan a few specific machines rather than a whole subnet. You can simply list them one after another.
destny@destinyfkr[/destinyfkr]$ sudo nmap -sn -oA tnet 172.20.100.10 172.20.100.25 172.20.100.33 | grep for | cut -d" " -f5
172.20.100.10
172.20.100.25
172.20.100.33Working with IP Ranges
If your targets are sequential, you can define a range within the last octet to save typing and keep your command clean.
destny@destinyfkr[/destinyfkr]$ sudo nmap -sn -oA tnet 172.20.100.10-42 | grep for | cut -d" " -f5
172.20.100.10
172.20.100.25
172.20.100.33
172.20.100.42Troubleshooting Discovery
To see exactly how Nmap decides if a host is alive, you can use packet tracing. This shows you every packet sent and received during the scan.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.10 -sn -oA host -PE --packet-trace
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:43 +0530
SENT (0.0177s) ARP who-has 172.20.100.10 tell 172.20.100.1
RCVD (0.0178s) ARP reply 172.20.100.10 is-at 02:42:AC:14:64:0A
NSOCK INFO [0.0530s] nsock_iod_new2(): nsock_iod_new (IOD #1)
NSOCK INFO [0.0530s] nsock_connect_udp(): UDP connection requested to 192.168.44.2:53 (IOD #1) EID 8
NSOCK INFO [0.0530s] nsock_read(): Read request from IOD #1 [192.168.44.2:53] (timeout: -1ms) EID 18
NSOCK INFO [0.0530s] nsock_write(): Write request for 44 bytes to IOD #1 EID 27 [192.168.44.2:53]
NSOCK INFO [0.0530s] nsock_trace_handler_callback(): Callback: CONNECT SUCCESS for EID 8 [192.168.44.2:53]
NSOCK INFO [0.0530s] nsock_trace_handler_callback(): Callback: WRITE SUCCESS for EID 27 [192.168.44.2:53]
NSOCK INFO [0.0610s] nsock_trace_handler_callback(): Callback: READ SUCCESS for EID 18 [192.168.44.2:53] (98 bytes)
NSOCK INFO [0.0610s] nsock_read(): Read request from IOD #1 [192.168.44.2:53] (timeout: -1ms) EID 34
NSOCK INFO [0.0610s] nsock_iod_delete(): nsock_iod_delete (IOD #1)
NSOCK INFO [0.0610s] nevent_delete(): nevent_delete on event #34 (type READ)
Nmap scan report for 172.20.100.10
Host is up (0.000070s latency).
MAC Address: 02:42:AC:14:64:0A (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 0.09 secondsUsing packet-trace reveals all network traffic generated by the scan, while PE explicitly enables ICMP echo request discovery.
Analyzing Detection Reasons
Another useful debugging tool is the reason flag. This tells Nmap to report why it classified a specific host or port the way it did.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.10 -sn -oA host -PE --reason
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:43 +0530
Nmap scan report for 172.20.100.10
Host is up, received arp-response (0.000072s latency).
MAC Address: 02:42:AC:14:64:0A (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 0.10 secondsThe reason option displays the specific cause for a result, such as receiving a syn-ack packet or an ICMP echo reply.
Host and Port Scanning
Once we have identified the live hosts, we move on to gathering detailed information. We want to find open ports and the services running on them, identify service versions to spot vulnerabilities, grab service banners for configuration details, and determine the operating system for targeted exploitation.
Decoding Port States
Nmap classifies ports into six states based on their response to probes.
| Port State | Description |
|---|---|
| open | The port is reachable and actively accepting connections. Be aware that a service is listening here. |
| closed | The port is reachable but not accepting connections. No service is listening, but the port is accessible. |
| filtered | Nmap cannot determine if the port is open because traffic is blocked by a firewall or similar device. |
| unfiltered | The port is reachable, but Nmap cannot decide if it is open or closed. This state is usually seen in ACK scans. |
| open|filtered | Nmap is unsure if the port is open or filtered. This happens when no response is received at all. |
| closed|filtered | Nmap cannot decide if the port is closed or filtered. This is typical in IP ID idle scans. |
Scanning Common Ports
To quickly find the most likely services, you can scan the top ports.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.10 --top-ports=10
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:44 +0530
Nmap scan report for 172.20.100.10
Host is up (0.000025s latency).
PORT STATE SERVICE
21/tcp closed ftp
22/tcp open ssh
23/tcp closed telnet
25/tcp closed smtp
80/tcp open http
110/tcp closed pop3
139/tcp closed netbios-ssn
443/tcp closed https
445/tcp closed microsoft-ds
3389/tcp closed ms-wbt-server
MAC Address: 02:42:AC:14:64:0A (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 3.76 secondsThis commands scans the ten most common ports, which gives you a rapid overview of the system.
Targeting Specific Ports
For a more focused approach, you can specify exactly which ports to scan using the p flag.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.25 -p 21 --packet-trace -Pn
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:45 +0530
SENT (0.0519s) ARP who-has 172.20.100.25 tell 172.20.100.1
RCVD (0.0520s) ARP reply 172.20.100.25 is-at 02:42:AC:14:64:19
NSOCK INFO [0.1320s] nsock_iod_new2(): nsock_iod_new (IOD #1)
NSOCK INFO [0.1320s] nsock_connect_udp(): UDP connection requested to 192.168.44.2:53 (IOD #1) EID 8
NSOCK INFO [0.1320s] nsock_read(): Read request from IOD #1 [192.168.44.2:53] (timeout: -1ms) EID 18
NSOCK INFO [0.1320s] nsock_write(): Write request for 44 bytes to IOD #1 EID 27 [192.168.44.2:53]
NSOCK INFO [0.1320s] nsock_trace_handler_callback(): Callback: CONNECT SUCCESS for EID 8 [192.168.44.2:53]
NSOCK INFO [0.1320s] nsock_trace_handler_callback(): Callback: WRITE SUCCESS for EID 27 [192.168.44.2:53]
NSOCK INFO [0.1420s] nsock_trace_handler_callback(): Callback: READ SUCCESS for EID 18 [192.168.44.2:53] (98 bytes)
NSOCK INFO [0.1420s] nsock_read(): Read request from IOD #1 [192.168.44.2:53] (timeout: -1ms) EID 34
NSOCK INFO [0.1420s] nsock_iod_delete(): nsock_iod_delete (IOD #1)
NSOCK INFO [0.1420s] nevent_delete(): nevent_delete on event #34 (type READ)
SENT (0.1679s) TCP 172.20.100.1:63307 > 172.20.100.25:21 S ttl=40 id=7532 iplen=44 seq=4264420530 win=1024 <mss 1460>
RCVD (0.1679s) TCP 172.20.100.25:21 > 172.20.100.1:63307 SA ttl=64 id=0 iplen=44 seq=433609458 win=64240 <mss 1460>
Nmap scan report for 172.20.100.25
Host is up (0.000070s latency).
PORT STATE SERVICE
21/tcp open ftp
MAC Address: 02:42:AC:14:64:19 (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 0.23 secondsHere we use p 21 to check only port 21, the FTP port. The Pn flag skips host discovery and treats the host as online. We also enable packet-trace for debugging.
Why skip discovery? Using Pn disables ICMP echo requests and assumes the host is up. This is extremely useful when firewalls block ping packets but services are still accessible.
Scanning via UDP
Many critical services run on UDP, such as DNS, SNMP, and DHCP. UDP scanning is slower but vital for a complete picture.
sudo nmap 172.20.100.55 -F -sUThe sU flag activates UDP scanning mode. We also used F for Fast mode, which scans fewer ports than the default.
Note: UDP scans take significantly longer than TCP scans because UDP is connectionless. Nmap has to wait for timeouts to determine if ports are open or filtered.
Documenting Your Findings
Documentation is a vital part of any security assessment. Nmap offers three primary output formats to help you save and organize your data. You have the Normal output (oN) which gives you a human-readable text file with the .nmap extension. Then there is the Grepable output (oG) using the .gnmap extension, which is perfect for parsing with command-line tools. Finally, the XML output (oX) saves data in a structured .xml format that can be imported into other tools.
Saving Everything
The best practice is to save your scans in all three formats simultaneously. You can do this easily with the oA option.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.42 -oA scan_results
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:46 +0530
Nmap scan report for 172.20.100.42
Host is up (0.0000060s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
23/tcp open telnet
80/tcp open http
MAC Address: 02:42:AC:14:64:2A (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 0.22 seconds
destny@destinyfkr[/destinyfkr]$ ls
scan_results.gnmap scan_results.nmap scan_results.xmlThis simple command generates three files: scan_results.nmap, scan_results.gnmap, and scan_results.xml.
Generating Professional Reports
Raw text files can be hard to read for non-technical stakeholders. Fortunately, you can convert your XML output into a polished HTML report using xsltproc.
destny@destinyfkr[/destinyfkr]$ xsltproc scan_results.xml -o scan_results.htmlThe resulting HTML report provides a visually organized view of your scan data with color-coding and structured sections. This is perfect for client deliverables or management presentations where clarity is key.
Service and Version Detection
Finding an open port is just the start. Knowing the exact service and version running on that port is what allows you to identify specific vulnerabilities. For instance, knowing a server runs “Apache 2.4.49” is far more valuable than just knowing it runs “Apache”.
Uncovering Versions
You can instruct Nmap to interrogate open ports for version information using the sV flag.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.10 -sV
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:48 +0530
Nmap scan report for 172.20.100.10
Host is up (0.000011s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
MAC Address: 02:42:AC:14:64:0A (Unknown)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.48 secondsThis command performs service version detection on all open ports, giving you detailed banner info.
Watching the Scan
Service scanning can sometimes take a while. To keep an eye on progress, you can combine the verbose flag v with the stats-every option.
sudo nmap 172.20.100.0/24 -sV --stats-every=5s -vThis tells Nmap to print a status line every 5 seconds so you know exactly how far along the scan is.
The Nmap Scripting Engine (NSE)
The NSE is what transforms Nmap from a scanner into a full-featured security toolkit. It uses Lua scripts to perform advanced tasks like vulnerability detection, exploitation, and sophisticated network enumeration.
Script Categories
Scripts are grouped into categories based on their function.
| Category | Function |
|---|---|
auth | Checks for authentication credentials and default logins. |
broadcast | Uses broadcast packets to discover hosts and add them to the scan. |
brute | Attempts to guess credentials using brute-force attacks. |
default | Safe scripts that run automatically when you use sC. |
discovery | Advanced probing to find more information about the network. |
dos | Checks for denial-of-service vulnerabilities. Handles with care. |
exploit | Actively tries to exploit known vulnerabilities on the target. |
external | Queries third-party databases like Whois or Geolocation. |
fuzzer | Sends malformed data to find unexpected application errors. |
intrusive | Aggressive scripts that might crash services or trigger alerts. |
malware | Scans for signs of malware infection on the target. |
safe | Scripts that are unlikely to crash services or raise alarms. |
version | Extended service version detection for difficult services. |
vuln | Checks for specific known vulnerabilities like Heartbleed. |
Using Default Scripts
For a standard assessment, you can run the default set of safe scripts using the sC flag.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.42 -sC
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:49 +0530
Nmap scan report for 172.20.100.42
Host is up (0.0000060s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE
23/tcp open telnet
80/tcp open http
|_http-title: Site doesn't have a title (text/html).
MAC Address: 02:42:AC:14:64:2A (Unknown)
Nmap done: 1 IP address (1 host up) scanned in 7.47 secondsThe Aggressive Scan
If you want to throw everything at a target, the Aggressive scan option A is your friend. It combines OS detection O, Version detection sV, Script scanning sC, and Traceroute into one simple command.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.10 -A
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-17 12:50 +0530
Nmap scan report for 172.20.100.10
Host is up (0.000073s latency).
Not shown: 998 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 8c:be:f7:b9:20:4e:27:cc:26:2c:bc:86:67:cb:58:fd (ECDSA)
|_ 256 ac:b9:9d:05:ce:14:65:ac:dc:1c:71:da:84:d8:cc:38 (ED25519)
80/tcp open http Apache httpd 2.4.52 ((Ubuntu))
|_http-title: Site doesn't have a title (text/html).
|_http-server-header: Apache/2.4.52 (Ubuntu)
MAC Address: 02:42:AC:14:64:0A (Unknown)
Device type: general purpose|router
Running: Linux 4.X|5.X, MikroTik RouterOS 7.X
OS CPE: cpe:/o:linux:linux_kernel:4 cpe:/o:linux:linux_kernel:5 cpe:/o:mikrotik:routeros:7 cpe:/o:linux:linux_kernel:5.6.3
OS details: Linux 4.15 - 5.19, OpenWrt 21.02 (Linux 5.4), MikroTik RouterOS 7.2 - 7.5 (Linux 5.6.3)
Network Distance: 1 hop
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
TRACEROUTE
HOP RTT ADDRESS
1 0.07 ms 172.20.100.10
OS and Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 7.91 secondsCustom Script Selection
You can also target specific vulnerabilities by choosing a script category.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.42 --script vulnThis runs every script in the vuln category. You can also specify individual scripts or mix multiple categories:
# Run specific script on Ubuntu web server
sudo nmap 172.20.100.10 --script http-enum
# Run multiple categories on Debian server
sudo nmap 172.20.100.42 --script "vuln,exploit"Evading Firewalls and IDS
Security defenses like firewalls and Intrusion Detection Systems are designed to catch scans like yours. Nmap provides several stealthy techniques to slip past these defenses during a penetration test.
Decoy Scanning
One effective method is the Decoy scan. This confuses defenders by hiding your true IP address among a crowd of fake ones.
destny@destinyfkr[/destinyfkr]$ sudo nmap 172.20.100.10 -p 80 -sS -Pn -n --disable-arp-ping --packet-trace -D RND:5Using D RND:5 generates five random IP addresses to act as decoys. The target sees traffic coming from multiple sources, making it incredibly difficult to pinpoint the actual attacker. This forces the security team to investigate multiple potential threats, masking your true identity.
Other Evasion Tactics
Nmap has a toolkit of other evasion methods ready to use. You can control the timing of your scan with templates ranging from T0 for paranoid stealth to T5 for insane speed. Splitting packets into tiny pieces with the f option can help evade packet inspection. You can also manually set a custom MTU size or even spoof your source IP address if you have the right network privileges. These tools allow you to adapt your scan to the specific defenses of your target environment.
This guide is for educational purposes only. Always obtain proper authorization before scanning networks.