Fuzzers are well-known tools for pentesters. They allow us to inject unexpected inputs into an application to see how it responds. In this case, we will use FFuF , a web fuzzer that will not only allow us to see how the application responds, but also help us discover paths and API endpoints.
We install the fuzzer:
When we fuzz, we will substitute parts of the requests with others that we choose. A good list of words to check can be found here:
FFuF can be linked to a proxy that we have running, so that only requests that have returned an allowed HTTP code, by default: 200,204,301,302,307,401,403,405,500, will be sent to this proxy:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0-dev
________________________________________________
:: Method : GET
:: URL : https://XXXXXXXXXX.com/FUZZ
:: Wordlist : FUZZ: actions.txt
:: Output file : output.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: ReplayProxy : http://192.168.69.204:8081
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
admin [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 190ms]
:: Progress: [222/222] :: Job [1/1] :: 120 req/sec :: Duration: [0:00:02] :: Errors: 0 ::
We can see how Burp has only received the request that returned a 302, not the 222:
If the application requires the use of cookies, we can forge them as follows:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0-dev
________________________________________________
:: Method : GET
:: URL : https://XXXXXXXXXX.com/FUZZ
:: Wordlist : FUZZ: actions.txt
:: Header : Cookie: Cookie: _ga=GA1.2.135180862.1669714648; _gid=GA1.2.334284534.1669893254; PHPSESSID=cmh79qfsgolamu70lhcvkb4dmi; acceptCookie=1; _gat_gtag_UA_11506612_2=1
:: Output file : output.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: ReplayProxy : http://192.168.69.204:8081
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
admin [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 921ms]
:: Progress: [222/222] :: Job [1/1] :: 31 req/sec :: Duration: [0:00:07] :: Errors: 0 ::
We can see how the request carries the indicated cookie:
Through FFuF, we can also fuzz HTTP headers:
We can see how the requests carry the fuzzed user-agent:
If we have any kind of traffic restriction by the web server, traffic balancer, WAF, or any other mechanism, we can limit the requests per second using the -r parameter:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0-dev
________________________________________________
:: Method : GET
:: URL : https://XXXXXXXXXX.com/FUZZ
:: Wordlist : FUZZ: actions.txt
:: Output file : output.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: ReplayProxy : http://192.168.69.204:8081
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
admin [Status: 302, Size: 0, Words: 1, Lines: 1, Duration: 192ms]
:: Progress: [222/222] :: Job [1/1] :: 2 req/sec :: Duration: [0:00:52] :: Errors: 0 ::
It is also possible to fuzz two parameters simultaneously:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0-dev
________________________________________________
:: Method : GET
:: URL : https://XXXXXXXXXX.com/FUZZ/SECOND
:: Wordlist : FUZZ: actions.txt
:: Wordlist : SECOND: actions.txt
:: Output file : output.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: ReplayProxy : http://192.168.69.204:8081
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
:: Progress: [49284/49284] :: Job [1/1] :: 119 req/sec :: Duration: [0:06:58] :: Errors: 0 ::
The results returned by FFuF can be filtered in two different ways.
Matchers: Show requests/responses whose pattern matches.
MATCHER OPTIONS:
-mc Match HTTP status codes, or "all" for everything. (default: 200,204,301,302,307,401,403,405,500)
-ml Match amount of lines in response
-mmode Matcher set operator. Either of: and, or (default: or)
-mr Match regexp
-ms Match HTTP response size
-mt Match how many milliseconds to the first response byte, either greater or less than. EG: >100 or <100
-mw Match amount of words in response
Filters: Eliminate requests/responses whose pattern matches.
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
-fmode Filter set operator. Either of: and, or (default: or)
-fr Filter regexp
-fs Filter HTTP response size. Comma separated list of sizes and ranges
-ft Filter by number of milliseconds to the first response byte, either greater or less than. EG: >100 or <100
-fw Filter by amount of words in response. Comma separated list of word counts and ranges
In this example, we are only going to match 302 responses:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0-dev
________________________________________________
:: Method : GET
:: URL : https://XXXXXXXXXX.com/FUZZ
:: Wordlist : FUZZ: actions.txt
:: Output file : output.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: ReplayProxy : http://192.168.69.204:8081
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
:: Filter : Response status: 302
________________________________________________
:: Progress: [222/222] :: Job [1/1] :: 119 req/sec :: Duration: [0:00:02] :: Errors: 0 ::
Sometimes the page responds with a 200 but an error message such as:
{ 'not found': true }
In such a case, we can discard these responses with the following filter:
Of course, FFuF also allows us to send POST requests. If we intercept a login, we can see the following fields posted:
We are going to fuzz the validate field:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0-dev
________________________________________________
:: Method : POST
:: URL : https://XXXXXXXXXX.com/cliente
:: Wordlist : FUZZ: actions.txt
:: Data : nick=kr0m%40alfaexploit.com&pwd=123PASSWORD123&recordarme=1&validar=FUZZ
:: Output file : output.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: ReplayProxy : http://192.168.69.204:8081
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
:: Progress: [222/222] :: Job [1/1] :: 8 req/sec :: Duration: [0:00:24] :: Errors: 0 ::
In Burp, we can see the sent requests:
A very interesting aspect of FFuF is that it does not limit us to fuzzing the values of existing parameters, but we can also fuzz the parameters themselves:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0-dev
________________________________________________
:: Method : POST
:: URL : https://XXXXXXXXXX.com/cliente
:: Wordlist : FUZZ: actions.txt
:: Data : nick=kr0m%40alfaexploit.com&pwd=123PASSWORD123&recordarme=1&validar=&FUZZ=valor_inventado
:: Output file : output.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: ReplayProxy : http://192.168.69.204:8081
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
:: Progress: [222/222] :: Job [1/1] :: 5 req/sec :: Duration: [0:00:38] :: Errors: 0 ::
In Burp, we can see the sent requests:
We can also post Json parameters:
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v1.5.0-dev
________________________________________________
:: Method : POST
:: URL : https://XXXXXXXXXX.com/cliente
:: Wordlist : FUZZ: actions.txt
:: Header : Content-Type: application/json
:: Data : {'FUZZ':'kr0m%40alfaexploit.com'}
:: Output file : output.txt
:: File format : json
:: Follow redirects : false
:: Calibration : false
:: ReplayProxy : http://192.168.69.204:8081
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________
:: Progress: [222/222] :: Job [1/1] :: 5 req/sec :: Duration: [0:00:37] :: Errors: 0 ::
In Burp, we can see the sent requests: