Enumeration is the phase that decides the engagement. More boxes fall to thorough enumeration than to clever exploitation, and the rule on a stuck target is almost always “enumerate harder”, not “find a better exploit”.

The mistake that costs the most time is treating recon as a pass: scan, read the output, move to exploitation. It is a loop. Each finding sends you back to enumerate something new, and the surface grows as you work.

scan all ports
      |
      v
enumerate each service  <---------+
      |                           |
      v                           |
foothold candidate?               |
      |                           |
      +-- no --> back to service enumeration
      |
      +-- new info --> back to scanning
      |
      v
    exploit

Record everything as you go, including the dead ends. A port dismissed at hour one is often the way in at hour three, and “SMB null session denied” is information.

Port scanning

Never scan the top 1000 TCP ports and stop. The interesting service is usually on a high port.

export TARGET=10.10.10.5
mkdir -p scans

# 1. all TCP ports, fast, to learn what exists
nmap -p- --min-rate 5000 -Pn -n -oN scans/all-ports.txt "$TARGET"

# 2. deep scan only the ports that answered
ports=$(grep -oP '^\d+(?=/tcp\s+open)' scans/all-ports.txt | paste -sd,)
nmap -p"$ports" -sCV -oN scans/services.txt "$TARGET"

# 3. top UDP ports, because UDP services are easy to miss
sudo nmap -sU --top-ports 100 -oN scans/udp.txt "$TARGET"

Two passes rather than one is the point. A -sCV scan across all 65535 ports takes long enough that people skip -p- entirely; splitting it means you get the full port list in under a minute and spend the slow scan only where something is listening.

What each open port is worth

PortServiceFirst thing to try
21FTPAnonymous login, then version exploits
22SSHVersion, key auth, user enumeration on old versions
25/110/143MailUser enumeration via VRFY and RCPT
53DNSZone transfer, subdomain brute force
80/443HTTPContent discovery and virtual hosts
88KerberosActive Directory is present; AS-REP roasting, LDAP enumeration
111/2049NFSShow mounts, look for no_root_squash
139/445SMBNull session, share listing, version exploits
161SNMPCommunity string brute force, then walk it
389/636LDAPAnonymous bind, dump the directory
1433MSSQLDefault credentials, xp_cmdshell
3306MySQLDefault credentials, file read and write
3389RDPCredential spray, BlueKeep on old hosts
5985WinRMValid credentials here mean a shell, via evil-winrm
6379RedisUnauthenticated access, write an SSH key or a cron job
8080HTTP altOften Tomcat or Jenkins, both with well-known paths

Web content discovery

whatweb "http://$TARGET"
feroxbuster -u "http://$TARGET" -w raft-medium-directories.txt -x php,txt,html

# virtual hosts: many targets serve a different site by Host header
ffuf -w subdomains.txt -u "http://$TARGET" -H "Host: FUZZ.target.htb" -fs BASELINE

Add the hostname to /etc/hosts as soon as a redirect reveals one. Many services only answer to their name and return nothing useful for the bare IP, which is exactly the kind of thing that looks like a dead end and is not.

Discipline that pays back

  • One finding per line in your notes, with the command that produced it.
  • Record the dead ends too. A negative result stops you repeating the work.
  • Re-scan after you get credentials. Authenticated enumeration reveals far more than the unauthenticated pass did, and this is the single most commonly skipped step in the loop.

What this does not solve

A time-boxed CTF challenge with a known single trick rewards pattern recognition over breadth, and this methodology is the wrong shape for it. Enumeration also has diminishing returns: past a point you are re-reading output rather than finding new surface, and the discipline is knowing which of the two you are currently doing.