Find Hidden Web Directories - Fuzz URLs with ffuf

Web servers often have secret directories, not linked from anywhere.

You could find them by trying different paths manually: /secret, /.svn /admin. This article shows you how fuff can do this to you automatically.

For practice, I coded a target that you can run locally, without Internet. I will also tell you the solution, so you can test your environment. As bonus, there is a challenge target where you can find to solution yourself.

What the Fuff?

Fuff is a web fuzzer by joohoi. This article shows you how you can use it to find hidden directories, just like gobuster or dirbuster.

Fuff is a full featured fuzzing tool. This article just fuzzes hidden directories, but you can also use fuff to fuzz anything: headers, POST parameters...

Use of penetration testing techniques requires legal and ethical considerations. To safely use these tools, tactics and procedures, you might need to obtain contracts and permissions; and posses adequate technical skills. Check your local laws.

Fuff is very fast. Disconnect your internet and play locally to avoid leaking packets to the Internet.

This tutorial was tested on Debian Linux amd64. It probably works on other Linuxes, too.

Download a sample target

Here is a sample target I wrote for you: dirfuzt-0. It has a secret page, not linked from anywhere from the site. Download it and run it

$ wget https://terokarvinen.com/2023/fuzz-urls-find-hidden-directories/dirfuzt-0
$ chmod u+x dirfuzt-0
$ ./dirfuzt-0
Learn more at TeroKarvinen.com
http://127.0.0.2:8000

Now you can use your browser, like Firefox, to browse the site (use the URL dirfuzt prints you)

http://127.0.0.2:8000

Does it say "Nothing, nil, null, nada."? Well, now you have your practice target running.

As long as the server is running, it will not give you the prompt back. You can use shell from another window.

Manually

Let's try our luck! Maybe it's http://127.0.0.2:8000/secret ? Nope.

We could keep trying. Or we could let ffuf try a huge number of URLs for us, automatically.

Install ffuf, a fast web fuzzer / dir buster

Download and extract a release of ffuf.

$ wget https://github.com/ffuf/ffuf/releases/download/v2.0.0/ffuf_2.0.0_linux_amd64.tar.gz
$ tar -xf ffuf_2.0.0_linux_amd64.tar.gz
$ ./ffuf
Fuzz Faster U Fool - v2.0.0
...

Well done, now you've got ffuf installed.

In case the link ever goes stale, here is a local mirror of ffuf_2.0.0_linux_amd64.tar.gz.

Install a Dictionary of Common Web Paths

We'll still need a dictionary. For directories, a text file with possible directories, one per line. You could even write one yourself:

secret/
.svn/
dashboard/

But luckily, many such dictionaries already exist. We can use one from Seclists by Daniel Miessler and others.

$ wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Discovery/Web-Content/common.txt

In case it ever disappears, here is a local mirror of SecLists common.txt It's just one path after another, just short of five thousand paths total.

$ head -3 common.txt 
.bash_history
.bashrc
.cache
$ wc -l common.txt 
4715 common.txt

Fuzz Faster, You F^H^H^Hool!

Fuff is very fast. Now might be a good time to disconnect your Internet before you start generating a lot of requests.

You can see all ffuf parameters with

$ ./fuff

It prints to stderr, so we need a funny pipe to see it paginated (space and b move, q quits)

$ ./ffuf |& less

Let's try the obvious approach, wordlist -w and target url -u

$ ./ffuf -w common.txt -u http://127.0.0.2:8000/FUZZ

Try every line in common.txt in place of FUZZ. So fuff will request

http://127.0.0.2:8000/.bash_history
http://127.0.0.2:8000/.bashrc

and so on.

Wow, that was fast! Did it already do almost 5k requests? Let's look at the output

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

       v2.0.0
________________________________________________

 :: Method           : GET
 :: URL              : http://127.0.0.2:8000/FUZZ
 :: Wordlist         : FUZZ: /home/vagrant/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: 200,204,301,302,307,401,403,405,500
________________________________________________

[Status: 200, Size: 132, Words: 6, Lines: 10, Duration: 1ms]
    * FUZZ: .web

[Status: 200, Size: 132, Words: 6, Lines: 10, Duration: 1ms]
    * FUZZ: .cache

[Status: 200, Size: 132, Words: 6, Lines: 10, Duration: 1ms]
    * FUZZ: .bashrc

... and thousands more ...

And it goes on and on - thousands of responses are listed.

Everything is OK

The stupid web server is giving us HTTP status 200 OK for everything. But if we look at the pages, they just say "Nothing, nil, null, nada.". Even ones with interesting URLs, like http://127.0.0.2:8000/.bashrc.

When this happens, we must look at other common qualities of unwanted responses. And filter them out.

Filter options are listed in ffuf help

$ ./fuff |& less
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 <10
 -fw Filter by amount of words in response. Comma separated list of word counts and ranges

Looking at a sample of 10-20 responses, we can see that most have a lot in common:

[Status: 200, Size: 132, Words: 6, Lines: 10, Duration: 1ms]
[Status: 200, Size: 132, Words: 6, Lines: 10, Duration: 1ms]
[Status: 200, Size: 132, Words: 6, Lines: 10, Duration: 1ms]
...

The unwanted hits, false positives have the same:

  • (HTTP) Status (-fc). But the hidden pages are probably 200 OK, too.
  • Size (in bytes) (-fs)
  • Words (-fw)
  • Lines (-fl)
  • Duration (in milliseconds) (-ft). Would not be my fist choice, as it can vary by chance.

The size of each unwanted response seems to be 132 bytes. That's 132 ASCII characters. It's shorter than an SMS text message! Let's filter by that.

$ ./ffuf -w common.txt -u http://127.0.0.2:8000/FUZZ -fs 132

Just two matched responses:

[Status: 200, Size: 160, Words: 10, Lines: 11, Duration: 0ms]
    * FUZZ: admin
[Status: 301, Size: 64, Words: 3, Lines: 3, Duration: 2ms]
    * FUZZ: render/https://www.google.com

Jackpot? Let's test with a browser (and maybe curl)

Manual verification confirms that /admin is the URL we were looking for. As the page says: "You've found it!".

The other one is a strange redirector, accepting many URLs after "/render/https://".

Well done, you've just fuzzed a hidden directory.

Your turn - Challenge

Can you find two URLs:

  • Admin page
  • Version control related page

Challenge binary dirfuzt-1.

Notice that this is a different binary from dirfuzt-0. You must shut the first exercise down before you can hack the second one. The challenge binary frontpage has the word "dirfutz-1" in the title.

Keep it safe, legal and ethical - use your powers for good.

Good luck!

What's Next

See Joona 'joohoi' Hoikkala himself teach ffuf in HelSec:

Try ffuf on some practice target you're working with.

You just fuzzed some directory paths. Try fuzzing something else, like POST requests, query string parameters or request headers.

Happy hacking!

See also

Fuff homepage https://github.com/ffuf/ffuf

Miessler, Haddix, g0tmi1k: SecLists https://github.com/danielmiessler/SecLists

Adminstrivia

2023-11-06: fixed a typo.