Back to Articles

Parameth: Brute-Force Parameter Discovery for the APIs Nobody Documented

[ View on GitHub ]

Parameth: Brute-Force Parameter Discovery for the APIs Nobody Documented

Hook

That interesting endpoint you discovered during reconnaissance accepts parameters—you just don't know which ones. While most security scanners move on, Parameth stays behind to ask the uncomfortable questions.

Context

Web application reconnaissance typically follows a predictable pattern: enumerate directories, identify scripts and endpoints, then attempt to understand their functionality. But there's a frustrating gap between finding an endpoint and actually using it effectively. Modern APIs often come with OpenAPI specifications or comprehensive documentation, but legacy applications, internal tools, and poorly maintained web services rarely do. You might discover /api/user.php or /admin/export.cgi during directory enumeration, but without knowing which parameters these endpoints accept, you're stuck guessing or moving on entirely.

This gap becomes particularly painful in penetration testing and bug bounty contexts. Directory brute-forcing tools excel at finding resources, and vulnerability scanners excel at testing known parameters, but the middle ground—discovering which parameters actually exist—remains largely manual work. Developers might use descriptive parameter names in some endpoints (?user_id=123) but completely opaque ones in others (?uid=123, ?u=123, or even ?x=123). Parameth emerged to fill this specific niche: systematically testing parameter names from wordlists and identifying which ones trigger different application behavior, effectively turning parameter discovery into a methodical process rather than educated guesswork.

Technical Insight

URL, wordlist, config

baseline response data

parameter names

queued params

concurrent requests

proxies, headers, cookies

responses

response metrics

size, status, content

offset threshold

detected params

validated results

CLI Input Parser

Baseline Request Module

Response Comparator

Wordlist Reader

Request Queue

Thread Pool Manager

HTTP Request Engine

Target Application

Variance Detector

False Positive Filter

Results Reporter

System architecture — auto-generated

Parameth's core architecture revolves around differential response analysis. When you probe an endpoint with different parameter names, most will be ignored by the application, producing identical responses. But when you hit a parameter the application actually processes, something changes—response size, status code, content structure, or execution time. The tool's job is to detect these changes reliably, even when dealing with dynamic pages that naturally vary between requests.

The basic workflow starts with sending a baseline request without parameters to establish what "normal" looks like. Then Parameth iterates through a wordlist, sending requests with each potential parameter name and comparing responses against this baseline. For a simple GET parameter discovery session, you might run:

python parameth.py -u https://target.com/api/user.php -w params.txt -t 10

This command tests the target URL with 10 concurrent threads, cycling through parameter names from params.txt. But the interesting engineering happens in how it handles response comparison. The tool implements an offset detection mechanism specifically for pages with dynamic content. Consider a PHP page that includes a timestamp or CSRF token in its HTML output—the response size will fluctuate slightly on every request, even without parameter changes. A naive comparison would flag every parameter as potentially valid, creating useless noise.

Parameth addresses this by calculating variance thresholds. After establishing the baseline, it can send multiple test requests to measure typical response size fluctuation. You can configure this with the --variance flag:

python parameth.py -u https://target.com/process.php \
  --variance 10 \
  --wordlist /usr/share/wordlists/params.txt \
  --method POST \
  --header "Authorization: Bearer token123"

Here, --variance 10 tells Parameth to ignore response size differences of 10 bytes or less, accommodating minor fluctuations while still catching meaningful changes. This is crucial for real-world applications where timestamps, session IDs, or random tokens make every response slightly unique.

The POST method support deserves special attention because POST parameter discovery is inherently more complex than GET. With GET parameters, you're appending to the URL query string—straightforward and easily logged. With POST, parameters live in the request body, and applications might expect specific Content-Types (application/x-www-form-urlencoded, multipart/form-data, or even JSON). Parameth handles the standard URL-encoded POST format:

# Simplified conceptual representation of Parameth's approach
import requests
from concurrent.futures import ThreadPoolExecutor

def test_parameter(base_url, param_name, baseline_size, method='GET'):
    if method == 'GET':
        response = requests.get(f"{base_url}?{param_name}=test")
    else:
        response = requests.post(base_url, data={param_name: "test"})
    
    size_diff = abs(response.content_length - baseline_size)
    
    # Check if response differs significantly from baseline
    if size_diff > VARIANCE_THRESHOLD:
        return param_name, response.status_code, response.content_length
    return None

baseline = requests.get(base_url)
baseline_size = baseline.content_length

with ThreadPoolExecutor(max_workers=10) as executor:
    results = executor.map(
        lambda p: test_parameter(base_url, p, baseline_size),
        parameter_wordlist
    )
    
    valid_params = [r for r in results if r is not None]

This multi-threaded approach dramatically speeds up discovery, especially with large wordlists containing thousands of common parameter names. The tool also supports filtering by HTTP status codes, allowing you to focus on parameters that trigger specific responses. For instance, if you're only interested in parameters that cause redirects, you could filter for 302 responses.

One particularly clever feature is Parameth's ability to extract potential parameters from PHP source code when available. If you've somehow obtained the source (perhaps through a directory traversal vulnerability or a Git exposure), you can generate a targeted wordlist:

python parameth.py --php source.php --output custom_params.txt

The tool uses regex patterns to extract variable names from $_GET, $_POST, and $_REQUEST accesses in the PHP code, creating a highly targeted wordlist specific to that application. This transforms parameter discovery from a spray-and-pray operation into surgical precision, dramatically reducing testing time and false positives.

The response analysis also considers percentage-based differences rather than just absolute byte counts. This matters when dealing with endpoints that return large payloads—a 50-byte difference might be significant for a 200-byte response but noise for a 50KB response. Parameth's --diff flag lets you specify a percentage threshold, making the tool adaptable to different application contexts without manual threshold recalculation.

Gotcha

Parameth's most significant limitation is its maintenance status. The project hasn't seen substantial updates in years, evident from its Python 2 origins and dated dependencies. While it generally works with Python 3 after minor modifications, you might encounter issues with modern TLS implementations, updated library APIs, or contemporary web application architectures. Single-page applications built with React or Vue, which heavily rely on JavaScript and API calls rather than traditional parameter-based server-side rendering, may not respond to Parameth's probing in predictable ways.

The tool also requires considerable manual tuning for optimal results. Determining the right variance threshold isn't always straightforward—set it too low and you'll drown in false positives from dynamic content; set it too high and you'll miss subtle but valid parameters. Applications with heavy caching, load balancers that route requests to different backend servers, or aggressive rate limiting can all produce inconsistent results that require multiple test runs and threshold adjustments. There's no automatic calibration mechanism that learns optimal settings for a given target, so you're often iterating through manual testing to find the sweet spot. Additionally, Parameth only tells you that a parameter exists and causes behavioral changes—it doesn't automatically test for vulnerabilities like SQL injection, XSS, or access control issues in those parameters, meaning it's just the first step in a longer testing process.

Verdict

Use if: You're conducting web application penetration testing or bug bounty hunting on legacy or poorly documented applications, particularly PHP-based systems where parameter names aren't obvious from the interface. It's especially valuable when you've already enumerated interesting endpoints through directory brute-forcing and need to understand their attack surface before moving to vulnerability testing. The multi-threaded approach makes it practical for testing large parameter wordlists against multiple discovered endpoints. Skip if: You're working with modern, well-documented APIs that provide OpenAPI specifications or comprehensive documentation, or if you need actively maintained tooling with support for contemporary frameworks. Also skip if you require integrated vulnerability testing rather than just parameter discovery—you'll need to chain Parameth with other tools anyway. For production security workflows, consider more recent alternatives like Arjun or x8 that handle modern web architectures better, or use Burp Suite's Param Miner if you have commercial tool budgets and need advanced features like cache poisoning detection.

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