Old Kit, New Kit Part 5: Content and Parameter Discovery — Feroxbuster and Ffuf vs Dirb and Gobuster
This is the fourth tool-pair actually run in a series that’s testing classic, well-documented Kali tooling against its quieter modern replacements, with a genuine look at what a lab Wazuh deployment sees from each (Part 1 has the full lab build). This one is the first pair in the whole series where the default Wazuh ruleset produced a real, unmissable alert footprint, not a handful of login-noise lines. Content and parameter discovery: dirb and gobuster against feroxbuster and ffuf, run for real against target-web (a minimal Debian host with deliberate misconfigurations) and target-wordpress (a live WordPress install), using the exact same wordlist across all four tools so the comparison is fair.
How this was verified
All four tools ran against both targets using /usr/share/wordlists/dirb/common.txt, on the same isolated lab bridge as the rest of this series, with the Wazuh manager watching both targets as enrolled agents. Every timing and count below is a real result from an actual run — including two results that needed a second pass to explain properly rather than report at face value.
The classic baseline: dirb and gobuster
Against target-web, dirb found 3 paths in 1.66 seconds:
$ dirb http://10.10.10.30/ /usr/share/wordlists/dirb/common.txt -o dirb-target-web.txt
-----------------
DIRB v2.22
By The Dark Raver
-----------------
GENERATED WORDS: 4612
---- Scanning URL: http://10.10.10.30/ ----
+ http://10.10.10.30/index.html (CODE:200|SIZE:431)
+ http://10.10.10.30/robots.txt (CODE:200|SIZE:102)
+ http://10.10.10.30/server-status (CODE:403|SIZE:316)
-----------------
DOWNLOADED: 4612 - FOUND: 3
gobuster, same wordlist and target, found 6 in 0.58 seconds — the same three plus .hta, .htaccess, and .htpasswd, all reported as 403. That discrepancy was worth chasing down rather than shrugging off, since both tools tested the identical wordlist against the identical host:
$ curl -s -o /dev/null -w "status:%{http_code} size:%{size_download}\n" http://10.10.10.30/.htaccess
status:403 size:226316
$ curl -s -o /dev/null -w "status:%{http_code} size:%{size_download}\n" http://10.10.10.30/.htpasswd
status:403 size:226316
$ curl -s -o /dev/null -w "status:%{http_code} size:%{size_download}\n" http://10.10.10.30/this-definitely-does-not-exist-xyz
status:404 size:226313
Apache is serving essentially the same generic error page for both codes here — 226316 bytes for the two 403s, 226313 bytes for a deliberately made-up 404. dirb runs its own not-found heuristic against a baseline signature, and this near-identical body is evidently close enough that dirb quietly treats the .ht* hits as false positives and drops them, even though the status code itself differs. gobuster’s simpler status-code-only matcher doesn’t care about body content, 403 is in its default include list, so it reports them. Neither tool is wrong; they’re just using different definitions of “found,” and that’s exactly the kind of thing this series is meant to surface.
Against target-wordpress, dirb (which recurses into discovered directories by default) found 12 in 9.55 seconds; gobuster (non-recursive by default) found 9 in 2.45 seconds.
The modern tools on target-web: two files nothing else found
feroxbuster on target-web ran in 1.62 seconds and reported found:7 errors:44 — the same three baseline hits, plus two files neither dirb, gobuster, nor ffuf turned up on the identical wordlist:
$ curl -s -o backup.zip http://10.10.10.30/backup.zip && file backup.zip && unzip -l backup.zip
backup.zip: empty
Archive: backup.zip
End-of-central-directory signature not found.
$ curl -s http://10.10.10.30/internal-notes.txt
TODO:
- Rotate the staging DB password (still the vendor default from the migration - jsmith flagged this in March and it never got done)
- Port 2121 was left open after the FTP migration, close it
- backup.zip in the web root needs moving to the archive server
backup.zip turned out to be a 0-byte placeholder, not a real archive. internal-notes.txt is the actual find: a plaintext ops TODO listing a default database password that was flagged and never rotated, an orphaned port left open from an old migration, and — confirming it’s the same file sitting right next to it — an explicit note that backup.zip needs moving off the web root. feroxbuster’s default link-extraction is what surfaced this; ffuf, run against the same target and wordlist, matched dirb and gobuster’s set exactly (7 matches in 0.46 seconds) and found neither extra file, which rules out a wordlist difference and confirms it was feroxbuster’s crawling behaviour specifically.
target-wordpress: what an 11-minute recursive crawl actually finds
feroxbuster against target-wordpress took 668.39 seconds — over 11 minutes — and reported found:1389 errors:30738. ffuf, non-recursive by default, found 10 matches in 0.52 seconds on the same target and wordlist.
The obvious first suspicion with a number like 1389 is that WordPress’s pretty-permalink structure is serving a soft-404 template that’s getting counted as distinct hits over and over:
$ curl -s -o /dev/null -w "status:%{http_code} size:%{size_download}\n" "http://10.10.10.40/index.php/2026/07/totally-made-up-nonsense-path-xyz123/"
status:404 size:57601
$ curl -s -o /dev/null -w "status:%{http_code} size:%{size_download}\n" "http://10.10.10.40/wp-login.php"
status:200 size:4645
That pattern is real, but it turned out to be a minor factor, not the main driver: only 40 of the 1751 total lines in feroxbuster’s output reference that index.php/2026/07/... path pattern. The actual driver is legitimate recursion:
$ wc -l ferox-target-wordpress.txt
1751 ferox-target-wordpress.txt
$ grep -c "wp-includes" ferox-target-wordpress.txt
1184
$ grep -c "wp-admin" ferox-target-wordpress.txt
209
1184 of 1751 lines reference wp-includes alone. feroxbuster’s default recursion (depth 4) plus link-extraction walked into WordPress’s real, large core file tree — jQuery, the ID3 audio-metadata library, certificate bundles, and hundreds of individual class-*.php files, most returning HTTP 200 with an empty body since PHP executing outside the WordPress bootstrap simply produces no output:
301 GET 9l 29w 356c http://10.10.10.40/wp-includes => http://10.10.10.40/wp-includes/
MSG 0.000 feroxbuster::heuristics detected directory listing: http://10.10.10.40/wp-includes (Apache)
200 GET 0l 0w 0c http://10.10.10.40/wp-includes/class-wp-post.php
200 GET 0l 0w 0c http://10.10.10.40/wp-includes/class-wp-user-request.php
That heuristics line matters more than the raw count: feroxbuster’s own directory-listing detection flagged live Apache directory listing enabled on wp-includes and several other subdirectories — a concrete, real misconfiguration that none of the other three tools across this entire series has surfaced. So found:1389 isn’t a fabricated number and it isn’t mostly duplicate noise either; it’s mostly genuine recursion into a real, sprawling directory tree. The honest caveat is that it overstates interesting findings, most of those 1389 lines are routine WordPress core files rather than anything an attacker or defender would specifically care about, but the number itself reflects real requests to real, distinct paths.
Comparison
| target-web found | target-web time | target-wordpress found | target-wordpress time | |
|---|---|---|---|---|
| dirb | 3 (missed .ht*) | 1.66s | 12 (recursive) | 9.55s |
| gobuster | 6 | 0.58s | 9 (non-recursive) | 2.45s |
| feroxbuster | 7 (+backup.zip, internal-notes.txt) | 1.62s | 1389 (mostly real wp-includes recursion) | 668.39s |
| ffuf | 7 | 0.46s | 10 (non-recursive) | 0.52s |
What Wazuh actually saw
Every earlier pair in this series came back from the Wazuh alert log with essentially nothing — our own SSH logins, and no trace of the actual scans. This one is different, and it took a second attempt to measure honestly. The first pass grepped the alerts log for the bare rule numbers and came back with what looked like a huge but suspicious result, since those same digit sequences can also turn up inside the alert log’s own epoch-timestamp IDs. Re-running it against the literal Rule: line instead gave a number that held up:
$ ssh -t -i ~/.ssh/wazuh-lab anon@10.10.10.10 "sudo grep -c 'Rule: 31101' /var/ossec/logs/alerts/alerts.log; sudo grep -c 'Rule: 31151' /var/ossec/logs/alerts/alerts.log"
75228
5908
75228 hits on rule 31101 (level 5, “Web server 400 error code”) and 5908 hits on rule 31151 (level 10, “Multiple web server 400 error codes from same source ip”) — a correlation rule that only fires once a threshold of individual alerts has already been crossed, not a single log-line match. Checking the actual timestamps turned up one more real-world gotcha: the Wazuh manager logs in UTC while the Kali box logs local time (BST, UTC+1), so an alert timestamped 17:13:35 in the manager’s log lines up with 18:13:20 in Apache’s own access log for the same event — a full hour offset that has to be accounted for before trusting any timing correlation between the two. Once adjusted, the alerts track continuously across the entire roughly 18-minute test window covering dirb, gobuster, feroxbuster, and ffuf back to back, at a sustained rate on the order of 30-37 alerts per second.
This is the first pair in the series where Wazuh’s default configuration produces a genuinely unmissable signal — a defender watching this manager wouldn’t need a custom rule or a hunch to notice it, the correlation rule alone would already be screaming. It’s exactly the outcome the outline predicted for this pair going in, and a sharp contrast to the zero-alert results from the port-scanning and fingerprinting pairs earlier in this series.
Verdict
For raw wordlist matching against a normal target, ffuf is simply better than dirb or gobuster on every axis that matters here: it’s the fastest of the four in both runs, it matched the same baseline set exactly, and its non-recursive default keeps it predictable. feroxbuster is a different tool with a different job: its default recursion and link-extraction genuinely find things a flat wordlist scan can’t, a real leaked ops file on target-web and confirmed directory listing on target-wordpress, but that thoroughness costs 400x the time against a large real site and produces a number that needs explaining rather than quoting. Neither is a strict upgrade over the other; feroxbuster is what you reach for when you specifically want depth, ffuf is what you reach for everywhere else. On detection, this pair broke the pattern set by the rest of the series so far: high request volume against a web server is exactly the traffic shape Wazuh’s default web-log ruleset is built to catch, and for the first time in this series, it actually did.