Back to Articles

Pathbrute: Exploit-Aware Directory Enumeration for Penetration Testing

[ View on GitHub ]

Pathbrute: Exploit-Aware Directory Enumeration for Penetration Testing

Hook

Most directory brute-forcers tell you what exists. Pathbrute tells you what's exploitable—and it does it by testing the same paths attackers actually use in the wild.

Context

Web directory enumeration has a dirty secret: generic wordlists optimized for discovery don't align with what penetration testers actually need. You can find thousands of paths with tools like gobuster or dirsearch, but how many of those lead to known vulnerabilities? How many are just static assets or benign admin panels?

Pathbrute emerged from this gap between discovery and exploitation. Instead of crawling GitHub for every possible directory name or scraping common web frameworks, it curates paths directly from Exploit-DB and Metasploit modules—the actual URIs that have known CVEs, misconfigurations, or exploit modules written for them. Built in Go for concurrent performance, it targets penetration testers who need to move quickly from reconnaissance to exploitation, not just enumerate every possible endpoint. The tool also tackles a specific pain point that anyone who's scanned modern web applications knows intimately: servers that return HTTP 200 for literally everything, making traditional "success response" detection completely useless.

Technical Insight

Request random paths

Yes

No

Per-host

Per-path

Concurrent requests

Compare size/title

Yes

No

Wordlists

Exploit-DB Paths

18,899 entries

Metasploit Modules

442 entries

CMS-Specific Paths

Start Scan

Baseline Detection

Intelligent Mode?

Record Response Signatures

Load Wordlists

Scan Strategy

Host Prioritization

Path Prioritization

Goroutine Worker Pool

HTTP Request Engine

Filter Response

Matches Baseline?

Discard False Positive

CMS Fingerprinting

Report Results

System architecture — auto-generated

Pathbrute's architecture centers on three design decisions that differentiate it from generic directory scanners: exploit-sourced wordlists, intelligent baseline filtering, and flexible scan prioritization.

The wordlist strategy is straightforward but effective. Rather than maintaining generic lists of common directory names, Pathbrute ships with 18,899 paths extracted from Exploit-DB and 442 from Metasploit modules. These aren't just random URIs—they're paths that have historical security relevance. When you scan with Pathbrute, you're testing for /admin/config.php, /backup/db.sql, /sites/default/settings.php and other paths that have actual exploit modules or documented vulnerabilities associated with them. The tool also includes CMS-specific fingerprinting that identifies WordPress, Joomla, and Drupal versions, then cross-references detected versions against Metasploit modules.

The intelligent filtering mode (-i flag) solves the "200 OK for everything" problem that breaks traditional scanners. Here's how it works: before scanning your actual wordlist, Pathbrute first requests several random, definitely-invalid paths to establish a baseline. It records response sizes, HTTP status codes, and even page titles from these non-existent resources. Then during the actual scan, it filters out any responses that match this baseline signature—same size, same title, same behavior. This means on a server configured to return friendly 200 responses for 404s, Pathbrute can still identify genuine endpoints by detecting deviations from the baseline.

The concurrent scanning implementation uses goroutines with configurable thread counts. While the default is conservatively set to 2 threads, you can scale up significantly. Here's a simplified view of how the concurrent request pattern works:

// Conceptual illustration of Pathbrute's concurrent scanning pattern
func scanPaths(targets []string, paths []string, threads int) {
    semaphore := make(chan struct{}, threads)
    var wg sync.WaitGroup
    
    // Establish baseline if intelligent mode enabled
    baseline := establishBaseline(targets[0])
    
    for _, target := range targets {
        for _, path := range paths {
            wg.Add(1)
            semaphore <- struct{}{} // Acquire semaphore
            
            go func(url, uri string) {
                defer wg.Done()
                defer func() { <-semaphore }() // Release semaphore
                
                resp := makeRequest(url + uri)
                if isSignificant(resp, baseline) {
                    reportFinding(url, uri, resp)
                }
            }(target, path)
        }
    }
    wg.Wait()
}

The scan prioritization modes address different attack scenarios. Default behavior iterates through all paths for each host before moving to the next (host-first). But the -x cross-host mode flips this: it tests one path across all targets before moving to the next path. This is invaluable when you're hunting for a specific vulnerability across multiple hosts—imagine testing /phpinfo.php across 50 servers immediately rather than waiting for 49 hosts to complete their full scans first.

Pathbrute also implements nested directory reduction to handle the exponential URI problem. If your wordlist contains both /admin and /admin/config and /admin/config/settings, the tool can intelligently collapse these to avoid redundant requests when parent paths don't exist. This feature alone can reduce scan times by 40-60% on hierarchical wordlists.

The output is pragmatic: URL, HTTP status code, response size, and page title. No JSON schema hell, no XML—just grep-able text that tells you what you need to know. For CMS fingerprinting, it additionally reports detected versions and matching Metasploit module names, creating a direct path from discovery to exploitation.

Gotcha

The intelligent filtering mode is completely opaque—documentation doesn't specify how many baseline requests are made, what constitutes a "significant deviation" in response size, or how dynamic content affects title matching. You're trusting an undocumented algorithm to filter security findings, which is problematic when false negatives could mean missing vulnerabilities. The default thread count of 2 is laughably conservative, but the tool requiring explicit flags for >100 threads suggests instability at scale. There's no retry logic, no rate-limiting handling, no WAF detection—features you'd expect in production-grade security tooling. The curated wordlists are the tool's main value proposition, but they appear static with no clear update schedule. In a space where new CVEs emerge daily, stale exploit paths limit effectiveness. The repository shows minimal recent maintenance, and the truncated README suggests documentation wasn't prioritized. Adding custom wordlists or extending CMS fingerprinting requires reading the source—there's no extension interface or plugin system.

Verdict

Use if: You're conducting penetration tests and need to quickly identify known vulnerable paths rather than enumerate every possible directory; you're dealing with servers that return 200 for everything and need intelligent filtering to cut through the noise; you want CMS fingerprinting that directly maps to Metasploit modules; or you're doing targeted vulnerability hunting across multiple hosts with the cross-host scan mode. Skip if: You need a general-purpose directory scanner with active development (use gobuster or feroxbuster); you require transparent, configurable filtering logic for compliance documentation; you need modern features like WAF detection, rate-limiting, or retry mechanisms; you want regularly updated wordlists reflecting current attack trends; or you need comprehensive documentation for customization and extension. Pathbrute excels in a narrow use case—exploit-focused enumeration during time-boxed penetration tests—but lacks the polish and maintainability for broader security automation workflows.

// ADD TO YOUR README
[![Featured on Starlog](https://starlog.is/api/badge/developer-tools/milo2012-pathbrute.svg)](https://starlog.is/api/badge-click/developer-tools/milo2012-pathbrute)