Web fuzzing: directories, extensions, subdomains and vhosts with ffuf, gobuster and feroxbuster
Find hidden paths, backups, vhosts and exposed .git repos with ffuf and gobuster: which wordlist to use and how to filter out false positives.
Half of the web footholds in the catalogue start with something that is not linked anywhere: a panel at /admin-old/, a config.php.bak, a dev. subdomain or a forgotten .git folder. Finding it is plain brute force, done properly. This guide covers the three flavours: directories, vhosts and exposed .git. The web enumeration cheat sheet has every command.
Before you fire anything
# If nmap or the browser redirects you to a name, add it to /etc/hosts
echo '10.10.11.20 target.htb' | sudo tee -a /etc/hosts
# See what is there: technologies, headers, robots.txt, sitemap.xml
whatweb http://target.htb
curl -s http://target.htb/robots.txt
The technology decides the extensions: PHP (.php), ASP.NET (.aspx, .asp), Java (.jsp, .do, .action). And always .txt, .bak, .old, .zip.
1. Directories and files
# ffuf: fast, with lots of filters
ffuf -u http://target.htb/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -e .php,.txt,.bak -t 60
# feroxbuster: recursive by default (descends into every folder it finds)
feroxbuster -u http://target.htb -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words.txt -x php,txt
# gobuster
gobuster dir -u http://target.htb -w /usr/share/seclists/Discovery/Web-Content/common.txt -x php,txt -t 50
Which wordlist to use:
Wordlist (SecLists, Discovery/Web-Content/) |
For |
|---|---|
common.txt |
A quick pass (about 4,700 entries) |
raft-medium-directories.txt / raft-medium-words.txt |
The normal pass |
directory-list-2.3-medium.txt |
When the above finds nothing (220,000 entries) |
api/api-endpoints.txt |
APIs |
CMS/ |
WordPress, Joomla, Drupal paths... |
Filtering false positives
If everything returns 200 (or 302 to the login), filter on what the fake responses have in common:
ffuf ... -fs 1532 # size in bytes
ffuf ... -fw 12 # word count
ffuf ... -fl 40 # line count
ffuf ... -fr 'Not Found' # a regex in the body
ffuf ... -ac # let ffuf calibrate the filter itself
Tip
A 403 is not a dead end: it means the path exists. Brute-force inside it (/admin/FUZZ) or try to bypass the restriction (/admin/., /%2e/admin, X-Original-URL: /admin).
2. Subdomains and virtual hosts
One server can serve different sites depending on the Host header. If the main site is target.htb, look for dev.target.htb, admin.target.htb, api.target.htb:
ffuf -u http://target.htb -H 'Host: FUZZ.target.htb' -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt -ac
gobuster vhost -u http://target.htb -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain
Everything that does not exist usually returns the same thing (the main site or a redirect), so the size filter (-fs) or -ac is essential. Every vhost you find goes into /etc/hosts and gets enumerated from scratch.
Note
Vhost fuzzing is not the same as looking for subdomains in DNS. On HTB there is no public DNS: what works is changing the Host header. If the target runs a DNS server (port 53), also try a zone transfer: dig axfr target.htb @10.10.11.20.
Examples: on Devvortex and LinkVortex, the dev. subdomain is the way in; on Forge, admin.forge.htb only answers from inside and needs an SSRF.
3. Exposed .git repositories
If http://target.htb/.git/HEAD returns something like ref: refs/heads/master, you can rebuild the whole source code with its history:
curl -s http://target.htb/.git/HEAD
pipx install git-dumper
git-dumper http://target.htb/.git/ source
cd source && git log --oneline && git show
What to look for: passwords deleted in a later commit (git log -p | grep -i pass), .env files, JWT or session keys, and the application's logic so you can find the bug without guessing. Also check the git history when the repository sits on the machine itself.
4. Other telltale files
/.env /.DS_Store /.svn/entries /.hg/ /backup.zip /www.tar.gz
/web.config /WEB-INF/web.xml /server-status /phpinfo.php /.well-known/
A .DS_Store lists the files in a folder (python3 ds_store_exp.py); on Tomcat, /WEB-INF/web.xml, and on Apache, /server-status, leak internal information when reachable.
Common errors
/FUZZfinds nothing but the browser does: you are missing extensions, or the server treats/adminand/admin/differently. Try-eand a trailing slash.- Results change between runs: you are going too fast and the server throttles or falls over. Lower the threads (
-t 20) or add-rate 50. - Every vhost returns 200: you did not filter by size. Check the size of a response for a made-up name and use
-fswith that value.
What next
With the full map, follow the web checklist: versions of each component (public CVEs), forms (SQL injection, SSTI), uploads (file upload) and APIs (IDOR and mass assignment).