Skip to content
pexels-madison-inouye-2894955

Fuzz Parameters, Directories & More with Ffuf

The art of fuzzing is a vital skill for any penetration tester or hacker to possess. The faster you fuzz, and the more efficiently you are at doing it, the closer you are to achieving your goal. Ffuf comes in handy to help speed things along and fuzz for parameters, directors, etc.

Table of Contents

What Is Fuzzing?

Fuzzing, or fuzz testing, is the automated process of providing malformed or random data to software to discover bugs. Typically, when it comes to pentesting, a wordlist is used to iterate through values, and the results are observed and analyzed.
Fuzzing usually involves testing input — this can be anything from alphanumeric characters to find buffer overflows, to odd characters to test for SQL injection. Fuzzing is also commonly used to discover hidden directories and files and to determine valid parameter names and values.
We will be using Metasploitable 2 as our target and Kali Linux as our local machine to demonstrate ffuf’s power at fuzzing.

Step 1: Install & Configure Ffuf

The only requirement to run ffuf is having Go installed, which can easily be done on Kali with the package manager.

~$ sudo apt install golang

Reading package lists... Done
Building dependency tree
Reading state information... Done
golang is already the newest version (2:1.14~2).
0 upgraded, 0 newly installed, 0 to remove and 17 not upgraded. 

Next, grab the latest ffuf release from GitHub. At the time of writing, this is version 1.1.0. We can use wget to download it.

~$ wget https://github.com/ffuf/ffuf/releases/download/v1.1.0/ffuf_1.1.0_linux_amd64.tar.gz

--2020-08-27 11:36:41--  https://github.com/ffuf/ffuf/releases/download/v1.1.0/ffuf_1.1.0_linux_amd64.tar.gz
Resolving github.com (github.com)... 140.82.112.4
Connecting to github.com (github.com)|140.82.112.4|:443... connected.
HTTP request sent, awaiting response... 302 Found
Location: https://github-production-release-asset-2e65be.s3.amazonaws.com/156681830/192d4700-cceb-11ea-97f4-adcd48470676?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20200827%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20200827T163641Z&X-Amz-Expires=300&X-Amz-Signature=493a4881a3e960fb7c29baa5ee999efe96bbb5414fd122355b1ec19fe65d1214&X-Amz-SignedHeaders=host&actor_id=0&repo_id=156681830&response-content-disposition=attachment%3B%20filename%3Dffuf_1.1.0_linux_amd64.tar.gz&response-content-type=application%2Foctet-stream [following]
--2020-08-27 11:36:41--  https://github-production-release-asset-2e65be.s3.amazonaws.com/156681830/192d4700-cceb-11ea-97f4-adcd48470676?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=AKIAIWNJYAX4CSVEH53A%2F20200827%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20200827T163641Z&X-Amz-Expires=300&X-Amz-Signature=493a4881a3e960fb7c29baa5ee999efe96bbb5414fd122355b1ec19fe65d1214&X-Amz-SignedHeaders=host&actor_id=0&repo_id=156681830&response-content-disposition=attachment%3B%20filename%3Dffuf_1.1.0_linux_amd64.tar.gz&response-content-type=application%2Foctet-stream
Resolving github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)... 52.217.37.12
Connecting to github-production-release-asset-2e65be.s3.amazonaws.com (github-production-release-asset-2e65be.s3.amazonaws.com)|52.217.37.12|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 3101002 (3.0M) [application/octet-stream]
Saving to: ‘ffuf_1.1.0_linux_amd64.tar.gz’

ffuf_1.1.0_linux_amd64.tar.gz                               100%[========================================================================================================================================>]   2.96M  5.74MB/s    in 0.5s

2020-08-27 11:36:42 (5.74 MB/s) - ‘ffuf_1.1.0_linux_amd64.tar.gz’ saved [3101002/3101002] 

Now we need to extract the contents of the archive.

~$ tar xzf ffuf_1.1.0_linux_amd64.tar.gz 

We should now have the ffuf executable in the current working directory, and we can run it with the dot-slash command.

~$ ./ffuf

Encountered error(s): 2 errors occured.
        * -u flag or -request flag is required
        * Either -w or --input-cmd flag is required

Fuzz Faster U Fool - v1.1.0

HTTP OPTIONS:
  -H               Header `"Name: Value"`, separated by colon. Multiple -H flags are accepted.
  -X               HTTP method to use (default: GET)
  -b               Cookie data `"NAME1=VALUE1; NAME2=VALUE2"` for copy as curl functionality.
  -d               POST data
  -ignore-body     Do not fetch the response content. (default: false)
  -r               Follow redirects (default: false)
  -recursion       Scan recursively. Only FUZZ keyword is supported, and URL (-u) has to end in it. (default: false)
  -recursion-depth Maximum recursion depth. (default: 0)
  -replay-proxy    Replay matched requests using this proxy.
  -timeout         HTTP request timeout in seconds. (default: 10)
  -u               Target URL
  -x               HTTP Proxy URL

GENERAL OPTIONS:
  -V               Show version information. (default: false)
  -ac              Automatically calibrate filtering options (default: false)
  -acc             Custom auto-calibration string. Can be used multiple times. Implies -ac
  -c               Colorize output. (default: false)
  -maxtime         Maximum running time in seconds for entire process. (default: 0)
  -maxtime-job     Maximum running time in seconds per job. (default: 0)
  -p               Seconds of `delay` between requests, or a range of random delay. For example "0.1" or "0.1-2.0"
  -s               Do not print additional information (silent mode) (default: false)
  -sa              Stop on all error cases. Implies -sf and -se. (default: false)
  -se              Stop on spurious errors (default: false)
  -sf              Stop when > 95% of responses return 403 Forbidden (default: false)
  -t               Number of concurrent threads. (default: 40)
  -v               Verbose output, printing full URL and redirect location (if any) with the results. (default: false)

... 

Running it without any arguments will print the help information and some usage examples. Now let’s say we wanted to be able to run this tool from anywhere — all we need to do is move ffuf to any directory in our path.

~$ sudo cp ffuf /usr/local/bin/ 

Now we can run it from anywhere without the need to have it in the current directory.

~$ ffuf -V

ffuf version: 1.1.0 

The last step to get up and running is optional. Having a good set of wordlists is essential for any security professional, and there is a collection called SecLists that has just about anything you need. It is available on GitHub, but we can also install it locally on our machine.

~$ sudo apt install seclists 

Step 2: Perform Some Basic Fuzzing

At the most basic level, we can use ffuf to fuzz for hidden directories or files. There are tools like gobuster out there that are made for this specific purpose, but using something like ffuf has its use cases.

For example, let’s say you’re testing a website that has some sort of rate-limiting in place. With other tools, it can sometimes be challenging to get them to go slower, and this is precisely where tools like ffuf come into play since we can more finely control the rate and timing options. More on that later.

Simply provide a wordlist with the -w flag, the URL with the -u flag, and put FUZZ where we want to insert our fuzzing.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]
.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]
config                  [Status: 301, Size: 319, Words: 21, Lines: 10]
docs                    [Status: 301, Size: 317, Words: 21, Lines: 10]
about                   [Status: 302, Size: 0, Words: 1, Lines: 1]
external                [Status: 301, Size: 321, Words: 21, Lines: 10]
favicon.ico             [Status: 200, Size: 1405, Words: 5, Lines: 2]
php.ini                 [Status: 200, Size: 148, Words: 17, Lines: 5]
index                   [Status: 302, Size: 0, Words: 1, Lines: 1]
robots                  [Status: 200, Size: 26, Words: 3, Lines: 2]
robots.txt              [Status: 200, Size: 26, Words: 3, Lines: 2]
instructions            [Status: 302, Size: 0, Words: 1, Lines: 1]
index.php               [Status: 302, Size: 0, Words: 1, Lines: 1]
logout                  [Status: 302, Size: 0, Words: 1, Lines: 1]
phpinfo                 [Status: 302, Size: 0, Words: 1, Lines: 1]
login                   [Status: 200, Size: 1289, Words: 83, Lines: 66]
phpinfo.php             [Status: 302, Size: 0, Words: 1, Lines: 1]
setup                   [Status: 200, Size: 3549, Words: 182, Lines: 81]
security                [Status: 302, Size: 0, Words: 1, Lines: 1]
:: Progress: [4658/4658] :: Job [1/1] :: 388 req/sec :: Duration: [0:00:12] :: Errors: 0 :: 
You’ll notice the usage is very similar to wfuzz, so new users of the tool will feel somewhat familiar with its operation.
After the nice little banner, we can see the request method, URL, and some other options that are set. When ffuf comes across something in the wordlist, it will give us the name of the file or directory, the HTTP status code, and some information about the request length.

We can also include any necessary cookies in our request using the -b flag.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -b "PHPSESSID=a4885a1d1802209109693054d94ae214; security=low" -u http://10.10.0.50/dvwa/FUZZ

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Header           : Cookie: PHPSESSID=a4885a1d1802209109693054d94ae214; security=low
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]

... 
Along the same lines, we can include any custom headers we want with the -H flag.
~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -H "Host: 10.10.0.50" -u http://10.10.0.50/dvwa/FUZZ

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Header           : Host: 10.10.0.50
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]
README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]

... 

Instead of doing the default GET request, we can also send POST requests. Use the -X flag to specify the request type, in this case, POST, and include the data for the request with the -d flag.

~$ ffuf -w /usr/share/seclists/Passwords/darkweb2017-top100.txt -X POST -d "username=admin\&password=FUZZ\&Login=Login" -u http://10.10.0.50/dvwa/login.php

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : POST
 :: URL              : http://10.10.0.50/dvwa/login.php
 :: Wordlist         : FUZZ: /usr/share/seclists/Passwords/darkweb2017-top100.txt
 :: Data             : username=admin\&password=FUZZ\&Login=Login
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

123abc                  [Status: 200, Size: 1289, Words: 83, Lines: 66]
123456789               [Status: 200, Size: 1289, Words: 83, Lines: 66]
123321                  [Status: 200, Size: 1289, Words: 83, Lines: 66]

... 

We can use ffuf to fuzz for parameters as well — simply replace the parameter name to fuzz for with the FUZZ keyword.

~$ ffuf -w /usr/share/seclists/Fuzzing/fuzz-Bo0oM.txt -u http://10.10.0.50/dvwa/instructions.php?FUZZ=readme

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/instructions.php?FUZZ=readme
 :: Wordlist         : FUZZ: /usr/share/seclists/Fuzzing/fuzz-Bo0oM.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

!.htpasswd              [Status: 302, Size: 0, Words: 1, Lines: 1]
.AppleDouble            [Status: 302, Size: 0, Words: 1, Lines: 1]
.AppleDesktop           [Status: 302, Size: 0, Words: 1, Lines: 1]
.bak                    [Status: 302, Size: 0, Words: 1, Lines: 1]
!.htaccess              [Status: 302, Size: 0, Words: 1, Lines: 1]

... 

Fuzzing for parameter values works the same way.

~$ ffuf -w /usr/share/seclists/Fuzzing/fuzz-Bo0oM.txt -u http://10.10.0.50/dvwa/instructions.php?doc=FUZZ

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/instructions.php?FUZZ=readme
 :: Wordlist         : FUZZ: /usr/share/seclists/Fuzzing/fuzz-Bo0oM.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

!.htpasswd              [Status: 302, Size: 0, Words: 1, Lines: 1]
.AppleDouble            [Status: 302, Size: 0, Words: 1, Lines: 1]
.AppleDesktop           [Status: 302, Size: 0, Words: 1, Lines: 1]
.bak                    [Status: 302, Size: 0, Words: 1, Lines: 1]
!.htaccess              [Status: 302, Size: 0, Words: 1, Lines: 1]

... 

Step 3: Try the Filtering & Timing Options

Ffuf can perform matching and filtering, depending on what you want to see in the results. For instance, if we only wanted to see results with a 200 status code, we could use the -mc switch to match.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -mc 200

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200
________________________________________________

README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]
favicon.ico             [Status: 200, Size: 1405, Words: 5, Lines: 2]
php.ini                 [Status: 200, Size: 148, Words: 17, Lines: 5]
robots                  [Status: 200, Size: 26, Words: 3, Lines: 2]
robots.txt              [Status: 200, Size: 26, Words: 3, Lines: 2]

... 

On the flip side, we can also filter certain status codes using the -fc switch.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -fc 403

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
 :: Filter           : Response status: 403
________________________________________________

README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]
config                  [Status: 301, Size: 319, Words: 21, Lines: 10]
docs                    [Status: 301, Size: 317, Words: 21, Lines: 10]
external                [Status: 301, Size: 321, Words: 21, Lines: 10]
favicon.ico             [Status: 200, Size: 1405, Words: 5, Lines: 2]
php.ini                 [Status: 200, Size: 148, Words: 17, Lines: 5]
about                   [Status: 302, Size: 0, Words: 1, Lines: 1]

... 

This will hide any results with a 403 status code. Multiple codes for wither matching or filtering can be used as long as they are comma-separated.

We can perform similar matching and filtering with request size and the number of words or lines. For example, to filter any results returning with request size 0, do the following.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -fs 0

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
 :: Filter           : Response size: 0
________________________________________________

.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]
README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]
config                  [Status: 301, Size: 319, Words: 21, Lines: 10]
docs                    [Status: 301, Size: 317, Words: 21, Lines: 10]
external                [Status: 301, Size: 321, Words: 21, Lines: 10]
favicon.ico             [Status: 200, Size: 1405, Words: 5, Lines: 2]
.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
php.ini                 [Status: 200, Size: 148, Words: 17, Lines: 5]

... 

Ffuf has some additional features to control timing of requests as well. To set a timeout for each individual request, use the -timeout option (default is 10 seconds).

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -timeout 5

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 5
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]
.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]
config                  [Status: 301, Size: 319, Words: 21, Lines: 10]

... 

We can also set a delay between each request with the -p flag. For example, to delay 2 seconds between requests, try the following.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -p 2

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Delay            : 2.00 seconds
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]

... 
This is extremely useful in situations where rate limiting is in place, or when we don’t want to hammer a site with requests.

Another handy feature is the ability to set a maximum time for ffuf to run — this is useful when using a large wordlist, and you don’t want to wait around all day for it to finish. Use the -maxtime option followed by the number of seconds for ffuf to run before exiting.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -maxtime 60

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]
README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]

... 

If we want to run faster, we can set the number of threads to use (default is 40).

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -t 60

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 60
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]
.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]
.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
config                  [Status: 301, Size: 319, Words: 21, Lines: 10]

... 

For simpler viewing in the terminal, we can use the -s flag to only print the found objects and none of the other noise.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -s

.htpasswd
README
config
docs
external
favicon.ico
about

... 

This is useful if we wanted to grep any output or use the results in a script or something, not to mention it’s just a bit cleaner.

We can also save any results to a file using the -o switch.

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -o results.txt

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Output file      : results.txt
 :: File format      : json
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]
README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]

... 

The default format is JSON, but we can change that with the -of flag. For example, to save the results in HTML format, try:

~$ ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u http://10.10.0.50/dvwa/FUZZ -o results.txt -of html

        /'___\  /'___\           /'___\
       /\ \__/ /\ \__/  __  __  /\ \__/
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
         \ \_\   \ \_\  \ \____/  \ \_\
          \/_/    \/_/   \/___/    \/_/

       v1.1.0
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.0.50/dvwa/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/common.txt
 :: Output file      : results.txt
 :: File format      : html
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403
________________________________________________

.htaccess               [Status: 403, Size: 297, Words: 22, Lines: 11]
.hta                    [Status: 403, Size: 292, Words: 22, Lines: 11]
.htpasswd               [Status: 403, Size: 297, Words: 22, Lines: 11]
README                  [Status: 200, Size: 4934, Words: 637, Lines: 120]

... 

Wrapping up

In this tutorial, we learned a bit about fuzzing and how to use a tool called ffuf to fuzz for directories, parameters, and more. First, we installed the tool and configured it to run on our system. Next, we covered some basic fuzzing, including fuzzing GET requests, POST requests, and parameters. Finally, we concluded with some filtering and timing options for more fine-grained control. Hopefully, you find ffuf as valuable as I do!

You might be interested in reading these too: