> For the complete documentation index, see [llms.txt](https://breakpoint-journal.gitbook.io/breakpoint/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://breakpoint-journal.gitbook.io/breakpoint/tools/ffuf.md).

# Ffuf

A great tool for your web fuzzing toolkit. This page covers tips, tricks, and know-how I've learned with this tool!

I created a very simple store dashboard that runs locally on `http://127.0.0.1:5000` to test my ffuf skills on, find it at <https://github.com/jgbby/store-demo>!

<details>

<summary>Setup Instructions (Linux)</summary>

```bash
┌─[✗]─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $python3 -m venv venv
┌─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $ls
app.py  requirements.txt  static  templates  venv
┌─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $source venv/bin/activate
(venv) ┌─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $pip install -r requirements.txt
...
Successfully installed blinker-1.9.0 click-8.4.1 flask-3.1.3 itsdangerous-2.2.0 jinja2-3.1.6 markupsafe-3.0.3 werkzeug-3.1.8
(venv) ┌─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $python3 app.py
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 157-059-708
127.0.0.1 - - [18/Jun/2026 17:09:35] "GET / HTTP/1.1" 200 -
```

</details>

<details>

<summary>Setup Instructions (Windows)</summary>

{% code title="" %}

```powershell
C:\Users\cainsmith\dev\demos\store>python -m venv venv

C:\Users\cainsmith\dev\demos\store>.\venv\scripts\activate

(venv) C:\Users\cainsmith\dev\demos\store>pip install -r requirements.txt
...
Successfully installed blinker-1.9.0 click-8.4.1 colorama-0.4.6 flask-3.1.3 itsdangerous-2.2.0 jinja2-3.1.6 markupsafe-3.0.3 werkzeug-3.1.8

(venv) C:\Users\cainsmith\dev\demos\store>python app.py

(venv) C:\Users\cainsmith\dev\demos\store>python3 app.py
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 989-800-741
```

{% endcode %}

</details>

I've also cloned [SecLists](https://github.com/danielmiessler/SecLists) locally, a repository that contains a breadth of common directories, file extensions, passwords, usernames, and what have you.

```bash
┌─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $head -n 10 /home/cainsmith/tools/SecLists/Discovery/Web-Content/common_directories.txt
gift-card
gift_card
insurance_applications
insurance-applications
shrdbms
top-categories
top_categories
encryptionkeys
encryption_keys
```

We can use the `common_directories.txt` list to search our web application:

{% code title="" %}

```bash
# -ic removes comments from wordlist
# -w <wordlist_path>:FUZZ
# -u <url>/FUZZ (place FUZZ wherever you want the substitution to occur)
└──╼ $ffuf -ic -w ~/tools/SecLists/Discovery/Web-Content/common_directories.txt:FUZZ -u http://127.0.0.1:5000/FUZZ

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://127.0.0.1:5000/FUZZ
 :: Wordlist         : FUZZ: /home/cainsmith/tools/SecLists/Discovery/Web-Content/common_directories.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200-299,301,302,307,401,403,405,500
________________________________________________

gift-card               [Status: 200, Size: 7553, Words: 1519, Lines: 214, Duration: 4ms]
:: Progress: [10/10] :: Job [1/1] :: 0 req/sec :: Duration: [0:00:00] :: Errors: 0 ::

```

{% endcode %}

Back on the host terminal, you can see the requests come through

```bash
(venv) ┌─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $python3 app.py
 * Serving Flask app 'app'
 * Debug mode: on
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
 * Running on http://127.0.0.1:5000
Press CTRL+C to quit
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 157-059-708
127.0.0.1 - - [18/Jun/2026 17:09:35] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [18/Jun/2026 17:09:35] "GET /favicon.ico HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /gift-card HTTP/1.1" 200 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /gift_card HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /encryption-keys HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /insurance_applications HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /insurance-applications HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /shrdbms HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /top-categories HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /encryptionkeys HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /top_categories HTTP/1.1" 404 -
127.0.0.1 - - [18/Jun/2026 17:22:09] "GET /encryption_keys HTTP/1.1" 404 -
```

You may also combine multiple wordlists together to create multiple fuzzing patterns, note that whatever variable you place after the wordlist specification (e.g. `FUZZ1`, `FUZZ2`), can then be used later on in the fuzzing pattern:

```bash
┌─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $ffuf -ic -w ~/tools/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-big.txt:FUZZ1 -w ~/tools/SecLists/Discovery/Web-Content/web-extensions.txt:FUZZ2 -u http://127.0.0.1:5000/FUZZ1.FUZZ2

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

       v2.1.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://154.57.164.65:31733/FUZZ1.FUZZ2
 :: Wordlist         : FUZZ1: /home/cainsmith/tools/SecLists/Discovery/Web-Content/DirBuster-2007_directory-list-2.3-big.txt
 :: Wordlist         : FUZZ2: /home/cainsmith/tools/SecLists/Discovery/Web-Content/web-extensions.txt
 ...
```

You can also run recursive queries, where, if suppose `http://127.0.0.1:5000/blog` exists, then it'll queue up a job to recursively search down `http://127.0.0.1:5000/blog/index`, `http://127.0.0.1:5000/blog/help`, and so on. You can specify the recursion flag and recursion depth as follows:

```bash
┌─[cainsmith@parrot]─[~/demos/ffuzz]
└──╼ $ffuf --help | grep recursion
  -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)
  -recursion-strategy Recursion strategy: "default" for a redirect based, and "greedy" to recurse on all matches (default: default)
```
