A path-normalization pentesting tool (inspired by FFUF)
Whats New • Bug Fixes • Installation • Usage • Example Scan • Examples • Contributing • License •
- Unified response filtering under --filter-* flags with stage prefixes (V/F).
- Implemented --drop-after-fail which will ignore requests with the same response code multiple times in a row.
- Added in a --proxy argument, so you can now perform proxy-related tasks such as sending everything to burp.
- Pathbuster will now give you an eta on when the tool will finish processing all jobs.
- Added in a --skip-brute argument, so you have the choice to perform a directory brute force or not.
- Split scan matching into --validate-status and --fingerprint-status for scan-stage control.
- Added in a --skip-validation argument which is used to bypass known protected endpoints using traversals.
- Added in a --header argument which is used to add in additonal headers into each request.
- Added --methods for scanning with one or more HTTP methods (comma-separated).
- Added --path for scanning a single path without a wordlist.
- Added wordlist transforms via --wordlist-manipulation (alias: --wm).
- Added traversal strategy selection via --traversal-strategy (
greedy/quick). - Added bruteforce gating via --wordlist-status and output filtering via --disable-show-all.
- Added bruteforce batching via --brute-queue-concurrency and optional noise filtering via --ac.
Install rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | shInstall pathbuster
cargo install pathbusterpathbuster -hThis command will show the tool's help information and present a list of all the switches that are available.
On first run, Pathbuster will create a default config file at:
- Linux/macOS:
~/.pathbuster/config.yml - Windows:
%USERPROFILE%\\.pathbuster\\config.yml
The generated config contains the default settings, with optional keys commented out.
You can also explicitly point to a config file:
pathbuster --config ./config.yml- Targets:
--url <URL>(repeatable) or--input-file <FILE> - Payloads:
--payloads <FILE> - Raw request:
--raw-request <FILE>(optional, uses*as injection points) - Bruteforce target:
--wordlist <FILE>(recommended) or--path <PATH>and optional--wordlist-dir <DIR> - Wordlist manipulation (optional):
--wordlist-manipulation <LIST>(alias:--wm) - Output:
--output <FILE>and optional--output-format <text|json|xml|html> - Output filtering:
--disable-show-all(only show matches allowed by--wordlist-status) - Networking:
--proxy <URL>,--follow-redirects,--timeout <SECONDS>,--methods <METHODS> - Validation tuning:
--response-diff-threshold <MIN-MAX> - Bruteforce tuning:
--wordlist-status <CODES>,--brute-queue-concurrency <N>,--ac - Filters:
--filter-status <SET>(e.g.V:404,F:500or404,500for both stages)--filter-size <SET>(e.g.V:1234,F:5678)--filter-words <SET>(e.g.V:10,F:25)--filter-lines <SET>(e.g.V:5,F:20)--filter-regex <STAGE:REGEX>(repeatable, e.g.--filter-regex V:<re> --filter-regex F:<re>)- Stage-scoped filters and status matchers work with both traversal strategies (
greedyandquick)
Pathbuster has two traversal strategies you can select via --traversal-strategy:
- Probes depths from
--start-depthup to--max-depthto find a depth where--fingerprint-statusmatches. - Once a “fingerprint depth” is found, it validates every depth back down toward
--start-depth. - Higher request count, but more consistent when you don’t know how the target normalizes paths.
- Computes the “fingerprint depth” directly from the target URL path segment count plus
--start-depth(clamped to--max-depth). - Validates a minimal set of depths (favoring speed over coverage) and moves on.
- Lower request count, best when your base URL has meaningful path segments (e.g.
https://example.com/app/).
| Flag | Value | Description |
|---|---|---|
-u, --url |
URL (repeatable) |
Target URL(s) to scan. |
-i, --input-file |
FILE |
Load target URLs from a file (one per line). |
-C, --config |
FILE |
Config file path (defaults to ~/.pathbuster/config.yml). |
--payloads |
FILE |
Payload file path (one payload per line). |
--raw-request |
FILE |
Raw HTTP request template with * injection points. |
--wordlist |
FILE |
Wordlist file path (one word per line). |
--path |
PATH |
Scan a single path instead of using a wordlist. |
--wordlist-dir |
DIR |
Targeted wordlist directory (auto-selected by tech fingerprint). |
--wordlist-manipulation, --wm |
LIST |
Comma-separated wordlist transforms (see Wordlist manipulation). |
--skip-brute |
(flag) | Skip bruteforce/discovery phase. |
-s, --skip-validation |
(flag) | Skip validation phase and go straight to bruteforce/discovery. |
-r, --rate |
RPS |
Request rate limit (requests per second). |
-t, --concurrency |
N |
Max in-flight requests during scanning. |
-w, --workers |
N |
Number of runtime worker threads. |
--timeout |
SECONDS |
Per-request timeout. |
-p, --proxy |
URL |
HTTP proxy URL (e.g. http://127.0.0.1:8080). |
--follow-redirects |
(flag) | Follow HTTP redirects. |
--header |
HEADER |
Add a header to all requests (Key: Value). |
-m, --methods |
METHODS |
Comma-separated HTTP methods to use (e.g. GET,POST). |
--wordlist-status, --ws |
CODES |
Allowed status codes for bruteforce findings (comma-separated). |
--brute-queue-concurrency, --bqc, --bfc |
N |
Max base URLs per bruteforce batch (0 = no batching). |
--ac |
(flag) | Enable automatic collaboration filtering during bruteforce. |
--drop-after-fail |
CODES |
Stop scanning a target after these status codes (comma-separated). |
--validate-status, --vs |
CODES |
HTTP status matcher for validation phase. |
--fingerprint-status |
CODES |
HTTP status matcher for fingerprinting phase. |
--filter-status |
SET |
Exclude responses by HTTP status using stage prefixes (e.g. V:404,F:500). |
--filter-size |
SET |
Exclude responses by body size using stage prefixes (e.g. V:1234,F:5678). |
--filter-words |
SET |
Exclude responses by word count using stage prefixes (e.g. V:10,F:25). |
--filter-lines |
SET |
Exclude responses by line count using stage prefixes (e.g. V:5,F:20). |
--filter-regex |
STAGE:REGEX |
Exclude responses matching regex using stage prefixes (repeatable). |
-d, --response-diff-threshold, --rdt |
MIN-MAX |
Response diff threshold range for comparisons. |
-I, --ignore-trailing-slash, --its |
(flag) | Treat URLs with/without trailing slash as equivalent. |
--start-depth |
N |
Initial traversal depth (0-based). |
--max-depth |
N |
Maximum traversal depth. |
-X, --traversal-strategy, --ts |
STRATEGY |
Traversal strategy (greedy or quick). |
--disable-fingerprinting |
(flag) | Disable fingerprinting (WAF/tech). |
--waf-test |
NAME |
Only test for a specific WAF signature by name. |
--tech |
NAME |
Override detected tech name for targeted wordlist selection. |
--disable-waf-bypass |
(flag) | Disable WAF-aware payload transformations. |
--bypass-level |
N |
Bypass aggressiveness level (0-3). |
--bypass-transform |
NAME (repeatable) |
Force specific payload transform families. |
-o, --output |
FILE |
Write results to a file. |
--output-format |
FORMAT |
Output format (e.g. text, json). |
-v, --verbose |
(count) | Increase verbosity (-v, -vv). |
-c, --color |
(flag) | Enable colored output (overrides --no-color). |
--disable-show-all, --dsa |
(flag) | Only show findings matching --wordlist-status. |
--no-color |
(flag) | Disable colored output. |
-h, --help |
(flag) | Print help. |
-V, --version |
(flag) | Print version. |
--wordlist-manipulation <LIST> (alias: --wm) applies one or more transforms to the bruteforce wordlist before scanning.
LIST is a comma-separated list of transforms:
sort: Sort words (also enablesuniqueviadedupwhen both are set)unique/uniq: Deduplicate words (preserves first-seen order unlesssortis also set)reverse/rev: Reverse each wordlower: Lowercase each wordupper: Uppercase each wordtitle: Title-case each word (ASCII)prefix=<STR>: Prefix each word with<STR>suffix=<STR>: Suffix each word with<STR>replace=<FROM:TO>: Replace substring<FROM>with<TO>(repeatable by adding multiplereplace=...entries)smart: Split naming conventions into separate words (AdminPanel->Admin,Panel;admin_panel->admin,panel;admin-panel->admin,panel)smartjoin=<CASE:SEP>: Split then join withSEP(CASE is one ofc,l,u,tor empty). Example:smartjoin=l:_turnsAdminPanelintoadmin_panel.
Examples:
pathbuster \
--url https://example.com/app/ \
--payloads ./payloads/traversals.txt \
--wordlist ./wordlists/wordlist.txt \
--wordlist-manipulation sort,unique,lower,replace=..%2f:../pathbuster \
--url https://example.com/app/ \
--payloads ./payloads/traversals.txt \
--wordlist ./wordlists/wordlist.txt \
--wm smartjoin=l:_Basic usage (single target):
pathbuster --url https://example.com/app/ \
--payloads ./payloads/traversals.txt \
--wordlist ./wordlists/wordlist.txt \
--output ./output.html --output-format htmlpathbuster --url https://example.com/app/ \
--methods GET,POST \
--payloads ./payloads/traversals.txt \
--wordlist ./wordlists/wordlist.txt \
--skip-brutepathbuster --url https://example.com/app/ \
--payloads ./payloads/traversals.txt \
--path admin \
--skip-brutepathbuster \
--url https://example.com/app/ \
--payloads ./payloads/traversals.txt \
--wordlist ./wordlists/wordlist.txt \
--wordlist-dir ./wordlists/targeted \
--rate 500 \
--concurrency 200 \
--timeout 10 \
--response-diff-threshold 5-1000 \
--validate-status 404 \
--fingerprint-status 400,500 \
--filter-status V:301,302,F:404 \
--bypass-level 2 \
--follow-redirectsCreate a request file containing one or more * markers:
GET /app/* HTTP/1.1
Host: example.com
User-Agent: pathbuster
Accept: */*
Then run:
pathbuster \
--url https://example.com/app/ \
--raw-request ./request.txt \
--payloads ./payloads/traversals.txt \
--wordlist ./wordlists/wordlist.txtCreate or edit ~/.pathbuster/config.yml:
rate: 500
concurrency: 200
timeout: 10
payloads: ./payloads/traversals.txt
wordlist: ./wordlists/wordlist.txt
# Or scan a single path without a wordlist:
# path: admin
wordlist_dir: ./wordlists/targeted
wordlist_manipulation: "sort,unique,lower"
wordlist_status: "200"
disable_show_all: false
validate_status: "404"
fingerprint_status: "400,500"
response_diff_threshold: "5-1000"
filter_status: ""
filter_size: ""
filter_words: ""
filter_lines: ""
filter_regex: []
follow_redirects: true
disable_fingerprinting: false
disable_waf_bypass: false
bypass_level: 3
bypass_transform: []Run using that config:
pathbuster --url https://example.com/app/ --config ~/.pathbuster/config.ymlpathbuster --url https://example.com/app/ \
--path internal/admin \
--validate-status 404 \
--fingerprint-status 404 \
--traversal-strategy quick \
--max-depth 5 \
--rate 50 \
--concurrency 20 \
--timeout 5 \
--wordlist-status 200 \
--brute-queue-concurrency 3 \
--disable-show-allLibrary usage examples are in the examples folder.
- Basic runner example: library_scan.rs
- Inline payloads + raw request example: library_scan_inline.rs
- Filters example: library_scan_filters.rs
- Skip-validation + wordlist manipulation example: library_scan_skip_validation.rs
Run the example binary:
cargo run --example library_scanYou can also run scans by constructing a Runner with Options and calling run().
Basic example (same as library_scan.rs):
use std::error::Error;
use pathbuster::runner::{Options, PayloadSource, Runner};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let runner = Runner::new(Options {
urls: vec!["https://example.com/app/".to_string()],
payloads: PayloadSource::FilePath("./payloads/traversals.txt".to_string()),
skip_brute: true,
rate: 10,
concurrency: 10,
timeout_seconds: 5,
max_depth: 2,
..Options::default()
})?;
let result = runner.run().await?;
println!("Targets: {}", result.fingerprints.len());
println!("Matches: {}", result.matches.len());
for m in result.matches.iter() {
println!("{} {} {}", m.base_url, m.status, m.result_url);
}
Ok(())
}Do not run automated scans, brute forcing, or high-rate tooling (including Pathbuster) against PentesterLab infrastructure or any training platform you do not own or explicitly have permission to test.
If this project helps you uncover any interesting or impactful bugs, I'd really appreciate a bit of support or recognition. A shout-out on Twitter/X (@z0idsec) or buying me a coffee goes a long way in supporting continued development and research.
Thanks for checking it out - hope you enjoy using it.
Contributions are welcome and appreciated. If you're planning a significant change, please open an issue first to discuss the proposed approach and ensure alignment before submitting a pull request.
When submitting changes, please ensure that any relevant tests are updated or added as appropriate.
Thanks to everyone who has contributed to this project.
Pathbuster is distributed under MIT License
