Skip to content

ffuf

ffuf stands for Fuzz Faster U Fool. It's a tool used for web enumeration, fuzzing, and directory brute forcing.

Installation

ffuf is included with many security oriented distributions. In most cases apt install ffuf is enough to install it.

Since ffuf is a go application you simply install it with go (after you've installed golang) like go get -u github.com/ffuf/ffuf

Basics

Minimal required options are -u for the URL and -w for the wordlist. FFUZ is the default keyword. It tell ffuf where to the entries from the wordlist will be injected.

fuff -u http://<IP_OR_URL>/FFUZ -w wordlist.txt

This command "fuzzes" or brute-forces all the entries from a wordlist and checks the returned HTTP Codes from the URL.

The default keyword can be changed by adding :<KEYWORD>at the end of the wordlist.

fuff -u http://<IP_OR_URL>/FUZZTHIS -w wordlist.txt:FUZZTHIS

The option to change the keyword can be used to to use more than one wordlist

fuff -u http://<IP_OR_URL>/FUZZTHIS/FUZZTHAT -w wordlist.txt:FUZZTHIS -w another_wordlist:FUZZTHAT

Some wordlists have comments like copyright notices, comments or notes included. To ignore those comments use -ic.

Finding Pages

Extension Fuzzing

If we don't know the technology used and we assume most of the pages have a index page index.ext we can do a quick check with a small extensions wordlist before we use a big, generic wordlist.

1
ffuf -u http://10.10.167.75/indexFUZZ -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
└─# ffuf -u http://10.10.167.75/indexFUZZ -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt

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

       v1.3.1 Kali Exclusive <3
________________________________________________

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

.php                    [Status: 302, Size: 0, Words: 1, Lines: 1]
.phps                   [Status: 403, Size: 289, Words: 21, Lines: 11]
:: Progress: [39/39] :: Job [1/1] :: 11 req/sec :: Duration: [0:00:05] :: Errors: 0 ::

We can see the technology uses is php.

Page fuzzing

After we get the extensions/technology used, we can try to find pages with those extensions. We can find pages by using the option -e and adding the extension we want to search for (including the .). There are wordlists that include generic filenames including with all the possible extensions (e.g. raft-medium-files-lowercase.txt). If we know the technology used, we can simply add the option -e with the extensions we want to search for and use a simple wordlist without extensions (e.g. raft-medium-words-lowercase.txt). We can also search for some generic extensions/ For example for a php-powered website we could use the extensions .php, .txt, .cfg, .conf.

ffuf -u http://10.10.167.75/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt -e .php,.txt,.cfg, .conf

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
└─# ffuf -u http://10.10.167.75/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt -e .php,.txt,.cfg,.conf -fc 403

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

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.167.75/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-medium-words-lowercase.txt
 :: Extensions       : .php .txt .cfg .conf 
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response status: 403
________________________________________________

login.php               [Status: 200, Size: 1523, Words: 89, Lines: 77]
index.php               [Status: 302, Size: 0, Words: 1, Lines: 1]
logout.php              [Status: 302, Size: 0, Words: 1, Lines: 1]
config                  [Status: 301, Size: 312, Words: 20, Lines: 10]
docs                    [Status: 301, Size: 310, Words: 20, Lines: 10]
about.php               [Status: 200, Size: 4840, Words: 331, Lines: 109]
.                       [Status: 302, Size: 0, Words: 1, Lines: 1]
external                [Status: 301, Size: 314, Words: 20, Lines: 10]
setup.php               [Status: 200, Size: 4066, Words: 308, Lines: 123]
robots.txt              [Status: 200, Size: 26, Words: 3, Lines: 2]
security.php            [Status: 302, Size: 0, Words: 1, Lines: 1]
phpinfo.php             [Status: 302, Size: 0, Words: 1, Lines: 1]
instructions.php        [Status: 200, Size: 14014, Words: 1484, Lines: 263]

Finding Directories

Directories are not technology-dependent and you can use a simple directory list

1
ffuf -u http://10.10.167.75/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories-lowercase.txt

Recursive fuzzing

A directory can contain other/more directories (login/user/) and, manually, we would have to fuzz again by starting a new instance of ffuf with a new url. We can automate this with recursive scanning. The flag for recursive fuzzing is -recursiion. We also have to add the depth with the flag -recursion-depth. For example, if we define -recursion-depth 1 it will only fuzz the main directory and direc subdirectories. We should always use the -v flag so we can see the full URL. Otherwise, if we fuzz for files, it could be hard to determine what file belongs to what directory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
└─# ffuf -u http://188.166.173.208:30769/FUZZ -recursion -recursion-depth 1 -c -w /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt -e .php -fc 403 -v

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

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://188.166.173.208:30769/FUZZ
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/raft-small-directories-lowercase.txt
 :: Extensions       : .php 
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response status: 403
________________________________________________

[Status: 301, Size: 327, Words: 20, Lines: 10]                                                                                                                                                                                                                                                                               
| URL | http://188.166.173.208:30769/forum
| --> | http://188.166.173.208:30769/forum/
    * FUZZ: forum

[INFO] Adding a new job to the queue: http://188.166.173.208:30769/forum/FUZZ

[Status: 301, Size: 326, Words: 20, Lines: 10]                                                                                                                                                                                                                                                                               
| URL | http://188.166.173.208:30769/blog
| --> | http://188.166.173.208:30769/blog/
    * FUZZ: blog

[INFO] Adding a new job to the queue: http://188.166.173.208:30769/blog/FUZZ

[Status: 200, Size: 986, Words: 423, Lines: 56]                                                                                                                                                                                                                                                                              
| URL | http://188.166.173.208:30769/index.php
    * FUZZ: index.php

[Status: 200, Size: 986, Words: 423, Lines: 56]                                                                                                                                                                                                                                                                              
| URL | http://188.166.173.208:30769/
    * FUZZ: 

[INFO] Starting queued job on target: http://188.166.173.208:30769/forum/FUZZ

[Status: 200, Size: 0, Words: 1, Lines: 1]                                                                                                                                                                                                                                                                                   
| URL | http://188.166.173.208:30769/forum/index.php
    * FUZZ: index.php

[Status: 200, Size: 21, Words: 1, Lines: 1]                                                                                                                                                                                                                                                                                  
| URL | http://188.166.173.208:30769/forum/flag.php
    * FUZZ: flag.php

[Status: 200, Size: 0, Words: 1, Lines: 1]                                                                                                                                                                                                                                                                                   
| URL | http://188.166.173.208:30769/forum/
    * FUZZ: 

[INFO] Starting queued job on target: http://188.166.173.208:30769/blog/FUZZ

[Status: 200, Size: 1046, Words: 438, Lines: 58]                                                                                                                                                                                                                                                                             
| URL | http://188.166.173.208:30769/blog/home.php
    * FUZZ: home.php
...

Filters

You can filter with matches and filters. matches are inclusive. That means they display only values you define. On the other hand filters are exclusive. That means everything except the values you add are displayed.

1
2
# Everything but the status code 403 responses are displayed
ffuf -u http://10.10.105.129/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files-lowercase.txt -fc 403
1
2
# Only status code 200 responses are displayed
ffuf -u http://10.10.105.129/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files-lowercase.txt -mc 200

You can add more values to the filter and matches, for example -mc 200,301,302 displays only responses with 200, 301 or 302 HTTP status codes.

Sometimes we get a lot of 403 responses for files that don't exist, especially for files with a . in front of them like .htdocs, .php. Instead of excluding 403 codes entirely, and probably miss some important files, we can filter those files with the regex-filter.

1
ffuf -u http://10.10.105.129/FUZZ -w /usr/share/seclists/Discovery/Web-Content/raft-medium-files-lowercase.txt -fr '/\..*'

Filter and matcher options

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
MATCHER OPTIONS:
  -mc                 Match HTTP status codes, or "all" for everything. (default: 200,204,301,302,307,401,403,405)
  -ml                 Match amount of lines in response
  -mr                 Match regexp
  -ms                 Match HTTP response size
  -mw                 Match amount of words in response

FILTER OPTIONS:
  -fc                 Filter HTTP status codes from response. Comma separated list of codes and ranges
  -fl                 Filter by amount of lines in response. Comma separated list of line counts and ranges
  -fr                 Filter regexp
  -fs                 Filter HTTP response size. Comma separated list of sizes and ranges
  -fw                 Filter by amount of words in response. Comma separated list of word counts and ranges

Parameter fuzzing

Sometimes we don't know what parameters are accepted at an API endpoint or URL. We can fuzz for them.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
└─# ffuf -u 'http://10.10.18.208/sqli-labs/Less-1/?FUZZ=1' -c -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt       

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

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.18.208/sqli-labs/Less-1/?FUZZ=1
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
________________________________________________

title                   [Status: 200, Size: 691, Words: 39, Lines: 29]
file                    [Status: 200, Size: 691, Words: 39, Lines: 29]
type                    [Status: 200, Size: 691, Words: 39, Lines: 29]
password                [Status: 200, Size: 691, Words: 39, Lines: 29]
content                 [Status: 200, Size: 691, Words: 39, Lines: 29]
action                  [Status: 200, Size: 691, Words: 39, Lines: 29]
c                       [Status: 200, Size: 691, Words: 39, Lines: 29]

We can see that every response is 200. It's not possible every single parameter from the wordlist is a valid parameter. A valid parameter has to have more or less than 39 words or a different size than 691. So we use the filter -fw 39 to filter out responses what are 39 words long.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
└─# ffuf -u 'http://10.10.18.208/sqli-labs/Less-1/?FUZZ=1' -c -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fw 39

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

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : GET
 :: URL              : http://10.10.18.208/sqli-labs/Less-1/?FUZZ=1
 :: Wordlist         : FUZZ: /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response words: 39
________________________________________________

id                      [Status: 200, Size: 721, Words: 37, Lines: 29]
:: Progress: [2588/2588] :: Job [1/1] :: 688 req/sec :: Duration: [0:00:06] :: Errors: 0 ::

Filtering out responses with 39 words we get the correct parameter id

We can use seclists word-wordlist if we didn't find anything. It's a bigger list than burp's list (Discovery/Web-Content/raft-medium-words-lowercase.txt).

Piping into ffuz

After we found the parameter we can fuzz the values. Take a look at the URL and we can see that the ID has to be a integer value. We can use or create a list and save it to a file or, since the values are simple integers, we can create a list in the console and pipe it to ffuf with the paramater -w -.

Below are 5 different examples how to create a list [0-255] and pipe it to ffuf.

1
2
3
4
5
$ ruby -e '(0..255).each{|i| puts i}' | ffuf -u 'http://MACHINE_IP/sqli-labs/Less-1/?id=FUZZ' -c -w - -fw 33
$ ruby -e 'puts (0..255).to_a' | ffuf -u 'http://MACHINE_IP/sqli-labs/Less-1/?id=FUZZ' -c -w - -fw 33
$ for i in {0..255}; do echo $i; done | ffuf -u 'http://MACHINE_IP/sqli-labs/Less-1/?id=FUZZ' -c -w - -fw 33
$ seq 0 255 | ffuf -u 'http://MACHINE_IP/sqli-labs/Less-1/?id=FUZZ' -c -w - -fw 33
$ cook '[0-255]' | ffuf -u 'http://MACHINE_IP/sqli-labs/Less-1/?id=FUZZ' -c -w - -fw 33

Again, we have to look at the response and filter out false positives with -fw.

Brute-forcing passwords

We can also brute-force passwords. We can look at burp how the header looks like and use a password list like below. We yest again get a lot of false positives so we filter them out by size -fs

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
└─# ffuf -u http://10.10.18.208/sqli-labs/Less-11/ -c -w /usr/share/seclists/Passwords/Leaked-Databases/hak5.txt -X POST -d 'uname=Dummy&passwd=FUZZ&submit=Submit' -fs 1435 -H 'Content-Type: application/x-www-form-urlencoded' 

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

       v1.3.1 Kali Exclusive <3
________________________________________________

 :: Method           : POST
 :: URL              : http://10.10.18.208/sqli-labs/Less-11/
 :: Wordlist         : FUZZ: /usr/share/seclists/Passwords/Leaked-Databases/hak5.txt
 :: Header           : Content-Type: application/x-www-form-urlencoded
 :: Data             : uname=Dummy&passwd=FUZZ&submit=Submit
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405
 :: Filter           : Response size: 1435
________________________________________________

p@ssword                [Status: 200, Size: 1526, Words: 100, Lines: 50]
:: Progress: [2351/2351] :: Job [1/1] :: 663 req/sec :: Duration: [0:00:06] :: Errors: 0 ::

VHOSTS and subdomains

There are better tools to enumerate vhost and subdomains but we can try it with ffuf as well.

1
2
3
4
# subdomain fuzzing
ffuf -u http://FUZZ.mydomain.com -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -fs 0
# vhost fuzzing, FFUZ in the header
ffuf -u http://mydomain.com -c -w  /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt -H  'Host: FUZZ.mydomain.com' -fs 0

For subdomains we place the FFUZ keyword in front of the domain. If we look for vhosts, we need to send the keyword as a header.

Proxying

Whether it' for network pivoting or for using BurpSuite plugins you can send all the ffuf traffic through a web proxy (HTTP or SOCKS5).

1
$ ffuf -u http://10.10.18.208/ -c -w /usr/share/seclists/Discovery/Web-Content/common.txt -x http://127.0.0.1:8080

It's also possible to send only matches to your proxy for replaying:

1
$ ffuf -u http://10.10.18.208/ -c -w /usr/share/seclists/Discovery/Web-Content/common.txt -replay-proxy http://127.0.0.1:8080

This may be useful if you don't need all the traffic to traverse an upstream proxy and want to minimize resource usage or to avoid polluting your proxy history.

Resources

github: https://github.com/ffuf/ffuf

HTBA: https://academy.hackthebox.com/module/details/54

THM: https://tryhackme.com/room/ffuf

YT (hackersploit): https://www.youtube.com/watch?v=9Hik0xy9qd0

Articles: https://www.hackingarticles.in/comprehensive-guide-on-ffuf/

Comments

Any feedback and suggestions are welcome. This website was created using mkdocs and the material plugin. If you want, you can make a pull request. The repository is https://github.com/dabonzo/itsec_hp