In today’s post, I’ll introduce you to a tool that should be a part of every bug hunter’s toolkit, wfuzz
! I use wfuzz
in every bug bounty program I participate in. If you don’t know what wfuzz
is, it’s a web application fuzzer. And if you haven’t heard of a web application fuzzer, they’re a type of tool that automates web requests.
As you’ll see in this post, fuzzers are a simple yet extremely powerful tool and by the end of this reading you’ll be able to confidently use wfuzz
, one of the best in the game!
Practical Examples
Instead of teaching you the syntax, I’m going to run you through a series of hypothetical, real-world examples so you can use it in your next pentest or bug hunt.
For simplicity, I’m going to use a popular repository of wordlists called SecLists. These wordlists were put together by a few well-known security professionals including Daniel Miessler and Jason Haddix. I highly recommend you check out the repo and get familiar with what’s in some of the lists.
Directory Discovery
Discovering hidden files or directories of a web application can be a gold mine for security testers. wfuzz
makes this a snap by fuzzing the target url:
wfuzz -c -w raft-small-directories-lowercase.txt -u "https://example.com/FUZZ"
Let’s break this down:
-c
, colored output to make output easier to read-w
, the specified wordlist-u
, the url to fuzz
You might have noticed the word FUZZ
in the URL. This is the keyword that tells wfuzz
where to fuzz, or supply all the lines from the wordlist.
In this example, the output would look something like this:
********************************************************
* Wfuzz 2.4.5 - The Web Fuzzer *
********************************************************
Target: https://example.com/FUZZ
Total requests: 20116
===================================================================
ID Response Lines Word Chars Payload
===================================================================
000000007: 404 0 L 0 W 0 Ch "cache"
000000008: 200 0 L 0 W 0 Ch "media"
000000009: 404 0 L 0 W 0 Ch "js"
000000001: 200 0 L 0 W 0 Ch "cgi-bin"
000000002: 404 0 L 0 W 0 Ch "images"
...
As you can see, wfuzz
neatly prints out the response code, payload used, as well as the number of lines, words, and characters in the response.
To make the output a little cleaner, I can filter it based on any of the response attributes, like status code:
$ wfuzz -c -w raft-small-directories-lowercase.txt -u "https://example.com/FUZZ" --sc 200
...
000000008: 200 0 L 0 W 0 Ch "media"
000000001: 200 0 L 0 W 0 Ch "cgi-bin"
...
Now all that is shown are responses with status code 200
. We can also use use -
-/sl/sw/sh/ss<number> for only showing responses with a specific number of lines, words, characters or that match a specific regex respectively.
Alternatively, you can replace --s
with --h
to hide matches rather than showing them.
This one-liner can lead to some big finds, including configuration files, admin logins, and much more depending on the wordlist.
Wordlist Contexts
A little bit of recon with a tool like wappalyzer
can go a long way. Knowing what Content Management System (think WordPress or Drupal), API endpoint locations, etc. can result in more findings.
For example, if I knew my target was running on top of an Nginx server, I might use a wordlist catered towards default files and directories:
wfuzz -c -w nginx.txt -u "https://example.com/FUZZ" --sc 200,403
If you uncovered an API endpoint, you can use a wordlist of common object names (like user
, team
, admin
, etc.):
wfuzz -c -w /api/objects.txt -u "https://api.example.com/v2/FUZZ" --hl 4037
Using a wordlist that is right for the situation makes it more likely that you’ll find something interesting. Don’t just blindly use random wordlists and there’s no one wordlist to rule them all!
Subdomain Bruteforcing
Finding subdomains is key to expanding your attack surface. More often than not, the most interesting subdomains aren’t found passively.
wfuzz
can fuzz anywhere the FUZZ
keyword is located in a request, including in headers.
To bruteforce subdomains with wfuzz
, fuzz in the Host
header:
wfuzz -c -w /Discovery/DNS/deepmagic.com-prefixes-top500.txt -H "Host: FUZZ.example.com" -u "https://example.com"
While wfuzz
is capable, sometimes using a tool that is specifically built for this (like gobuster
or amass
) might be easier and yield better results.
IDOR
wfuzz
offers a variety of payloads that can be used to fuzz with, including a list of numbers.
If I stumbled upon a an id
parameter in JSON that used a guessable four digit number, I might fuzz it like so to see if I can access any accounts I don’t own:
wfuzz -c -z range,0000-9999 -X POST -H "Content-type: application/json" "https://example.com/myaccount" -d '{"id": "FUZZ"}'
The -z
parameter is used to specify a payload type and the payload options. You can see the full list of payload’s with wfuzz -e payloads
.
You might also notice that I changed the method from the default GET
to POST
with the -X
option and added a body to my request with -d
. This syntax should be familiar with you if you’ve every used curl
before. Another reason why I love wfuzz
!
Injection
If you’ve ever tested for injection vulnerabilities (like SQLi or command injection) then it is likely that your attempts have been blocked by a WAF (Web Application Firewall). WAFs are all fine and dandy but they’re not perfect.
It can be useful to fuzz common payloads to see if any might slip through the cracks
wfuzz -c -w Fuzzing/XSS/XSS-Somdev.txt -u "https://example.com?q=FUZZ" --hc 403
In this example, I used a Cross-site Scripting (XSS) wordlist to see if any common payloads weren’t blocked. This likely will not lead to a XSS bug right away, but might clue me in on what keywords or encoding methods might allow me to build a working payload.
If you’re into finding XSS bugs, fuzzing with Portswigger’s XSS Cheat Sheet can help you see what HTML tags and events are permitted so you can get an idea of how to build your own payload.
Password Spraying
Database leaks for a specific target can be a great asset when testing login pages, but with thousands of accounts to choose from, it can be hard to find valid credentials. Luckily, wfuzz
can do this much faster than we can.
Password spraying is the act of guessing the password of many different accounts (not just one like in brute-force attacks). To achieve this, you need to tell wfuzz
to fuzz in two separate locations each with its own payload like so:
wfuzz -c -m zip -w users.txt -w passwords.txt -X POST -u "https://example.com" -d "username=FUZZ&password=FUZ2Z" --sc 302
For fuzzing multiple locations with different payloads, you need to supply FUZ<payload #>Z
. In this example FUZZ
is associated with users.txt
and FUZ2Z
with passwords.txt
.
I also supplied an iterator type of zip
with the -m
argument. The zip
iterator type will match each of the payloads to the other, 1-to-1, so it is perfect for password spraying. If you’ve used Burp’s Intruder before, than the zip
iterator is just like the Intruder’s Pitchfork attack type.
We can list the other iterator types with the -e
options like we did for payloads.
Wrapping it all up
wfuzz
does one thing (and only one thing) well: spit out a bunch of requests really fast. It’s up to you to make it an effective hacking too.
That means choosing the right wordlist for the job. There are tons of wordlists out there for different jobs. Don’t just limit yourself to SecLists. It’s a great wordlist and popular for a reason, but it’s popularity just means that a lot of other hunters are going to be using it too, so you’ll likely stumble across the same bugs they do.
So, Google around and find wordlists that work for you. Or, better yet, make your own as you hunt!
Also don’t go fuzzing around unless the program allows it. Sometimes there’s a request delay requirement and other times companies won’t permit the use of automated tools at all. Read the policy carefully! Otherwise, you may end up overwhelming the company’s servers.
Now, you have most of what you need to unleash the power of wfuzz
and hunt more efficiently! Happy hunting!