Reconnaissance: A Little CTF Recon Guide

October 11, 2026

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 enumeration
  • dns: DNS subdomain enumeration
  • vhost: virtual host enumeration (you most probably want to use the IP address as the URL)
  • fuzz: replaces the keyword FUZZ in the URL, headers, and request body
  • tftp: TFTP enumeration
  • s3 / 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:

  • dns does a DNS lookup on each FQDN made by combining the domain with a wordlist entry
  • vhost sends HTTP requests to the target URL with the Host header 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

  1. nmap everything: -sC -sV -p- -T4 -O --open
  2. Web server? gobuster dir it, then gobuster dir whatever you find
  3. Got a domain? gobuster dns and vhost it
  4. Found nothing? Swap wordlists and cast a wider net