ffuf

Fast web fuzzer written in Go. Discovers hidden directories, files, subdomains, and parameters by brute-forcing with wordlists. The FUZZ keyword goes wherever you want to fuzz.

Linux Web CPTS
LevelBeginner. A good first web tool once you know what a URL and an HTTP status code are.
Know firstWhat a URL path and an HTTP status code are (200 found, 404 not found, 403 forbidden), and what a wordlist is.
You will learn tofind hidden directories, files and virtual hosts, and filter the noise so the real results stand out.

What is it?

ffuf (Fuzz Faster U Fool) sends HTTP requests with a wordlist, replacing the keyword FUZZ with each word. It's the go-to tool for web content discovery, finding hidden pages, directories, API endpoints, subdomains, and parameters that aren't linked from the visible site.

ffuf is faster than Gobuster, more flexible than dirb, and handles edge cases better. It's the standard for CPTS web enumeration.

Run this first

FUZZ is the placeholder ffuf replaces with every word from the wordlist. Put it where the unknown part goes.

bash: your first scan
$ ffuf -u http://10.10.10.10/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt
  • -u is the URL, with FUZZ where the guesses go.
  • -w is the wordlist.

ffuf already hides 404s. Check the first few results: if everything has the same size, the server returns one page for every missing path, so filter that size with -fs or let ffuf work it out with -ac. Add -e .php,.txt to look for files as well as directories.

output: what a scan looks like
admin                   [Status: 301, Size: 313, Words: 20, Lines: 10, Duration: 112ms]  <-- redirect, a real directory
robots.txt              [Status: 200, Size: 42, Words: 5, Lines: 3, Duration: 87ms]
backup                  [Status: 403, Size: 279, Words: 20, Lines: 10, Duration: 95ms]  <-- exists but forbidden, still worth noting
:: Progress: [87664/87664] :: Job [1/1] :: 320 req/sec :: Duration: [0:04:33] :: Errors: 0 ::

When do you use it?

Run ffuf when
  • Nmap shows port 80/443 open → directory and file fuzzing is always step 1 for web
  • You found a web app → fuzz for hidden pages, admin panels, backup files
  • You suspect virtual hosts → subdomain/vhost fuzzing reveals hidden sites on the same server
  • You found an API → fuzz for undocumented endpoints
  • You need to find parameters → parameter fuzzing for GET/POST params
  • You found a login page → fuzz for default creds or valid usernames

How do you use it?

Directory fuzzing

bash: find hidden directories
# Basic directory fuzzing
$ ffuf -u http://10.10.10.10/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt

# With extensions (find files, not just directories)
$ ffuf -u http://10.10.10.10/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -e .php,.html,.txt,.bak,.old,.conf

# 404s are already hidden: the default matcher is 200-299,301,302,307,401,403,405,500
# Use -fc to drop codes from that list, for example a flood of 403s
$ ffuf -u http://10.10.10.10/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -fc 403

# Recursive fuzzing (fuzz inside found directories)
$ ffuf -u http://10.10.10.10/FUZZ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -recursion -recursion-depth 2

Subdomain / vhost fuzzing

bash: find hidden virtual hosts
# Subdomain fuzzing via Host header
$ ffuf -u http://10.10.10.10 -H "Host: FUZZ.corp.local" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt

# The server responds differently for valid vhosts vs invalid ones
# You'll get a flood of results, filter by response size to find unique ones:
$ ffuf -u http://10.10.10.10 -H "Host: FUZZ.corp.local" -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 1234

# -fs 1234 = filter out responses of size 1234 (the default page size)
# Run once without -fs, note the common response size, then add -fs to filter it
How vhost fuzzing works

Many web servers serve different content based on the Host header. A server at 10.10.10.10 might serve the default page for unknown hosts, but a completely different site for admin.corp.local. Vhost fuzzing finds these hidden sites. Always add discovered hostnames to /etc/hosts.

Filtering results

bash: remove noise
# Filter by status code (exclude 403s; 404s never show by default, add -mc all to see them)
$ ffuf -u http://target/FUZZ -w wordlist.txt -fc 403

# Let ffuf calibrate the filter itself, which helps when the server returns 200 for missing pages
$ ffuf -u http://target/FUZZ -w wordlist.txt -ac

# Filter by response size (exclude pages of size 1234)
$ ffuf -u http://target/FUZZ -w wordlist.txt -fs 1234

# Filter by word count
$ ffuf -u http://target/FUZZ -w wordlist.txt -fw 42

# Filter by line count
$ ffuf -u http://target/FUZZ -w wordlist.txt -fl 10

# Match ONLY specific status codes (only show 200 and 301)
$ ffuf -u http://target/FUZZ -w wordlist.txt -mc 200,301

POST request fuzzing (login brute-force)

bash
# Fuzz a login form, try passwords for a known user
$ ffuf -u http://target/login -X POST -d "username=admin&password=FUZZ" -H "Content-Type: application/x-www-form-urlencoded" -w /usr/share/wordlists/rockyou.txt -fc 401

# Fuzz both username and password (two FUZZ keywords)
$ ffuf -u http://target/login -X POST -d "username=W1&password=W2" -H "Content-Type: application/x-www-form-urlencoded" -w users.txt:W1 -w passwords.txt:W2 -fc 401

# W1 and W2 are custom keyword names for multiple wordlists

Parameter fuzzing

bash: find hidden GET/POST parameters
# Fuzz GET parameters
$ ffuf -u 'http://target/page?FUZZ=test' -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 1234

# Fuzz POST parameters
$ ffuf -u http://target/page -X POST -d "FUZZ=test" -H "Content-Type: application/x-www-form-urlencoded" -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs 1234

Useful flags

bash
# Throttle speed (for rate-limited targets)
$ ffuf -u http://target/FUZZ -w wordlist.txt -rate 100

# Set threads (default 40)
$ ffuf -u http://target/FUZZ -w wordlist.txt -t 80

# Add a cookie (for authenticated fuzzing)
$ ffuf -u http://target/FUZZ -w wordlist.txt -b "session=abc123"

# Output to file
$ ffuf -u http://target/FUZZ -w wordlist.txt -o results.json -of json

# Follow redirects
$ ffuf -u http://target/FUZZ -w wordlist.txt -r

# Use a proxy (route through Burp)
$ ffuf -u http://target/FUZZ -w wordlist.txt -x http://127.0.0.1:8080

Which wordlists?

TaskWordlist (SecLists path)
Directory fuzzingDiscovery/Web-Content/directory-list-2.3-small.txt (fast) or -medium.txt (thorough)
File discoveryDiscovery/Web-Content/raft-medium-files.txt
Subdomain/vhostDiscovery/DNS/subdomains-top1million-5000.txt or -20000.txt
ParametersDiscovery/Web-Content/burp-parameter-names.txt
Login brute-force/usr/share/wordlists/rockyou.txt
API endpointsDiscovery/Web-Content/api/objects.txt

What do you do with the output?

  • Found directories: Browse them, look for sensitive files, check for directory listing
  • Found .bak / .old / .conf files: Download them: they often contain credentials or configuration
  • Found admin panels: Try default credentials, check for auth bypass
  • Found subdomains: Add to /etc/hosts and enumerate each one separately
  • Found hidden parameters: Test for injection (SQLi, XSS, SSRF, LFI), sqlmap, Burp Suite
  • Found API endpoints: Check for IDOR, auth bypass, information disclosure

Common mistakes

Watch out
  • Not filtering results: A custom 404 page returns 200 for everything. Always check the first results, if everything returns the same size, filter with -fs
  • Forgetting extensions: /admin might 404 but /admin.php exists. Always add -e .php,.html,.txt,.asp,.aspx based on the tech stack
  • Not fuzzing vhosts: A server might only show a default page on the IP. Vhost fuzzing can reveal entirely different applications
  • Using the wrong wordlist: directory-list-2.3-small.txt is a good start. If it finds nothing, try -medium.txt or a different list entirely
  • Forgetting to add subdomains to /etc/hosts: After finding dev.corp.local, add it: echo "10.10.10.10 dev.corp.local" >> /etc/hosts
  • Too many threads on slow targets: Default 40 threads can overwhelm some targets. Use -rate 50 or -t 10 if getting errors

Check yourself

Answer each question out loud or on paper first, then open it. Retrieving the answer teaches you more than rereading the page.

Every result has status 200 and the same size. What is happening, and what do you do?
The server returns a custom page with status 200 for paths that do not exist. Filter that size with -fs, or use -ac to let ffuf calibrate the filter.
Why do you need the -e flag, and what is an example?
/admin may return 404 while /admin.php exists. -e .php,.html,.txt makes ffuf also try each word with those extensions.
A server shows only a default page on its IP address. What else can you fuzz?
Virtual hosts. Send -H "Host: FUZZ.corp.local" and filter out the default response size. One server can host different applications by hostname.
Which status codes does ffuf show by default, and which flag shows the 404s?
200-299, 301, 302, 307, 401, 403, 405 and 500. -mc all shows every code, including 404.
The target returns errors or slows down. What do you change?
Lower the load with -rate 50 or fewer threads such as -t 10. The default is 40 threads.