No CTF starts perfectly. At a certain point, you just gotta start trying. This recon guide is a little dinky, but it's the checklist I keep coming back to whenever I get dropped onto a fresh box with nothing but an IP address.
The whole point of recon is to gather as much info as possible. CTF strategy is really just finding out what you need to know to solve the problem. If it were obvious, it would be too easy.
The Tools
Nmap is a network scanner, and it's the hammer of the toolkit. It's good for finding open ports, the services running on them, and other network details for a domain/IP. A basic nmap scan often isn't enough on its own though. You need the right flags to get it to leak more info. I break down the ones I use in step 1 below.
Gobuster is an enumeration tool that's good for all sorts of guessing games. Any time an nmap scan gives you a lead (like a web server on port 80), it's worth checking whether gobuster can help you dig a little further. It has a handful of modes:
dir: directory/file enumerationdns: DNS subdomain enumerationvhost: virtual host enumeration (you most probably want to use the IP address as the URL)fuzz: replaces the keywordFUZZin the URL, headers, and request bodytftp: TFTP enumerations3/gcs: AWS and GCP bucket enumeration
I mostly live in dir, dns, and vhost.
A Little Step-by-Step for CTF Recon
1. Run an nmap scan
nmap -sC -sV -p- -T4 -O --open <IP>
-sC: runs nmap's default scripts against the target-sV: version detection for each service it finds-p-: checks all 65535 ports, not just the top 1000-T4: speeds up the scan (timing goes from T0 to T5; T4 is aggressive but reliable, T5 can miss things)-O: OS detection, tries to fingerprint what OS the target is running--open: only shows open ports, which cuts down the noise
Here's what that looked like on TryHackMe's Lian_Yu room:
root@ip-10-66-121-195:~# nmap -sC -sV -p- 10.66.187.249
Starting Nmap 7.80 ( https://nmap.org ) at 2026-05-15 19:06 BST
Nmap scan report for 10.66.187.249
Host is up (0.00022s latency).
Not shown: 65530 closed ports
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 3.0.2
22/tcp open ssh OpenSSH 6.7p1 Debian 5+deb8u8 (protocol 2.0)
| ssh-hostkey:
| 1024 56:50:bd:11:ef:d4:ac:56:32:c3:ee:73:3e:de:87:f4 (DSA)
| 2048 39:6f:3a:9c:b6:2d:ad:0c:d8:6d:be:77:13:07:25:d6 (RSA)
| 256 a6:69:96:d7:6d:61:27:96:7e:bb:9f:83:60:1b:52:12 (ECDSA)
|_ 256 3f:43:76:75:a8:5a:a6:cd:33:b0:66:42:04:91:fe:a0 (ED25519)
80/tcp open http Apache httpd
|_http-server-header: Apache
|_http-title: Purgatory
111/tcp open rpcbind 2-4 (RPC #100000)
| rpcinfo:
| program version port/proto service
| 100000 2,3,4 111/tcp rpcbind
| 100000 2,3,4 111/udp rpcbind
| 100000 3,4 111/tcp6 rpcbind
| 100000 3,4 111/udp6 rpcbind
| 100024 1 40457/tcp status
| 100024 1 43586/udp status
| 100024 1 45265/udp6 status
|_ 100024 1 54567/tcp6 status
40457/tcp open status 1 (RPC #100024)
Service Info: OSs: Unix, Linux; CPE: cpe:/o:linux:linux_kernel
Nmap done: 1 IP address (1 host up) scanned in 15.63 seconds
FTP, SSH, and a web server titled "Purgatory" on port 80. The web server is the obvious next lead.
2. Check for subdirectories
This is necessary when the nmap scan doesn't tell you much, but you can also just run it in case and see what's there.
gobuster dir -u http://<IP> -w <wordlist> -r
-u(required): the base URL to scan-w(required): the wordlist of directory names to try against the base URL-r: follow redirects-x: file extensions to check for too, e.g.php,js,txt
root@ip-10-66-121-195:/usr/share/wordlists/dirb# gobuster dir -u http://10.66.187.249 -w big.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.66.187.249
[+] Method: GET
[+] Threads: 10
[+] Wordlist: big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/.htpasswd (Status: 403) [Size: 199]
/.htaccess (Status: 403) [Size: 199]
/island (Status: 301) [Size: 236] [--> http://10.66.187.249/island/]
/server-status (Status: 403) [Size: 199]
Progress: 20469 / 20470 (100.00%)
===============================================================
Finished
===============================================================
If nothing turns up, run it again with a few different wordlists. You never know what will show up. When you're stuck, the answer is usually to cast a wider net: more scans, more lists, more tools. You need more variety in what you try in order to find more things.
And (silly me) don't forget to search inside the directories you find. In Lian_Yu, /island looked like a dead end until I ran gobuster on /island itself and found /2100:
root@ip-10-66-121-195:/usr/share/wordlists/SecLists/Discovery/Web-Content# gobuster dir -u http://10.66.187.249/island -w directory-list-2.3-big.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://10.66.187.249/island
[+] Method: GET
[+] Threads: 10
[+] Wordlist: directory-list-2.3-big.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/2100 (Status: 301) [Size: 241] [--> http://10.66.187.249/island/2100/]
Progress: 1273833 / 1273834 (100.00%)
===============================================================
Finished
===============================================================
3. Try for subdomains
gobuster dns -d example.thm -w /path/to/wordlist
-d(required): the target domain to check for subdomains-w(required): the wordlist of subdomains to try
root@tryhackme:~# gobuster dns -d example.thm -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Domain: example.thm
[+] Threads: 10
[+] Timeout: 1s
[+] Wordlist: /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt
===============================================================
Starting gobuster in DNS enumeration mode
===============================================================
Found: www.example.thm
Found: shop.example.thm
Found: academy.example.thm
Found: primary.example.thm
Progress: 4989 / 4990 (99.98%)
===============================================================
Finished
===============================================================
4. Look for virtual hosts
A virtual host is a different website living on the same machine. The server decides which site to serve based on the Host header, so a vhost won't necessarily show up in DNS at all.
The difference between the two gobuster modes:
dnsdoes a DNS lookup on each FQDN made by combining the domain with a wordlist entryvhostsends HTTP requests to the target URL with theHostheader set to each wordlist entry (combined with the domain)
gobuster vhost -u "http://10.64.183.191" --domain example.thm -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain --exclude-length 250-320
-u(required): the target URL to check virtual hosts on-w(required): the wordlist of subdomains to try--append-domain: appends the configured domain to each wordlist entry--exclude-length: filters out responses by size, so you can drop the false positives (the server's default page usually comes back for every miss with the same length)
TL;DR
- nmap everything:
-sC -sV -p- -T4 -O --open - Web server? gobuster
dirit, then gobusterdirwhatever you find - Got a domain? gobuster
dnsandvhostit - Found nothing? Swap wordlists and cast a wider net