Post

CWES Cheatsheet — Fuzzing

CWES Cheatsheet — Fuzzing

fuzzing is how you find what’s hidden: directories, parameters, virtual hosts, API endpoints. pick your tool, pick your wordlist, and let it rip.


When to Fuzz

  • if the website has no links to anything else, and gives you no info that leads to more pages – fuzz it.
  • fuzz for directories first.
  • when a directory returns an empty page – fuzz inside it for hidden pages.
  • before fuzzing pages, find out what file types the site uses (.html, .php, .aspx, etc.) – fuzz the extension first.
  • if you found nothing but the server is Apache, try .php. if IIS, try .asp or .aspx. but extension fuzzing is more reliable.
  • if you’ve done a full recursive scan and still found nothing – try looking for sub-domains.
  • if the domain is registered in DNS – use DNS fuzzing for subdomains.
  • if the domain is in /etc/hosts – use VHost fuzzing for subdomains.
  • fuzz everything – directories, extensions, pages, parameters, values, subdomains, vhosts, API endpoints.

Use Cases

Use CaseDescription
Directory and File EnumerationQuickly identify hidden directories and files on a web server.
Parameter DiscoveryFind and test parameters within web applications.
Brute-Force AttackPerform brute-force attacks to discover login credentials or other sensitive information.

uncovering hidden assets:

  • sensitive data - backup files, configuration settings, or logs containing user credentials
  • outdated content - older versions of files or scripts vulnerable to known exploits
  • development resources - test environments, staging sites, admin panels
  • hidden functionalities - undocumented features or endpoints that could expose vulnerabilities

FFUF Filters Reference

knowing your filters is what separates useful results from noise.

FlagDescriptionWhen to use
-mcMatch response codes (e.g. 200,301)When you only want specific codes
-fcFilter out response codes (e.g. 404)When getting flooded with 404s
-fsFilter by response sizeWhen default pages all have the same size
-msMatch specific response sizeWhen you know the exact size you’re looking for
-fwFilter by word countWhen all junk responses have same word count
-mwMatch word countWhen you want a specific word count
-flFilter by line countWhen error pages have consistent line count
-mlMatch line countWhen target response has known line count
-mtMatch time (TTFB)When looking for time-based responses (e.g. blind injection)
1
2
3
4
5
6
7
8
9
10
11
# combine filters -- e.g. match 200, filter noise by word count
ffuf -u http://TARGET/FUZZ -w wordlist.txt -mc 200 -fw 427

# filter multiple codes
ffuf -u http://TARGET/FUZZ -w wordlist.txt -fc 404,401,302

# find backup files by size range
ffuf -u http://TARGET/FUZZ.bak -w wordlist.txt -fs 0-10239 -ms 10240-102400

# find slow endpoints (time-based injection hint)
ffuf -u http://TARGET/FUZZ -w wordlist.txt -mt ">500"

Gobuster Filters

FlagDescription
-s (include)Include only responses with specified status codes (dir mode only)
-b (exclude)Exclude responses with specified status codes (dir mode only)
--exclude-lengthExclude responses with specific content lengths
1
2
# find directories with status codes 200 or 301, exclude empty responses
gobuster dir -u http://TARGET/ -w wordlist.txt -s 200,301 --exclude-length 0

Wenum Filters

FlagDescription
--hc (hide code)Exclude responses matching specified status codes
--sc (show code)Include only responses matching specified status codes
--hl (hide length)Exclude responses with specified content length (in lines)
--sl (show length)Include only responses with specified content length (in lines)
--hw (hide word)Exclude responses with specified number of words
--sw (show word)Include only responses with specified number of words
--hs (hide size)Exclude responses with specified response size (bytes)
--ss (show size)Include only responses with specified response size (bytes)
--hr (hide regex)Exclude responses whose body matches the regex
--sr (show regex)Include only responses whose body matches the regex
1
2
3
4
5
6
7
8
# show only successful requests and redirects
wenum -w wordlist.txt --sc 200,301,302 -u https://example.com/FUZZ

# hide common error codes
wenum -w wordlist.txt --hc 404,400,500 -u https://example.com/FUZZ

# filter for responses containing specific info
wenum -w wordlist.txt --sr "admin\|password" -u https://example.com/FUZZ

Feroxbuster Filters

FlagDescription
--dont-scanExclude specific URLs/patterns from being scanned
-S / --filter-sizeExclude responses based on size (bytes)
-X / --filter-regexExclude responses matching regex
-W / --filter-wordsExclude responses with specific word count
-N / --filter-linesExclude responses with specific line count
-C / --filter-statusExclude responses with specific status codes
--filter-similar-toExclude responses similar to a given page
-s / --status-codesInclude only specified status codes (allowlist)
1
2
# find directories with status 200, excluding large or error responses
feroxbuster --url http://TARGET -w wordlist.txt -s 200 -S 10240 -X "error"

Wordlists

pick the right wordlist for the job. don’t just throw rockyou.txt at everything.

WordlistUse case
Discovery/Web-Content/directory-list-2.3-small.txtDirectory/page fuzzing
Discovery/Web-Content/directory-list-2.3-medium.txtBigger directory scan
Discovery/Web-Content/common.txtGeneral purpose (dirs + files)
Discovery/Web-Content/big.txtMassive wordlist for wide net scanning
Discovery/Web-Content/web-extensions.txtExtension fuzzing
Discovery/Web-Content/raft-large-directories.txtLarge directory name collection
Discovery/Web-Content/raft-large-files.txtConfig/backup file names
Discovery/DNS/subdomains-top1million-5000.txtSubdomain/VHost fuzzing
Discovery/Web-Content/burp-parameter-names.txtParameter fuzzing
Discovery/Web-Content/api/objects.txtAPI object/endpoint names
Discovery/Web-Content/quickhits.txtKnown sensitive paths (.git, .env, wp-admin)
Fuzzing/LFI/LFI-Jhaddix.txtLFI path traversal payloads
Fuzzing/SQLi/Generic-SQLi.txtSQLi payloads for param fuzzing
Fuzzing/XSS/XSS-BruteLogic.txtXSS payloads

all paths are under /usr/share/seclists/.


Directory Fuzzing

start here – this is always the first thing you fuzz.

1
2
3
4
5
6
7
8
9
10
11
12
# ffuf
ffuf -ic -c -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u http://TARGET/FUZZ
ffuf -w wordlist.txt -u http://TARGET/FUZZ -fc 404               # filter 404
ffuf -w wordlist.txt -u http://TARGET/FUZZ -fs <size>            # filter by size
ffuf -w wordlist.txt -u http://TARGET/FUZZ -t 200                # more threads

# Gobuster
gobuster dir -u http://TARGET/ -w wordlist.txt -b 404,403
gobuster dir -u http://TARGET/ -w wordlist.txt -s 200,301 --exclude-length 0

# Feroxbuster
feroxbuster -u http://TARGET/ -w wordlist.txt --filter-status 404,403

after finding directories – fuzz inside them for files and pages next.


Extension Fuzzing

before you fuzz for pages, you need to know what file type the site uses.

one file you can always find on most websites is index.* – use it to fuzz extensions.

1
2
3
4
5
6
# fuzz extension on index file
ffuf -ic -c -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt:FUZZ \
  -u http://TARGET/indexFUZZ

# note: the web-extensions.txt wordlist already includes the dot (.)
# so you don't need to add one after "index"

tips:

  • if nothing comes back and the server is Apache – probably .php
  • if the server is IIS – try .asp or .aspx
  • but don’t guess – fuzz the extension first, it’s more reliable.

Page Fuzzing

once you know the extension, fuzz for actual page names.

1
2
3
4
5
6
ffuf -ic -c -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt \
  -u http://TARGET/blog/FUZZ.php

# file fuzzing with multiple extensions at once
ffuf -u http://TARGET/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt \
  -e .php,.html,.txt,.bak,.js -v

what to look for:

  • .php, .html – active pages
  • .bak, .old, .sql, .zip – backup files, potentially containing secrets
  • .txt, .conf, .cfg – config/info files
  • .js – JavaScript with hidden logic or endpoints

File Fuzzing

1
2
3
# fuzz for files inside a directory with multiple extensions
ffuf -u http://TARGET/w2ksvrus/FUZZ -w /usr/share/seclists/Discovery/Web-Content/common.txt \
  -e .php,.html,.txt,.bak,.js -v
  • .php - server-side scripts
  • .html - web pages
  • .txt - plain text files, logs
  • .bak - backup files (previous versions)
  • .js - JavaScript code

Recursive Fuzzing

if you find a lot of directories, each might have their own subdirectories and files. don’t fuzz them one by one – use recursive fuzzing.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# ffuf recursive
ffuf -ic -c -w wordlist.txt -u http://TARGET/FUZZ \
  -recursion -recursion-depth 1 -e .php -v

# with rate limiting and timeout
ffuf -u http://TARGET/FUZZ -w wordlist.txt \
  -e .html -recursion -recursion-depth 2 -rate 500

# with rate and timeout
ffuf -u http://TARGET/FUZZ \
  -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt \
  -e .html -recursion -recursion-depth 2 \
  -rate 50 -timeout 5

# Feroxbuster (recursive by default)
feroxbuster -u http://TARGET/ -w wordlist.txt --depth 3

-recursion-depth 1 – only fuzz main directories and their direct sub-directories.


Sub-domain Fuzzing

when you’ve done a full directory and page scan and still found nothing useful – try sub-domains. they can reveal entirely different applications.

use this when the domain is registered in DNS (public).

1
2
3
4
5
6
7
# ffuf subdomain fuzzing
ffuf -ic -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ \
  -u https://FUZZ.target.com/

# Gobuster DNS mode
gobuster dns -d target.com -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt
gobuster dns -d target.com -w wordlist.txt -i   # show IPs

note: when you find a new subdomain, you might need to add it to /etc/hosts before you can access it.

1
echo "IP inlanefreight.htb" | sudo tee -a /etc/hosts

VHost Fuzzing

use this when the domain is NOT in public DNS – it’s in /etc/hosts or internal only. VHost fuzzing works by changing the Host: header.

1
2
3
4
5
6
7
8
9
10
# ffuf VHost fuzzing
ffuf -ic -c -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ \
  -u http://TARGET/ \
  -H "Host: FUZZ.target.com" \
  -fs <default_size>

# gobuster vhost
gobuster vhost -u http://target.com/ \
  -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-5000.txt \
  --append-domain

important: you’ll likely see a ton of noise – filter by response size (-fs) to only see the real ones. run it once without filter to see what the default (junk) response size is, then filter it out.

VHost vs Subdomain – when to use which:

ScenarioMethod
Domain registered in public DNSDNS subdomain fuzzing
Domain only in /etc/hosts or internalVHost fuzzing (Host header)

Parameter Fuzzing

fuzzing parameters may expose unpublished endpoints that are publicly accessible. these tend to be less tested and less secured – good targets for injection.

1
2
3
4
5
6
7
8
9
10
11
12
# GET parameter fuzzing
ffuf -ic -c -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ \
  -u http://TARGET/page.php?FUZZ=key \
  -fs <size>

# POST parameter fuzzing
# tip: in PHP, POST data Content-Type can only accept "application/x-www-form-urlencoded"
ffuf -ic -c -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ \
  -u http://TARGET/admin/admin.php \
  -X POST -d "FUZZ=key" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -fs <size>

note: when fuzzing POST and you want to verify with curl – make sure to use curl -X POST too, otherwise it defaults to GET.

1
2
# verify with curl
curl http://TARGET/admin/admin.php -X POST -d 'id=key' -H 'Content-Type: application/x-www-form-urlencoded'

Value Fuzzing

once you’ve found a parameter, fuzz its values to find valid inputs.

1
2
3
4
5
6
7
8
9
10
11
12
# generate numeric wordlist
for i in $(seq 1 1000); do echo $i >> ids.txt; done

# fuzz value for POST parameter
ffuf -w ids.txt:FUZZ \
  -u http://TARGET/admin/admin.php \
  -X POST -d "id=FUZZ" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -fs <size>

# fuzz value for GET parameter
ffuf -w ids.txt:FUZZ -u "http://TARGET/page.php?id=FUZZ" -fs <size>

JSON POST Parameter Fuzzing (APIs)

1
2
3
4
5
6
7
8
9
10
11
# fuzz JSON parameter names
ffuf -u http://TARGET/api/endpoint -X POST \
  -H "Content-Type: application/json" \
  -d '{"FUZZ":"test"}' \
  -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -fs <default_size>

# fuzz JSON parameter values
ffuf -u http://TARGET/api/endpoint -X POST \
  -H "Content-Type: application/json" \
  -d '{"id":FUZZ}' \
  -w ids.txt -fs <default_size>

always try both application/x-www-form-urlencoded AND application/json content types when fuzzing POST parameters.


API Endpoint Fuzzing

1
2
3
4
5
6
7
8
9
10
11
12
13
# API directory fuzzing
ffuf -w /usr/share/seclists/Discovery/Web-Content/api/api-endpoints.txt \
  -u http://TARGET/api/FUZZ

# API with version path
ffuf -w wordlist.txt -u http://TARGET/api/v1/FUZZ

# quick sensitive file check (good first step)
ffuf -u http://TARGET/FUZZ -w /usr/share/seclists/Discovery/Web-Content/quickhits.txt -fc 403,404

# API POST request fuzzing
ffuf -u http://TARGET/api/FUZZ -c -w /usr/share/seclists/Discovery/Web-Content/raft-small-words.txt -mc all -fs 0
ffuf -u http://TARGET/api/FUZZ -c -w wordlist.txt -mc all -fs 50 -d 'x=x'

Fuzzing the API with webfuzz_api

1
2
3
4
git clone https://github.com/PandaSt0rm/webfuzz_api.git
cd webfuzz_api
pip3 install -r requirements.txt
python3 api_fuzzer.py http://IP:PORT

Fuzzing with Rate Limiting / Stealth

1
2
3
4
5
6
7
8
# slow down to avoid WAF/rate limits
ffuf -u http://TARGET/FUZZ -w wordlist.txt -rate 50

# add delay between requests (seconds)
ffuf -u http://TARGET/FUZZ -w wordlist.txt -p 0.5

# lower threads for stealth (default 40)
ffuf -u http://TARGET/FUZZ -w wordlist.txt -t 10

Fuzzing with Cookies / Auth Headers

1
2
3
4
5
6
7
8
# fuzz authenticated endpoints with cookie
ffuf -u http://TARGET/admin/FUZZ -w wordlist.txt -b "PHPSESSID=abc123" -fc 403

# fuzz with Bearer token (API)
ffuf -u http://TARGET/api/FUZZ -w wordlist.txt -H "Authorization: Bearer eyJhb..."

# fuzz with multiple headers
ffuf -u http://TARGET/FUZZ -w wordlist.txt -H "Cookie: session=abc" -H "X-Custom: value"

Username Enumeration via Fuzzing

1
2
3
4
5
6
7
8
9
10
11
12
# login page reveals if user exists with message "An account with this username already exists"
ffuf -c -w /usr/share/wordlists/SecLists/Usernames/Names/names.txt \
  -X POST -d "username=FUZZ&email=x&password=x&cpassword=x" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u http://TARGET/customers/signup \
  -mr "An account with this username already exists"

# getting valid credential combinations
ffuf -c -w valid_usernames.txt:W1,/usr/share/wordlists/SecLists/Passwords/Common-Credentials/10-million-password-list-top-100.txt:W2 \
  -X POST -d "username=W1&password=W2" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -u http://TARGET/customers/login -fc 200

FFUF via Proxy

1
2
3
4
5
# route through a proxy
ffuf -c -w wordlist.txt -u http://TARGET/FUZZ -x http://proxy:port

# replay through Burp for inspection
ffuf -c -w wordlist.txt -u http://TARGET/FUZZ -replay-proxy http://127.0.0.1:8080

FFUF Report Output

1
2
3
4
5
# generate HTML report
ffuf -c -w wordlist.txt -u http://TARGET/FUZZ -o ffuf_report.html -of html

# generate and open
ffuf -c -w wordlist.txt -u http://TARGET/FUZZ -o ffuf_report.html -of html && firefox ffuf_report.html

Multi-Subdomain Workflow

once you find multiple subdomains, fuzz them all with a for loop.

1
2
3
4
5
6
7
8
9
10
11
12
# fuzz extensions on each subdomain
for sub in archive test faculty; do
  ffuf -ic -c -w /usr/share/seclists/Discovery/Web-Content/web-extensions.txt:FUZZ \
    -u http://$sub.academy.htb:PORT/indexFUZZ
done

# fuzz pages on each subdomain with recursive
for sub in archive test faculty; do
  ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-large-words-lowercase.txt:FUZZ \
    -u http://$sub.academy.htb:PORT/FUZZ \
    -recursion -recursion-depth 1 -e .php,.phps,.php7 -v -t 200 -fs 287 -ic
done

Manual Verification

after fuzzing, always verify your findings manually.

1
2
3
4
5
6
7
8
9
10
# curl the found path
curl http://TARGET/backup/

# check Content-Type header to understand file type
curl -I http://TARGET/backup/password.txt
# Content-Type: application/sql = database dump
# Content-Type: application/zip = compressed backup

# check Content-Length -- if 0, the file is empty
# Content-Length: 171 = has data, worth checking

Sample Workflows

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 1. root directories
ffuf -c -w big.txt -u http://TARGET/FUZZ

# 2. root with extensions
ffuf -c -w big.txt -u http://TARGET/FUZZ -e .git,.txt,.json,.php,.html,.bak,.old,.sql,.zip,.conf,.cfg

# 3. sub web folders
ffuf -c -w big.txt -u http://TARGET/secret/FUZZ

# 4. sub web folder with extensions
ffuf -c -w big.txt -u http://TARGET/secret/FUZZ -e .git,.txt,.json,.php,.html,.bak,.old,.sql,.zip,.conf,.cfg,.js

# 5. vHost fuzz
ffuf -c -w big.txt -H "Host: FUZZ.target.com/" -u http://TARGET/

# 6. subdomain root (repeat step 1 for found subdomain)
ffuf -c -w big.txt -u http://sub.target.com/FUZZ

Fuzzing Quick Reference (ffuf)

CommandDescription
ffuf -ic -c -w wordlist:FUZZ -u http://IP:PORT/FUZZDirectory Fuzzing
ffuf -ic -c -w wordlist:FUZZ -u http://IP:PORT/indexFUZZExtension Fuzzing
ffuf -ic -c -w wordlist:FUZZ -u http://IP:PORT/blog/FUZZ.phpPage Fuzzing
ffuf -ic -c -w wordlist:FUZZ -u http://IP:PORT/FUZZ -recursion -recursion-depth 1 -e .php -vRecursive Fuzzing
ffuf -ic -c -w wordlist:FUZZ -u https://FUZZ.target.com/Sub-domain Fuzzing
ffuf -ic -c -w wordlist:FUZZ -u http://target/ -H 'Host: FUZZ.target.com' -fs xxxVHost Fuzzing
ffuf -ic -c -w wordlist:FUZZ -u http://target/page.php?FUZZ=key -fs xxxParameter Fuzzing (GET)
ffuf -ic -c -w wordlist:FUZZ -u http://target/page.php -X POST -d 'FUZZ=key' -H 'Content-Type: application/x-www-form-urlencoded' -fs xxxParameter Fuzzing (POST)
ffuf -c -w ids.txt:FUZZ -u http://target/page.php -X POST -d 'id=FUZZ' -H 'Content-Type: application/x-www-form-urlencoded' -fs xxxValue Fuzzing

Misc Commands

1
2
3
4
5
6
7
8
# add DNS entry for vhost resolution
sudo sh -c 'echo "SERVER_IP  academy.htb" >> /etc/hosts'

# create sequence wordlist
for i in $(seq 1 1000); do echo $i >> ids.txt; done

# curl with POST
curl http://TARGET/admin/admin.php -X POST -d 'id=key' -H 'Content-Type: application/x-www-form-urlencoded'

← Back to CWES Cheatsheet Index

This post is licensed under CC BY 4.0 by the author.