// HackTricks · Web Pentesting

Web Tool - WFuzz

Web Tool - WFuzz

A tool to FUZZ web applications anywhere.

Wfuzz has been created to facilitate the task in web applications assessments and it is based on a simple concept: it replaces any reference to the FUZZ keyword by the value of a given payload.

Installation

Installed in Kali:

sudo apt install wfuzz
pip install -U wfuzz
# Docker image
# docker run -v $(pwd)/wordlist:/wordlist/ -it ghcr.io/xmendez/wfuzz wfuzz

Github: https://github.com/xmendez/wfuzz

If pip install wfuzz breaks in recent Python environments, upgrade to the latest upstream release first (pip install -U wfuzz).[1]

Filtering options

--hs/ss "regex" # Hide/Show by regex in response body
# Simple example, match a string: "Invalid username"
# Regex example: "Invalid .*"

--hc/sc CODE # Hide/Show by code in response
--hl/sl NUM  # Hide/Show by number of lines in response
--hw/sw NUM  # Hide/Show by number of words in response
--hh/sh NUM  # Hide/Show by number of chars in response

--filter "c=200 and h!=BBB" # Expression filter (check: wfuzz --filter-help)
# c,l,w,h are response code, lines, words and chars
# BBB means: use the baseline response value

Baseline filtering (BBB)

Very useful when the application answers every invalid request with the same 200, 302 or custom error page.

# Baseline the response using a value that should not exist
wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/common.txt \
  --hc BBB http://target.tld/FUZZ{this-should-not-exist-123}

# Same idea, but keeping only responses that differ from the baseline in size
wfuzz -c -w users.txt -d 'username=FUZZ&password=invalid' \
  --filter 'c=200 and h!=BBB' http://target.tld/login.php

Output options

wfuzz -e printers              # Prints the available output formats
-f /tmp/output.csv,csv         # Saves the output in csv format
-f /tmp/output.json,json       # Saves the output in json format
-o json                        # Prints json to stdout
--efield r                     # Print extra fields (useful to inspect raw request)
--field url                    # Replace default payload column with a selected field

Useful advanced switches

-R 1            # Recursive path discovery depth
-L              # Follow HTTP redirections
-Z              # Scan mode: don't abort on connection/DNS errors (errors become XXX)
-m zip          # Pair payloads 1:1 instead of using the default cartesian product
-m chain        # Use one payload after another
-V allvars      # Fuzz all GET params without placing FUZZ in the URL
-V allpost      # Fuzz all POST params without placing FUZZ in the body
--dump-recipe /tmp/wfuzz.rcp  # Save a run for later reuse
--recipe /tmp/wfuzz.rcp       # Re-run a saved recipe
--ip 10.10.10.10              # Connect to a chosen IP while keeping the URL host

Encoders options

wfuzz -e encoders # Prints the available encoders
# Examples: urlencode, md5, base64, hexlify, uri_hex, double urlencode

In order to use an encoder, you have to indicate it in the -w or -z option.[2]

Examples:

-z file,/path/to/file,md5 # Will use a list inside the file, and will transform each value into its md5 hash before sending it
-w /path/to/file,base64   # Will use a list, and transform to base64
-z list,each-element-here,hexlify # Inline list and to hex before sending values

You can also chain encoders and combine payloads:

# Apply several encoders to the same payload source
wfuzz -z list,1-2-3,sha1-sha1@none http://example.com/FUZZ

# Pair usernames and passwords line-by-line instead of trying the full cartesian product
wfuzz -w users.txt -w passwords.txt -m zip \
  -d 'username=FUZZ&password=FUZ2Z' http://example.com/login

CheatSheet

Login Form bruteforce

POST, Single list, filter string (hide)

wfuzz -c -w users.txt --hs "Login name" -d "name=FUZZ&password=FUZZ&autologin=1&enter=Sign+in" http://zipper.htb/zabbix/index.php
# Here we have filtered by regex in the response body

POST, 2 lists, filter code (show)

wfuzz.py -c -z file,users.txt -z file,pass.txt --sc 200 -d "name=FUZZ&password=FUZ2Z&autologin=1&enter=Sign+in" http://zipper.htb/zabbix/index.php
# Here we have filtered by code

POST, 2 paired lists (zip iterator)

wfuzz -c -w users.txt -w passwords.txt -m zip --sc 200 \
  -d 'username=FUZZ&password=FUZ2Z' http://example.com/login

GET, 2 lists, filter string (show), proxy, cookies

wfuzz -c -w users.txt -w pass.txt --ss "Welcome " -p 127.0.0.1:8080:HTTP -b "PHPSESSIONID=1234567890abcdef;customcookie=hey" "http://example.com/index.php?username=FUZZ&password=FUZ2Z&action=sign+in"

JSON API login / OTP bruteforce

wfuzz -c -z range,000000-999999 --hh BBB \
  -H 'Content-Type: application/json' \
  -d '{"username":"admin","otp":"FUZZ"}' \
  http://example.com/api/verify

Bruteforce Directory/RESTful bruteforce

Arjun parameters wordlist

wfuzz -c -w /tmp/tmp/params.txt --hc 404 https://domain.com/api/FUZZ

Path Parameters BF

wfuzz -c -w ~/git/Arjun/db/params.txt --hw 11 'http://example.com/path%3BFUZZ=FUZZ'

Fuzz all parameters automatically

# Bruteforce every GET parameter value without placing FUZZ manually
wfuzz -c -z file,/usr/share/seclists/Fuzzing/LFI/LFI-Jhaddix.txt \
  -V allvars --hc 404 'http://example.com/index.php?file=1&page=2&lang=en'

# Same idea for POST bodies
wfuzz -c -z list,"1'-2'-3'" -V allpost \
  -d 'username=admin&password=test&otp=000000' \
  --ss 'SQL|syntax|warning' http://example.com/login

Header Authentication

Basic, 2 lists, filter string (show), proxy

wfuzz -c -w users.txt -w pass.txt -p 127.0.0.1:8080:HTTP --ss "Welcome" --basic FUZZ:FUZ2Z "http://example.com/index.php"

NTLM, 2 lists, filter string (show), proxy

wfuzz -c -w users.txt -w pass.txt -p 127.0.0.1:8080:HTTP --ss "Welcome" --ntlm 'domain\FUZZ:FUZ2Z' "http://example.com/index.php"

Cookie/Header bruteforce (vhost brute)

wfuzz -c -w users.txt -p 127.0.0.1:8080:HTTP --ss "Welcome " -H "Cookie:id=1312321&user=FUZZ"  "http://example.com/index.php"

User-Agent, filter code (hide), proxy

wfuzz -c -w user-agents.txt -p 127.0.0.1:8080:HTTP --ss "Welcome " -H "User-Agent: FUZZ"  "http://example.com/index.php"

Host

wfuzz -c -w /usr/share/wordlists/SecLists/Discovery/DNS/subdomains-top1million-20000.txt \
  --hc 400,404,403 -H 'Host: FUZZ.example.com' -u http://example.com -t 100

Host + explicit IP (reverse proxy / misrouting checks)

wfuzz -c -w vhosts.txt --ip 10.10.10.10 \
  --hc BBB -H 'Host: FUZZ.example.com' http://example.com/

For more methodology around wildcard vhosts and related recon edge-cases, check this page.

HTTP Verbs (methods) bruteforce

Using file

wfuzz -c -w methods.txt -p 127.0.0.1:8080:HTTP --sc 200 -X FUZZ "http://example.com/index.php"

Using inline list

wfuzz -z list,GET-HEAD-POST-TRACE-OPTIONS -X FUZZ http://testphp.vulnweb.com/

Directory & Files Bruteforce

# Filter by whitelisting codes
wfuzz -c -z file,/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt --sc 200,202,204,301,302,307,403 http://example.com/uploads/FUZZ

Recursive discovery

# Reuse the same payload inside discovered directories up to depth 2
wfuzz -c -w /usr/share/seclists/Discovery/Web-Content/common.txt \
  -R 2 --sc 200,301,302,307,403 http://example.com/FUZZ

Save / restore long fuzzing sessions

wfuzz -c -w dirs.txt --hc 404 --dump-recipe /tmp/wfuzz.recipe http://example.com/FUZZ
wfuzz --recipe /tmp/wfuzz.recipe -b 'session=abc123'

Tool to bypass Webs

https://github.com/carlospolop/fuzzhttpbypass

References

☧ CPTS · ordered posts

Locked

Unlock with your Main_Key through local GPG.

Start the local GPG helper

In your cheatsheet checkout, run:

npm run unlock:helper

Open the link it prints, or paste its pairing code below. Keep the terminal open until GPG finishes.

Import an index you already decrypted

Download the encrypted index, then decrypt with GPG:

gpg --output report-index.json --decrypt report-index.gpg

Your private key stays in GPG. The decrypted index is saved in this browser so you can reopen it after navigation, refreshes and restarts. Lock this browser or clear this site's browser data to remove the saved index. Nothing is uploaded.