Mastering FFUF: Advanced Techniques for Hidden Endpoint and API Attack Surface Discovery
Moving beyond rudimentary directory busting, advanced FFUF techniques are crucial for uncovering the elusive endpoints and API attack surface often missed by standard reconnaissance. This isn't about throwing common wordlists; it's about surgical precision, dynamic payload generation, and sophisticated response analysis to reveal hidden functionality and exploitable paths.
FFUF Fundamentals: A Quick Recap
At its core, FFUF leverages a "FUZZ" keyword placeholder within HTTP requests, substituting it with entries from a provided wordlist. A basic directory bruteforce demonstrates this principle:
ffuf -w /usr/share/seclists/Discovery/Web-Content/common.txt -u https://target.com/FUZZ -mc 200,301,302,403
This command iterates through a wordlist, testing each entry as a path segment, and matches successful responses with HTTP status codes 200, 301, 302, or 403.
Recursive Fuzzing: Mapping the Depths
Hidden endpoints often reside several directories deep. Simple linear fuzzing won't cut it. FFUF's recursive mode automates this deeper exploration, following discovered directories to uncover further nested paths.
The -recursion flag enables this behavior, and -recursion-depth controls how deep FFUF will go. It's essential to set a reasonable depth to avoid overly long scans or getting lost in endless trees.
ffuf -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt -u https://target.com/FUZZ -recursion -recursion-depth 2 -e .php,.html,.json -mc all -fc 404,429
Here, FFUF will fuzz the initial path, and if a directory is found, it will then fuzz within that new directory up to two levels deep, appending common extensions like .php, .html, and .json.
Header Fuzzing: Unmasking Hidden Controls
Beyond URL paths, HTTP headers are a fertile ground for hidden functionality. Applications might expose debug endpoints, administrative panels, or bypass security mechanisms based on specific header values (e.g., X-Forwarded-For, X-Custom-Admin, Authorization). Fuzzing headers involves placing the FUZZ keyword directly within the header's value or even the header's name.
Fuzzing Header Values:
ffuf -w /path/to/custom_tokens.txt -u https://target.com/api/v1/admin_panel -H "X-Admin-Token: FUZZ" -mc 200,302
This attempts to bypass a custom administrative token. Similarly, for uncovering internal routes via X-Forwarded-For:
ffuf -w /path/to/internal_ips.txt -u https://target.com/ -H "X-Forwarded-For: FUZZ" -H "Host: admin.internal.target.com" -mc 200,302
Fuzzing Header Names:
In rare cases, an application might react differently to an unrecognized header name, hinting at an undocumented feature. For this, a wordlist of common or custom header names can be used.
ffuf -w /path/to/header_names.txt -u https://target.com/api/status -H "FUZZ: true" -mc all -fs 1337
The -fs 1337 filters for responses with a specific size, often indicating a different server response than the default.
Parameter Fuzzing: Query Strings and JSON Bodies
APIs and web applications heavily rely on parameters. FFUF excels at discovering both known and unknown parameters, and their accepted values, whether in URL query strings or POST data.
GET Parameter Fuzzing:
To find hidden GET parameters for an existing endpoint:
ffuf -w /usr/share/seclists/Discovery/Web-Content/burp-parameter-names.txt -u https://target.com/api/search?query=test&FUZZ=value -mc 200,302 -fs 0,100
This fuzzer uses a wordlist for parameter names, keeping a static value for query. The -fs 0,100 attempts to filter out responses that are very small or empty, which often signify no change or an invalid parameter.
POST Data (JSON) Fuzzing:
For API endpoints expecting JSON payloads, FFUF can inject the FUZZ keyword directly into the JSON structure. Remember to set the Content-Type header accordingly.
ffuf -w /usr/share/seclists/Fuzzing/Common-Parameter-Names.txt -X POST -H "Content-Type: application/json" -d '{"username":"admin", "password":"FUZZ"}' -u https://target.com/api/login -fc 401,403
This example fuzzed the password field in a JSON login request. For broader parameter discovery within JSON, you might need to iterate through potential keys or use tools that can parse and modify JSON structures dynamically.
Advanced Filtering and Matching: Cutting Through Noise
The sheer volume of FFUF output can be overwhelming. Effective filtering is paramount for distinguishing relevant findings from noise. FFUF offers a rich set of matchers (-mc, -ms, -ml, -mw, -mr) and filters (-fc, -fs, -fl, -fw, -fr).
-mc/-fc: Match/Filter by HTTP status code. Example:-mc 200,301 -fc 404(match 200/301, filter 404).-ms/-fs: Match/Filter by response size. This is often more reliable than status codes when default error pages return 200 OK.-ml/-fl: Match/Filter by number of lines in response.-mw/-fw: Match/Filter by number of words in response.-mr/-fr: Match/Filter by regular expression in the response body. Useful for specific error messages or content patterns.
The -ac (auto-calibrate) flag can intelligently set filters based on a few initial requests, though manual tuning is often superior for complex targets.
ffuf -w /usr/share/seclists/Discovery/Web-Content/raft-large-directories.txt -u https://target.com/FUZZ -mc 200 -fs 100-2000 -fr "login failed|Access Denied"
This command matches HTTP 200 responses with sizes between 100 and 2000 bytes, while filtering out responses containing "login failed" or "Access Denied" to reduce false positives.
Smart Wordlist Generation and Augmentation
The quality of your FFUF results directly correlates with the quality of your wordlists. Generic wordlists are a starting point, but bespoke lists derived from the target are far more effective.
- Passive Reconnaissance: Tools like
gau(Get All URLs) andwaybackurlscan pull historical URLs from various sources, providing a goldmine of potentially forgotten or deprecated endpoints. - JavaScript File Analysis: Client-side JavaScript often contains hardcoded API endpoints, internal routing, and parameter names. Extracting these programmatically is a powerful technique.
- From Broader Scans: Initial reconnaissance with tools like Zondex can reveal exposed services and technologies. This information can then be used to craft highly specific FFUF wordlists, for example, targeting known endpoints for a detected CMS or web framework.
Here's a Python snippet to extract potential endpoints from a JavaScript file:
import re
import sys
def extract_endpoints_from_js(js_content):
# Regex for common API path patterns, adjust as needed
patterns = [
r'[\'"](/api/[a-zA-Z0-9_/.-]+)[\'"]', # /api/v1/users, /api/auth
r'[\'"](/?assets/[a-zA-Z0-9_/.-]+\.[a-z]{2,4})[\'"]', # /assets/js/app.min.js
r'[\'"](/?admin/[a-zA-Z0-9_/.-]+)[\'"]', # /admin/dashboard
r'[\'"](/?config/[a-zA-Z0-9_/.-]+)[\'"]' # /config/settings
]
endpoints = set()
for pattern in patterns:
matches = re.findall(pattern, js_content)
for match in matches:
if not match.startswith('http'): # Exclude external URLs
endpoints.add(match)
return sorted(list(endpoints))
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Usage: python3 js_endpoint_extractor.py <js_file_path>")
sys.exit(1)
js_file_path = sys.argv
try:
with open(js_file_path, 'r', encoding='utf-8', errors='ignore') as f:
js_content = f.read()
discovered_endpoints = extract_endpoints_from_js(js_content)
for endpoint in discovered_endpoints:
print(endpoint)
except FileNotFoundError:
print(f"Error: File not found at {js_file_path}")
except Exception as e:
print(f"An error occurred: {e}")
Run this script by piping a JavaScript file into it or providing the path:
curl -s https://target.com/app.js | python3 js_endpoint_extractor.py /dev/stdin > js_endpoints.txt
ffuf -w js_endpoints.txt -u https://target.com/FUZZ -mc 200,301,302
Proxying FFUF Traffic: Visibility and Evasion
Routing FFUF traffic through a proxy serves multiple purposes: inspection, modification, and evasion. Using the -proxy flag, FFUF can be integrated with local proxies like Burp Suite or ZAP for granular request/response analysis. This is invaluable for understanding how the application processes your fuzzed inputs and for crafting more targeted payloads.
ffuf -w common.txt -u https://target.com/FUZZ -proxy http://127.0.0.1:8080 -v
For scenarios requiring IP rotation, bypassing rate limits, or routing through specific network egress points, leveraging advanced proxy services is beneficial. GProxy offers various proxy types, including rotating IPv6 proxies and dedicated IPv4 options, which can be integrated with FFUF to maintain stealth and overcome blocking mechanisms during extensive fuzzing campaigns.
ffuf -w api_paths.txt -u https://api.target.com/FUZZ -H "Authorization: Bearer valid_token" -x http://<GProxy_IP>:<PORT> -p 0.5-1.0 -rate 10
This setup routes traffic through a GProxy instance, adds a random delay between requests, and limits the rate to avoid detection and maintain session persistence.
Fuzzing APIs with OpenAPI/Swagger Specs
When API documentation in OpenAPI (Swagger) format is available, it provides a blueprint for the attack surface. Instead of blind fuzzing, you can parse these specifications to generate highly targeted FFUF commands, enumerating every documented endpoint, method, and parameter. This eliminates guesswork and ensures comprehensive coverage of the known API surface.
Here's a simplified Python script that parses an OpenAPI (YAML) specification and generates FFUF commands for path discovery. This can be extended to include parameter fuzzing based on schema definitions.
import yaml
import sys
def generate_ffuf_commands_from_openapi(openapi_spec_path, target_domain, wordlist_path):
try:
with open(openapi_spec_path, 'r') as f:
spec = yaml.safe_load(f)
except FileNotFoundError:
print(f"Error: OpenAPI spec file not found at {openapi_spec_path}")
return
except yaml.YAMLError as e:
print(f"Error parsing YAML: {e}")
return
base_path = spec.get('servers', [{}]).get('url', '')
if base_path and not target_domain:
print(f"Warning: Using base path '{base_path}' from spec. Consider overriding with -d/--domain for external targets.")
target_url_prefix = base_path
elif target_domain:
target_url_prefix = f"https://{target_domain}" # Assuming HTTPS
else:
print("Error: No target domain provided and no server URL in OpenAPI spec.")
return
print(f"--- FFUF Commands for API Fuzzing ({target_url_prefix}) ---")
if 'paths' in spec:
for path, methods in spec['paths'].items():
full_path = f"{target_url_prefix}{path.replace('{', 'FUZZ_PARAM_').replace('}', '')}" # Simple placeholder for path params
for method in methods:
if method.upper() in ['GET', 'POST', 'PUT', 'DELETE', 'PATCH']: # Only generate for common HTTP methods
# Basic path fuzzing, assumes 'FUZZ' is for discovering sub-paths
# For parameter fuzzing, more logic is needed to insert FUZZ into query/body
ffuf_cmd = f"ffuf -w {wordlist_path} -u \"{full_path}/FUZZ\" -X {method.upper()} -mc 200,301,302,403,404"
print(ffuf_cmd)
# Example for fuzzing a specific known parameter if we parsed it
# if 'parameters' in methods[method]:
# for param in methods[method]['parameters']:
# if param.get('in') == 'query':
# print(f"ffuf -w {wordlist_path} -u \"{full_path}?{param['name']}=FUZZ\" -X {method.upper()} -mc 200")
if __name__ == "__main__":
if len(sys.argv) < 3:
print("Usage: python3 openapi_ffuf_generator.py <openapi_spec.yaml> <target_domain.com> [<wordlist_path>]")
print("Example: python3 openapi_ffuf_generator.py api-spec.yaml example.com /usr/share/seclists/Discovery/Web-Content/common.txt")
sys.exit(1)
spec_file = sys.argv
domain = sys.argv
wordlist = sys.argv if len(sys.argv) > 3 else "/usr/share/seclists/Discovery/Web-Content/common.txt" # Default wordlist
generate_ffuf_commands_from_openapi(spec_file, domain, wordlist)
To use this, save an OpenAPI specification (YAML) as api-spec.yaml, then run:
python3 openapi_ffuf_generator.py api-spec.yaml api.example.com /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-small.txt | bash
This approach transforms static documentation into active reconnaissance, systematically probing every corner of the defined API.