Introduction to Web Fuzzing
Web fuzzing is used when a site does not link to hidden pages or does not expose anything useful through normal navigation. The technique sends many controlled inputs to an interface to see how the server responds. In some notes, the Spanish verb for fuzzing is translated as borrar, but the actual goal is discovery. When you do this correctly, you can reveal directories, pages, subdomains, and parameters that are not linked anywhere.
Fuzzing relies on the idea that web servers do not publish a full list of content. By sending wordlist entries and watching HTTP status codes and response sizes, you can determine which resources exist. Tools can send hundreds of requests per second, which makes manual browsing unnecessary. This is why fuzzing is one of the first steps when reconnaissance is stuck.
Wordlists and Seclists
Wordlists are the fuel for fuzzing because they contain common directory and file names. This is similar to a dictionary attack, but for web paths and parameters. Wordlists can find a large percentage of pages even when the names are not obvious. For most web tests, SecLists is the standard reference for high quality lists.
SecLists organizes lists by fuzzing type, such as web content, DNS, and parameters. You can browse the lists and select the smallest or most specific set to reduce noise. A good list often returns 70 to 90 percent of discoverable pages on a typical target. If the site uses unique names, you can still build your own list from context.
Basic Fuzzing with ffuf
ffuf is a fast fuzzing tool and is installed by default in PwnBox. You can also install it locally using apt install ffuf -y or build it from the GitHub repository. The two most important options are -w for the wordlist and -u for the target URL. The placeholder FUZZ is replaced by each word in the list.
Start with ffuf -h to see the core options. The flags you will use often are -w, -u, -X, -d, -H, -recursion, -recursion-depth, -e, and filtering options like -fs or -mc. You do not need every flag every time, but you should know what each one does. This keeps your scans targeted and repeatable.
ffuf -h
Directory Fuzzing
Directory fuzzing looks for hidden directories by placing FUZZ where a directory name would appear. This is the most common first scan because it reveals admin panels, upload paths, or API roots. The wordlist used here is usually a directory list like directory-list-2.3-small.txt. The output will show which directories return a non-404 response.
ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://SERVER_IP:PORT/FUZZ
Page Extension Discovery
If a directory exists but looks empty, the next step is to identify file extensions. This tells you whether the site uses .php, .aspx, .html, or another extension. You place FUZZ where the extension should be, such as indexFUZZ. Use a small extensions wordlist to keep the scan fast.
ffuf -w /opt/useful/seclists/Discovery/Web-Content/web-extensions.txt:FUZZ -u http://SERVER_IP:PORT/blog/indexFUZZ
You can also use two wordlists at once with two placeholders. For example, use FUZZ_1.FUZZ_2 to test file names and extensions in one scan. This is useful when the file name is not always index.
Page Fuzzing
Once you know the extension, fuzz for actual pages inside the directory. This uses the same directory wordlist but places FUZZ in the file name and adds the extension. This often reveals hidden scripts, backups, and configuration pages. The same approach works on any directory you discover.
ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://SERVER_IP:PORT/blog/FUZZ.php
Recursive Fuzzing
Recursive fuzzing tells ffuf to keep scanning inside newly discovered directories. This is useful for deep directory trees like /login/user/content/uploads. To control time and noise, set a recursion depth. You can also add extensions with -e and print full URLs with -v.
ffuf -w /opt/useful/seclists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://SERVER_IP:PORT/FUZZ -recursion -recursion-depth 1 -e .php -v
Domain and DNS Fuzzing
Domain fuzzing is used when the target is accessed by a name rather than a direct IP. Subdomains and vhosts can expose additional applications that are not visible on the main site. A subdomain is a DNS record like photos.google.com, while a vhost is a different site served on the same IP. The techniques look similar but the way you reach them is different.
DNS and /etc/hosts
If a domain is not public, your system cannot resolve it through public DNS. In that case, add it to /etc/hosts so your browser knows where to connect. This is required for internal targets like academy.htb or custom lab domains. You will use the same step before fuzzing parameters on vhost targets.
sudo sh -c 'echo "SERVER_IP academy.htb" >> /etc/hosts'
Subdomain Fuzzing
Subdomain fuzzing discovers public DNS entries by replacing the subdomain with FUZZ. Use a DNS subdomain list from SecLists and point the URL at the target domain. This works when the DNS record exists and points to a reachable server. The responses show which subdomains are valid.
ffuf -w /opt/useful/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u https://FUZZ.inlanefreight.com/
Vhost Fuzzing
Vhost fuzzing is used when subdomains are not publicly listed in DNS. The idea is to fuzz the Host header while keeping the URL fixed. All requests go to the same IP, but the server responds differently when the host name is valid. This lets you find hidden sites on a shared server.
ffuf -w /opt/useful/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://academy.htb:PORT/ -H 'Host: FUZZ.academy.htb'
Result Filtering
Vhost fuzzing often returns 200 OK for every entry, which makes the output noisy. In that case, filter by response size with -fs. Identify the common size of invalid responses and filter it out. This quickly highlights the real vhosts.
ffuf -w /opt/useful/seclists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://academy.htb:PORT/ -H 'Host: FUZZ.academy.htb' -fs 900
Parameter Fuzzing
Parameter fuzzing finds hidden input names that control access or behavior. This is common on admin panels where a parameter is required to reveal content. The workflow is to fuzz parameter names first, then fuzz values if needed. You must add the domain to /etc/hosts before scanning parameterized pages.
GET Parameters
GET parameters appear after ? in the URL, so you fuzz them by placing FUZZ where the parameter name should be. Use a parameter name list such as burp-parameter-names.txt. Filter by response size to reduce false positives. This is the fastest way to find hidden keys.
ffuf -w /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u http://admin.academy.htb:PORT/admin/admin.php?FUZZ=key -fs xxx
POST Parameters
POST parameters are sent in the body, not in the URL, so you use -X POST and -d. Keep the content type as application/x-www-form-urlencoded and replace the parameter name with FUZZ. Once you find a parameter, you can test it with curl to see how the app responds.
ffuf -w /opt/useful/seclists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u http://admin.academy.htb:PORT/admin/admin.php -X POST -d 'FUZZ=key' -H 'Content-Type: application/x-www-form-urlencoded' -fs xxx
curl http://admin.academy.htb:PORT/admin/admin.php -X POST -d 'id=key' -H 'Content-Type: application/x-www-form-urlencoded'
Value Fuzzing with Custom Lists
When a parameter is found, the next step is to fuzz its values. If the values are numeric, generate a list with a simple loop. This is common for IDs that are sequential. You can do it in bash or fish and then use the list in ffuf.
for i in $(seq 1 1000); do echo $i >> ids.txt; done
for i in (seq 1 1000)
echo $i >> ids.txt
end
ffuf -w ids.txt:FUZZ -u http://admin.academy.htb:PORT/admin/admin.php -X POST -d 'id=FUZZ' -H 'Content-Type: application/x-www-form-urlencoded' -fs xxx
curl http://admin.academy.htb:PORT/admin/admin.php -X POST -d 'id=73' -H 'Content-Type: application/x-www-form-urlencoded'
Full Workflow Example
A full fuzzing workflow combines DNS or vhost discovery, extension discovery, page discovery, and parameter fuzzing. The sequence matters because each step feeds the next. When you find a new subdomain or vhost, add it to /etc/hosts and repeat the scan for that host. This approach builds a clean map of the application surface.
Start by finding DNS subdomains or vhosts, then check file extensions and pages. Use recursion if the site is large, but keep depth controlled. Next, fuzz parameter names on interesting pages and verify any hits with curl. Finally, fuzz parameter values with a custom list if needed.
# DNS and Vhost discovery
ffuf -w /SecLists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://FUZZ.academy.htb:PORT/
ffuf -w SecLists/Discovery/DNS/subdomains-top1million-5000.txt:FUZZ -u http://academy.htb:PORT/ -H "Host: FUZZ.academy.htb" -fs xxx
# Extension discovery
ffuf -w SecLists/Discovery/Web-Content/web-extensions.txt:FUZZ -u http://test.academy.htb:PORT/indexFUZZ
ffuf -w SecLists/Discovery/Web-Content/web-extensions.txt:FUZZ -u http://archive.academy.htb:PORT/indexFUZZ
ffuf -w SecLists/Discovery/Web-Content/web-extensions.txt:FUZZ -u http://faculty.academy.htb:PORT/indexFUZZ
# Page discovery with recursion
ffuf -w SecLists/Discovery/Web-Content/directory-list-2.3-small.txt:FUZZ -u http://academy.htb:PORT/FUZZ -recursion -recursion-depth 1 -e .php,.php7,.phps -fs 287
# Parameter discovery and value fuzzing
ffuf -w SecLists/Discovery/Web-Content/burp-parameter-names.txt:FUZZ -u http://faculty.academy.htb:PORT/courses/linux-security.php7 -X POST -d 'FUZZ=key' -H 'Content-Type: application/x-www-form-urlencoded' -fs xxx
ffuf -w SecLists/Usernames/xato-net-10-million-usernames.txt:FUZZ -u http://faculty.academy.htb:PORT/courses/linux-security.php7 -X POST -d 'username=FUZZ' -H 'Content-Type: application/x-www-form-urlencoded' -fs 781
curl http://faculty.academy.htb:PORT/courses/linux-security.php7 -X POST -d 'username=harry' -H 'Content-Type: application/x-www-form-urlencoded'
Reference
This article is based on my personal study notes from the Information Security Foundations track.
Full repository: https://github.com/lameiro0x/pentesting-path-htb