July 1, 2026
The ART of FUZZING - How to fuzz
At its core, fuzzing means sending a large set of inputs at a target and watching what comes back. But that description undersells it. Good…

By hunhun
7 min read
At its core, fuzzing means sending a large set of inputs at a target and watching what comes back. But that description undersells it. Good fuzzing isn't random it's methodical. This guide walks through the core fuzzing techniques I use from subdomain discovery all the way to error-based differential testing.
The main tool throughout is ffuf (Fuzz Faster U Fool). It's fast, flexible, and handles everything from subdomains to POST body parameters. You'll also need subfinder or any subdomain enumeration tool, httpx, and SecLists for wordlists.
Subdomain Fuzzing Follow the Pattern
The basic command is simple:
ffuf -u https://FUZZ.target.com -w wordlist.txtffuf -u https://FUZZ.target.com -w wordlist.txtBut most people run it and move on without reading the output..
When your enumeration tools come back with results, read them before piping them anywhere. Every company's subdomain structure tells you how they think. Take this example output:
usa.target.com
vs36.target.com
sng-a1.target.com
user.target.com
user2.target.com
admin.target.comusa.target.com
vs36.target.com
sng-a1.target.com
user.target.com
user2.target.com
admin.target.comThis is a map. It tells you they have country-based subdomains, versioned infrastructure, regional codes, and numbered environments. Which means there are almost certainly more of each. Now you fuzz with intent:
# Country codes — could there be eg, uae, gbr, ind?
FUZZ.target.com
# vs36 looks sequential - try vs35, vs37, vs38
vsFUZZ.target.com
# user and user2 exist - where does the sequence end?
userFUZZ.target.com
# Regional servers often follow a pattern
sng-FUZZ.target.com# Country codes — could there be eg, uae, gbr, ind?
FUZZ.target.com
# vs36 looks sequential - try vs35, vs37, vs38
vsFUZZ.target.com
# user and user2 exist - where does the sequence end?
userFUZZ.target.com
# Regional servers often follow a pattern
sng-FUZZ.target.comYou're not guessing anymore you're completing a pattern someone else started. A useful tool for generating more candidates is CeWLAI, a domain generation tool that uses AI to build variations from seed domains.
Vhost Fuzzing Don't Hit the CDN
The critical step before running this: find the origin IP, not the CDN IP. If the target sits behind Cloudflare or a similar provider, you're fuzzing their infrastructure, not the target's. Use httpx with the -cdn flag to identify which resolved IPs belong to a CDN, then fuzz the origin directly:
ffuf -w SecLists/Discovery/DNS/subdomains-top1million-110000.txt:FUZZ \
-u https://ORIGIN_IP \
-H 'Host: FUZZ.target.com'ffuf -w SecLists/Discovery/DNS/subdomains-top1million-110000.txt:FUZZ \
-u https://ORIGIN_IP \
-H 'Host: FUZZ.target.com'Here's a detail most guides skip: some subdomains appear in DNS but don't respond over HTTP. Take everything that showed up in your enumeration tools but got no response in httpx, and use those hostnames as your wordlist:
# Subfinder found these four:
admin.target.com
test.target.com
target.target.com
blah.target.com
# httpx only got responses from two:
admin.target.com → 403
test.target.com → 200
# Vhost fuzzing wordlist - the silent ones:
target
blah# Subfinder found these four:
admin.target.com
test.target.com
target.target.com
blah.target.com
# httpx only got responses from two:
admin.target.com → 403
test.target.com → 200
# Vhost fuzzing wordlist - the silent ones:
target
blahSubdomains that don't respond publicly might respond when you speak directly to the origin server with the right Host header. Internal environments and staging sites surface this way constantly.
Directory Fuzzing Think Beyond /FUZZ
The standard directory fuzz is a good start, but leaving it at the root means you're testing the same paths every automated scanner already has.
Start by adding extensions. Backup files, configuration files, and temporary files get left in web roots more often than anyone admits:
ffuf -u https://target.com/FUZZ -w wordlist.txt \
-e .php,.txt,.bak,.html,.aspx,.old,.jsonffuf -u https://target.com/FUZZ -w wordlist.txt \
-e .php,.txt,.bak,.html,.aspx,.old,.jsonMany wordlist exist you could use seclist raft ones and one list for all.
Then use what you already know about the target. The application's own name, or the subdomain name, is one of the most underused fuzzing inputs:
ffuf -u https://target.com/targetFUZZ -w wordlist.txt
ffuf -u https://target.com/target/FUZZ -w wordlist.txt
ffuf -u https://target.com/target-FUZZ -w wordlist.txtffuf -u https://target.com/targetFUZZ -w wordlist.txt
ffuf -u https://target.com/target/FUZZ -w wordlist.txt
ffuf -u https://target.com/target-FUZZ -w wordlist.txtThis applies to subdomains too. If you're testing api.target.com, build your paths around the subdomain name:
ffuf -u https://api.target.com/apiFUZZ -w wordlist.txt
ffuf -u https://api.target.com/api/FUZZ -w wordlist.txt
ffuf -u https://api.target.com/api-FUZZ -w wordlist.txtffuf -u https://api.target.com/apiFUZZ -w wordlist.txt
ffuf -u https://api.target.com/api/FUZZ -w wordlist.txt
ffuf -u https://api.target.com/api-FUZZ -w wordlist.txtFor custom wordlists built from the actual content of a target's website, CeWL is worth adding to your workflow.
Dot and Underscore Files The Forgotten Prefixes
Some of the most sensitive files on a web server start with a dot or an underscore. .env, .htaccess, .git/config, _config.yml. They're easy to overlook because most directory wordlists don't lead with these characters by default. Explicitly fuzz for them:
ffuf -u https://target.com/.FUZZ -w wordlist.txt
ffuf -u https://target.com/_FUZZ -w wordlist.txtffuf -u https://target.com/.FUZZ -w wordlist.txt
ffuf -u https://target.com/_FUZZ -w wordlist.txtThere's also a newer angle worth adding: applications built with AI coding tools (often called "vibe-coded" apps) commonly deploy Markdown files containing agent instructions, system prompts, or internal specifications at predictable paths. These don't show up in standard wordlists. Add .md to your extension fuzzing:
ffuf -u https://target.com/FUZZ -w wordlist.txt -e .mdffuf -u https://target.com/FUZZ -w wordlist.txt -e .mdHTTP Method Fuzzing Testing What the Server Actually Accepts
Every endpoint was built to accept certain HTTP methods. Most developers test the methods they implemented and nothing else. That leaves a lot of untested surface.
In Burp Suite, place your fuzzing position in the method field:
§FUZZ§ /api/v1/getuser HTTP/1.1
Host: example.com§FUZZ§ /api/v1/getuser HTTP/1.1
Host: example.comUse a comprehensive methods wordlist that goes well beyond GET and POST. WebDAV methods (PROPFIND, MKCOL, COPY, MOVE), diagnostic methods (TRACE, TRACK, DEBUG), and extension methods are frequently left enabled by misconfiguration:
GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, CONNECT,
PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ORDERPATCH,
ACL, REPORT, VERSION-CONTROL, CHECKOUT, CHECKIN, UNCHECKOUT,
MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE-CONTROL, MKACTIVITY,
SEARCH, PURGE, TRACK, DEBUG, LINK, UNLINK, BIND, UNBIND, REBINDGET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, TRACE, CONNECT,
PROPFIND, PROPPATCH, MKCOL, COPY, MOVE, LOCK, UNLOCK, ORDERPATCH,
ACL, REPORT, VERSION-CONTROL, CHECKOUT, CHECKIN, UNCHECKOUT,
MKWORKSPACE, UPDATE, LABEL, MERGE, BASELINE-CONTROL, MKACTIVITY,
SEARCH, PURGE, TRACK, DEBUG, LINK, UNLINK, BIND, UNBIND, REBINDAn endpoint returning a 405 Method Not Allowed for most methods but something different for one specific method is worth investigating further.
Parameter Fuzzing Finding Inputs That Were Never Meant to Be Found
Applications often contain parameters left behind from development or internal use that were never cleaned up. Fuzzing parameter names surfaces them.
For GET parameters, try different value types some parameters only respond to specific truthy or falsy inputs:
ffuf -u https://target.com/path.php?FUZZ=key -w params_wordlist.txt
ffuf -u https://target.com/path.php?FUZZ=true -w params_wordlist.txt
ffuf -u https://target.com/path.php?FUZZ=false -w params_wordlist.txt
ffuf -u https://target.com/path.php?FUZZ=1 -w params_wordlist.txt
ffuf -u https://target.com/path.php?FUZZ=0 -w params_wordlist.txtffuf -u https://target.com/path.php?FUZZ=key -w params_wordlist.txt
ffuf -u https://target.com/path.php?FUZZ=true -w params_wordlist.txt
ffuf -u https://target.com/path.php?FUZZ=false -w params_wordlist.txt
ffuf -u https://target.com/path.php?FUZZ=1 -w params_wordlist.txt
ffuf -u https://target.com/path.php?FUZZ=0 -w params_wordlist.txtFor POST parameters:
ffuf -u https://target.com/path.php \
-X POST \
-d 'FUZZ=key' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-w params_wordlist.txt
-d 'FUZZ=true'
-d 'FUZZ=false'
-d 'FUZZ=1'
-d 'FUZZ=0'ffuf -u https://target.com/path.php \
-X POST \
-d 'FUZZ=key' \
-H 'Content-Type: application/x-www-form-urlencoded' \
-w params_wordlist.txt
-d 'FUZZ=true'
-d 'FUZZ=false'
-d 'FUZZ=1'
-d 'FUZZ=0'A real-world example: CVE-2023–23752, an information disclosure vulnerability in Joomla, could have been found using exactly this technique. IppSec demonstrated this while solving the HTB machine Devvortex by fuzzing API parameters.
Differential Fuzzing Making the Application Tell You Things
This is the most nuanced technique because you're not looking for a path that exists. You're looking for inputs that cause the application to behave differently from everything else.
The idea: send inputs designed to trigger errors and pay close attention to what comes back. Different error messages, different response sizes, different response times all of this is information.
Fuzz with special characters first:
ffuf -k -u https://target.com/search.php \
-d "param=FUZZ" \
-w /opt/SecLists/Fuzzing/special-chars.txt \
-H 'Content-Type: application/x-www-form-urlencoded'ffuf -k -u https://target.com/search.php \
-d "param=FUZZ" \
-w /opt/SecLists/Fuzzing/special-chars.txt \
-H 'Content-Type: application/x-www-form-urlencoded'Other input categories worth testing:
- Every byte from
%00through%ff - Unicode look-alike characters tools like this converter make this easy
- Input in a different language or character set than the application expects
IppSec demonstrated this technique in his HTB Streamio walkthrough, where fuzzing a search parameter with special characters revealed an MSSQL injection point through differential error responses that only appeared for certain inputs.
User-Agent Fuzzing What the Application Shows Depends on Who It Thinks You Are
Web applications don't always serve the same content to everyone. Some endpoints behave differently depending on what browser or client is making the request. WAFs whitelist certain crawlers. Admin panels reveal debug information to internal tools. Mobile endpoints only activate for mobile user agents. Fuzzing the User-Agent header surfaces all of this.
The command is simple you're just moving the fuzzing position into the header:
ffuf -w SecLists/Fuzzing/User-Agents/UserAgents.fuzz.txt:FUZZ \
-u https://target.com/endpoint \
-H "User-Agent: FUZZ"ffuf -w SecLists/Fuzzing/User-Agents/UserAgents.fuzz.txt:FUZZ \
-u https://target.com/endpoint \
-H "User-Agent: FUZZ"Watch for any variation in the response different status codes, different response sizes, different content. Any of those is worth investigating manually.
Don't forget to try POST request as well.
ffuf -k -u https://target.com/ \
-d "param=dummy" \
-w /opt/SecLists/Fuzzing/special-chars.txt \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H "User-Agent: FUZZ"ffuf -k -u https://target.com/ \
-d "param=dummy" \
-w /opt/SecLists/Fuzzing/special-chars.txt \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H "User-Agent: FUZZ"Mobile endpoints applications often serve entirely different functionality to mobile clients. Different endpoints, different parameters, sometimes reduced security controls. Fuzzing with mobile user agents can expose an attack surface the desktop version doesn't have:
Different application versions legacy user agents sometimes hit older code paths that were never updated with the same security controls as the modern version.
Timing-Based Enumeration
There are many bugs that could be exploited or found just by inspecting the response time such as blind ssrf, but for this section am going to mention a technique i see few people talking about which is username enumeration.
This technique extends naturally into login portals during penetration testing engagements. Most login forms return the same response same status code, same message whether a username exists or not. But the response time often tells a different story. this was explained pretty nicely in Hunter machine on hacksmarter.
Reset endpoints are cleaner for this than login forms because they only take a username. No password field means less variables affecting your timing data. The application still has to process the username identically to how a login would.
Here's the mechanism: when you submit a username that exists, the server finds it in the database and performs additional operations retrieving user data, running comparisons. When the username doesn't exist, the server fails at the lookup and returns immediately. That difference shows up in the Duration column.
Run ffuf against the reset endpoint with your username wordlist:
ffuf -w usernames.txt:FUZZ \
-u http://target.com/resetpassword \
-X POST \
-d "username=FUZZ" \
-H "Content-Type: application/x-www-form-urlencoded"ffuf -w usernames.txt:FUZZ \
-u http://target.com/resetpassword \
-X POST \
-d "username=FUZZ" \
-H "Content-Type: application/x-www-form-urlencoded"The output will look something like this:
sarah [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 571ms]
joey [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 534ms]
adam [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 612ms]
assal [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 1547ms]
james [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 589ms]sarah [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 571ms]
joey [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 534ms]
adam [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 612ms]
assal [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 1547ms]
james [Status: 200, Size: 388, Words: 13, Lines: 11, Duration: 589ms]Status code, response size, words, and lines are identical across all results the application intentionally returns the same response regardless of whether the username exists. Duration is the only signal.
The baseline here sits between 530–612ms. assalat 1547ms is a clear outlier the server found that username and performed additional processing before responding.
One caveat worth noting before you chase every outlier: network jitter makes small timing gaps unreliable. A 20–50ms difference could easily be noise. You're looking for significant, consistent outliers responses sitting 500ms or more above your baseline. If results are close, run the wordlist a second time and compare patterns across both runs rather than trusting a single result.
Injection Bug Fuzzing Let the Parameter Name Tell You What to Test
One important caveat: I don't recommend doing this against a WAF-protected target. The payloads in these lists are well-known and will trigger detection immediately. Where this becomes useful is when you've already found a way around the WAF origin IP access, a bypass via header manipulation, a subdomain that doesn't sit behind the same protection or when you're testing an internal application in a pentest environment where WAF rules aren't in play.
The parameter name itself is usually the biggest hint. file= suggests the application is reading from the filesystem. url= suggests it's making outbound requests. id= suggests it's querying a database. search= suggests it's rendering user input somewhere. The parameter name tells you which injection class to prioritize before you've even sent a single request.
Once you've identified a parameter worth testing, fuzzing it with a targeted wordlist is straightforward:
# LFI — parameter looks like it reads files
ffuf -u https://target.com/index.php?file=FUZZ -w SecLists/Fuzzing/LFI/LFI-Jhaddix.txt
# SSRF - parameter looks like it makes requests
ffuf -u https://target.com/index.php?url=FUZZ -w SecLists/Fuzzing/SSRF/SSRF-Sherlock.txt
# SQLi / Command Injection - parameter looks like a database query
ffuf -u https://target.com/index.php?id=FUZZ -w SecLists/Fuzzing/SQLi/Generic-SQLi.txt
# XSS / SSTI - parameter looks like it renders output
ffuf -u https://target.com/index.php?search=FUZZ -w SecLists/Fuzzing/XSS/XSS-Jhaddix.txt# LFI — parameter looks like it reads files
ffuf -u https://target.com/index.php?file=FUZZ -w SecLists/Fuzzing/LFI/LFI-Jhaddix.txt
# SSRF - parameter looks like it makes requests
ffuf -u https://target.com/index.php?url=FUZZ -w SecLists/Fuzzing/SSRF/SSRF-Sherlock.txt
# SQLi / Command Injection - parameter looks like a database query
ffuf -u https://target.com/index.php?id=FUZZ -w SecLists/Fuzzing/SQLi/Generic-SQLi.txt
# XSS / SSTI - parameter looks like it renders output
ffuf -u https://target.com/index.php?search=FUZZ -w SecLists/Fuzzing/XSS/XSS-Jhaddix.txtthere are many bugs to try out and seclist has many lists of different bugs but the main issue for this technique is the WAF.
When the path is clear, this is a fast way to confirm a vulnerability class before you slow down and exploit it manually.
Closing Thought
Fuzzing is a way of thinking. Every piece of information you gather feeds the next decision. The subdomains you find tell you what to fuzz in vhosts. The directory structure tells you what to build paths around. The parameter names you find tell you what the application cares about.
The best results come from slowing down and reading your output before moving to the next step. The pattern is almost always there.
Hope this helps you out or learned something from.