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.

· 3 min read

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

  • /FUZZ finds nothing but the browser does: you are missing extensions, or the server treats /admin and /admin/ differently. Try -e and 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 -fs with 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).

Practise it on real machines

Hack The Box machines where this technique is used. Each ▶ jumps to the minute of the IppSec video where it shows up.

Directory brute forcing 42 machines

See all 42 machines →

Vhost and subdomain fuzzing 47 machines

See all 47 machines →

Exposed .git repository 10 machines

See all 10 machines →

Spotted a mistake or something missing? Tell us