Port Enumeration
Port scanning is the method used to identify open ports and services on a network or system. In penetration testing, it helps security professionals detect vulnerabilities, understand the network structure, and pinpoint potential entry points for unauthorized access.
Nmap
Nmap, short for “Network Mapper,” is a powerful open-source tool used for network discovery and security auditing. It’s one of the most popular and widely used network scanning tools in the cybersecurity community. You can find a comprehensive cheat sheet and links to download Nmap.
Port Scanning
The following are some commands I commonly use with the Nmap tool to port scan targets and networks.
sudo nmap -sTCV -O -A 10.1.1.1 -p 1-65535 -oA All_TCP_ports # Performs a TCP connect and default script scan of all ports on a target with output in all formats with advanced scanning techniques enabledsudo nmap -sTCV -A -O -iL IPs.txt -Pn -p- -oX targets.xml # Performs a TCP connect and default script scan of all ports on a list of targets with no ping and advanced scanning techniques enabled and output to XML filesudo nmap -sTCV -A -v -Pn -p- 10.1.1.1 # Performs a TCP connect and default script scan of all ports on a target with no ping enabledPort Sweep
An Nmap port sweep is a technique used to scan a range of IP addresses for specific open ports. This method is useful for identifying which devices on a network are running specific services associated with particular ports.
sudo nmap -p80 10.1.1.1-254 # Performs a ping sweep of port 80 across the specified IP rangesudo nmap -p22 10.1.1.0/24 # Performs a ping sweep of port 22 across the specified CIDR
Masscan
Masscan is known for its speed, making it possible to scan the entire Internet in minutes. It uses asynchronous transmission to achieve high speeds, making it ideal for large-scale scanning operations. You can find Masscan here.
masscan -p1-65535 10.1.1.1 --rate=1000 # Scans all TCP ports on the target IP with a rate of 1000 packets per secondmasscan 10.1.1.1/24 -p80,443,22 --rate 5000 # Scans common ports on a subnetmasscan -iL targets.txt -p1-1000 --rate 10000 # Read target IPs from filemasscan 10.1.1.1/24 -p80 --source-ip 10.1.1.1 # Spoof source IPmasscan 10.1.1.1/24 -p80 --spoof-mac 00:11:22:33:44:55 # Spoof MAC addressHere is a list of common commands I use with Masscan and the most common ports I’ve seen on engagements.
sudo masscan -p 21,22,23,53,80,111,135,445,139,443,389,1521,3306,8080,8081,1433,8200,8500,8999,9000,10000,8443 10.1.1.0/24 --rate 1500sudo masscan 10.1.1.0/22 -p 1-65535 --banners --rate 1000sudo masscan -iL External_IPs.txt -p 1-65535 --rate 500 --http-user-agent "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:67.0) Gecko/20100101 Firefox/67.0"You can use the following script to take in Masscan results and produce an output which can be fed into Nmap:
# This script will take in Masscan results and produce an output which can be fed into NMap# Save your Masscan result as mscan.txt then run this script to produce nmap.txt then you can run:# while read item; do sudo nmap -sV -sA -sU $item; done < nmap.txt
# If you want to export to .xml file you can use the following command and then later use this script to merge files: https://github.com/sidaf/scripts/blob/master/nmap_merge.py# while read item; do filename=$(echo $item | grep -o "^\S*"); sudo nmap -sV -sA -sU -vv $item -oX $filename.xml; done < nmap.txt
import reimport socket
regex = re.compile(r"Discovered open port (\d+)\/(udp|tcp) on (\d+\.\d+\.\d+\.\d+)", re.I)
ip_list = {}
with open('mscan.txt') as f: lines = f.readlines() for line in lines: port = regex.match(line).group(1) protocol = regex.match(line).group(2) ip = regex.match(line).group(3)
# Add the IP to dictionary if it's not already try: ip_list[ip] except KeyError: ip_list[ip] = {}
# Add protocol to dictionary if it's not already try: ip_list[ip][protocol] except KeyError: ip_list[ip][protocol] = []
# Append the port to the list ip_list[ip][protocol].append(port)
with open('nmap.txt', 'a') as f: sorted_ips = sorted(ip_list.items(), key=lambda item: socket.inet_aton(item[0])) for ip, protocols in sorted_ips: udp_ports = "" tcp_ports = ""
# Check to see if any UDP ports were found try: for port in protocols['udp']: udp_ports += port + ',' except KeyError: pass
# Check to see if any TCP ports were found try: for port in protocols['tcp']: tcp_ports += port + ',' except KeyError: pass
# Print IP and ports to file ready for NMap scan if udp_ports and tcp_ports: line = ip + ' -p U:' + udp_ports + 'T:' + tcp_ports elif udp_ports: line = ip + ' -p U:' + udp_ports elif tcp_ports: line = ip + ' -p T:' + tcp_ports f.write(line + '\n')Save your Masscan result as mscan.txt, and then run this script to produce nmap.txt. Then you can run:
while read item; do filename=$(echo $item | grep -o "^\S*"); sudo nmap -sCV -T4 -O --version-light -vv $item -oX $filename.xml; done < ../nmap.txtwhile read item; do filename=$(echo $item | grep -o "^\S*"); sudo nmap -sCV -T4 -O -F --version-light -vv $item -oX $filename.xml; done < nmap.txtScanning the whole internet
You can scan the whole internet for port 80 for example using the following:
sudo masscan -p 80 0.0.0.0/0 --rate 1000000 --exclude 255.255.255.255 > port80.txtOnce you have a list of IP addresses with port 80 open you can run it through a tool like HTTPX to get the title of the web page running on the port.
httpx -l port80.txt -ports 80 -title -no-color > port80_titles.txthttpx -list Web_Ports.txt -p 80,443 -silent -sc -location -title -server -mc 200 -fr
ZMap
ZMap is a free, open-source security scanner developed as a faster alternative to Nmap. ZMap can scan the entire IPv4 address space in 44 minutes on a single port using one gigabit per second of network bandwidth. It can complete a scan in under five minutes with a ten-gigabit connection. You can find the details, as well as installation instructions for Zmap, here.
zmap -p 80 10.1.1.0/24 -o results.csv # Scans port 80 across the specified subnet and outputs the results to a CSV filezmap -p 22,80,443 --target-ip=10.1.1.1 -o output.txt # Scan SSH, HTTP, and HTTPS on a single IPzmap -p 23 -o telnet_scan.txt --rate=1000000 # Scan the whole internet for open Telnet ports
RustScan
RustScan is a fast port scanner written in Rust that quickly identifies open TCP ports and can automatically pipe the results into Nmap for service enumeration. You can find it here: https://github.com/RustScan/RustScan
rustscan -a 10.1.1.1 # Scan a target for open ports and pass them to Nmaprustscan -a 10.1.1.1,10.1.1.2 # Scan multiple targetsrustscan -a 10.1.1.0/24 # Scan a CIDR rangerustscan -a 10.1.1.1 -p 22,80,443,445,3389 # Scan a specific list of portsrustscan -a 10.1.1.1 -r 1-1000 # Scan a specific port rangerustscan -a 10.1.1.1 -r 1-65535 # Scan the full TCP port rangeArguments after -- are passed straight through to Nmap, so any Nmap flags and output options can be used as normal.
rustscan -a 10.1.1.1 -- -sC -sV # Run Nmap default scripts and service detection on discovered portsrustscan -a 10.1.1.1 -- -sC -sV -Pn # Same as above, skipping the ping check for hosts that don't respond to ICMPrustscan -a 10.1.1.1 -- -sT -sC -sV -T4 -Pn # Use a TCP connect scan instead of a SYN scanrustscan -a 10.1.1.1 -- --script vuln -sV -Pn # Run Nmap's vulnerability scripts against discovered portsrustscan -a 10.1.1.1 -r 1-65535 -- -sC -sV -Pn -oA initial # Full port range scan piped into Nmap, saving output in all formatsRustScan scans in batches, and the batch size and connection timeout can be tuned to trade off speed against reliability and the likelihood of triggering defensive controls.
rustscan -a 10.1.1.1 -b 5000 # Increase batch size for faster scanningrustscan -a 10.1.1.1 -t 5000 # Increase the connection timeout (ms) for slower or higher-latency networks| Option | Description |
|---|---|
-a | Target IP, hostname, or list of addresses |
-p | Specific ports |
-r | Port range |
-b | Batch size |
-t | Connection timeout (ms) |
-- | Pass all following arguments to Nmap |
Naabu
When pentesting a large number of systems, running detailed Nmap scans against every host can quickly become time-consuming. Naabu is a fast port scanner developed by ProjectDiscovery that’s great at first identifying which hosts and ports are actually reachable, before handing the interesting ones off to a tool like Nmap for detailed enumeration. Its simple output also makes it easy to pipe into other reconnaissance tools. You can find it here: https://github.com/projectdiscovery/naabu
naabu -host 10.1.1.1 # Scan a single hostnaabu -host 10.1.1.0/24 # Scan a network rangenaabu -list hosts.txt # Scan a list of targetsCombining with Other Tools
Naabu’s output pipes cleanly into other reconnaissance tools, which is particularly useful during external engagements where subdomains discovered via Subfinder need to be checked for exposed services.
subfinder -d example.com -silent | naabu -silent # Feed discovered subdomains straight into Naabunaabu -list hosts.txt -silent | httpx -silent # Identify which open ports are actually serving HTTPsubfinder -d example.com -silent | naabu -silent | httpx -silent # Chain subdomain enumeration, port discovery and HTTP probing togetherWeb applications, APIs and admin interfaces aren’t always exposed on 80/443 — non-standard ports such as 8000, 8080, 8081, 8443, 8888, 9000 and 9090 are worth including when scanning for web services.
ASN and Large Range Scanning
echo AS3741 | naabu -p 80,443 -passive -v -rate 10 # Scan the address space associated with an Autonomous System (only within authorised scope)naabu -host 10.1.0.0/16 -p 80,443 -rate 1000 -silent # Scan a large range for HTTP/HTTPS services, tuning -rate to the target environmentFinding Exposed Configuration Files
Naabu can be chained with httpx and curl to search a large, authorised web estate for exposed configuration files, such as ASP.NET’s web.config or appsettings.json. A 200 response alone isn’t enough to confirm a file exists, since many applications return a 200 on custom error pages, so the response body should be validated too.
naabu -host <authorised-range> -p 80,443 -rate 1000 -silent | httpx -stream -silent -path /web.config -mc 200 | \while read -r url; do curl -sk --max-time 10 "$url" | head -c 300 | grep -qiE '<configuration|connectionStrings|system.web|appSettings' && echo "$url"donenaabu -host <authorised-range> -p 80,443 -rate 1000 -silent | httpx -stream -silent -path /appsettings.json -mc 200 | \while read -r url; do body=$(curl -sk --max-time 10 "$url") printf "%s" "$body" | jq empty 2>/dev/null && echo "$url [$(printf "%s" "$body" | wc -c) bytes]"doneIf a configuration file is found, look for values such as connectionString, password, ApiKey, ClientSecret, machineKey, AWSSecretAccessKey or AzureWebJobsStorage before concluding anything sensitive was actually exposed.
The below adds parallelisation and additional keywords for sensitive files that I have come accross in real tests.
naabu -host <authorised-range> -p 80,443 -rate 5000 -silent | \httpx -stream -silent -threads 300 -path /web.config -mc 200 | \xargs -P 100 -I{} sh -c 'url="{}";body=$(curl -sk --max-time 10 "$url");printf "%s" "$body" | head -c 300 | grep -qiE "<configuration|connectionStrings|system.web|appSettings" || exit 0;match=$(printf "%s" "$body" | grep -Eio "connectionString|password|pwd|User ID|uid=|ApiKey|ClientSecret|machineKey|validationKey|decryptionKey|StorageAccountKey|AccountKey|AccessKey|SecretKey|AWSAccessKeyId|AWSSecretAccessKey|AzureWebJobsStorage|smtp|mailSettings|appSettings|connectionStrings" | sort -fu | paste -sd "," -);[ -n "$match" ] && echo "$url [$(printf "%s" "$body" | wc -c) bytes] [$match]"'This reports not only that a configuration file was found, but its approximate size and which sensitive keywords were present, for example:
https://example.com/web.config [2841 bytes] [connectionString,password]
PowerShell Port Scanning
If you are in a Windows environment or only have access to a compromised Windows host, an excellent set of scripts for native Windows PowerShell port scanning is Minimalistic TCP and UDP port scanners, which can be found here.
Import-Module .\port-scan-tcp.ps1port-scan-tcp 10.1.1.1 8080 # Scanning a single IP and Portport-scan-tcp 10.1.1.1 (21,22,23,25,80,443,445,3389) # Scanning a single IP for a list TCP portsport-scan-tcp (gc .\targets.txt) 22 # Scanning a list of IPs in a file for port 221..255 | foreach { port-scan-tcp 10.1.1.$_ 8080} # Scanning a range of IPs for a single PortIf you have no script execution, you can try using the Test-NetConnection cmdlet, which displays diagnostic information for a connection.
80,3601|%{ Test-NetConnection -Port $_ 10.1.1.1 -WA SilentlyContinue} | ?{$_.TCPTestSucceeded -eq $true} | select ComputerName,RemoteAddress,RemotePort # Single IP with a list of ports1..254 | % {"10.1.1.$($_): $(Test-Connection -count 1 -comp 10.1.1.$($_) -quiet)"} # Test if a range of IPs are aliveAnother native PowerShell script you can use to scan for hosts on a network is:
1..254 | ForEach-Object {$ip = "10.1.1.$_"$hostname = [System.Net.Dns]::GetHostEntry($ip).HostNameif ($hostname -ne $null) { Write-Host "IP: $ip, Hostname: $hostname" }}You can also use Test-Connection to perform a PowerShell Ping on a network to test for live hosts and return if they are reachable or not:
$baseIP = "10.1.1."
for ($i = 1; $i -le 254; $i++) { $ip = $baseIP + $i $result = Test-Connection -ComputerName $ip -Count 1 -Quiet if ($result -eq $true) { Write-Host "$ip is reachable" } else { Write-Host "$ip is unreachable" }}The above script can be used to take a file of IPs and write the results to a file:
$file = "IPs.txt"$outputFile = "Ping_Responded.txt"$ips = Get-Content $file
foreach ($ip in $ips) { $result = Test-Connection -ComputerName $ip -Count 1 -Quiet if ($result) { Write-Output "$ip is live" | Out-File -FilePath $outputFile -Append } else { Write-Output "$ip is dead" | Out-File -FilePath $outputFile -Append }}


