July 17, 2026
Finding Reflected XSS Parameters Fast: A Deep Dive into RefleX
How a multi-threaded Python scanner turns manual parameter testing into an automated, context-aware workflow

By whoami_404
4 min read
The Problem With Manual Reflection Testing
Anyone who has spent time hunting for Cross-Site Scripting (XSS) knows the drill: take a URL, throw a unique marker into every parameter, reload the page, and Ctrl+F for the marker in the response. Repeat that for every parameter, on every endpoint, on every target — and do it fast enough that it doesn't eat your entire afternoon.
It's a mechanical process, which makes it a perfect candidate for automation. But most quick-and-dirty reflection scripts stop at "yes, it's reflected" — they don't tell you where it landed in the DOM, whether the app sanitized your special characters, or what payload actually has a shot at firing. That gap is what RefleX is built to close.
RefleX is a concurrent, context-aware reflected-parameter and XSS discovery tool written in Python. It doesn't just flag reflections — it analyzes the HTML context they land in, probes how the target handles dangerous characters, scores severity, and hands you a tailored proof-of-concept payload to try next.
What RefleX Actually Does
At its core, RefleX automates a five-step loop for every parameter it finds:
- Inject a random, unique marker (
REFLTESTXXXX) into the parameter, one at a time, while leaving the rest of the request intact. - Send the request — GET or POST, form-encoded or JSON — and capture the response body.
- Detect whether the marker comes back in the response, case-insensitively (so normalization to upper/lowercase doesn't hide a hit).
- Diagnose the context: is the reflection sitting inside plain HTML text, inside a tag attribute, inside a
<script>block, or inside an HTML comment? And does the app strip, HTML-encode, or pass through raw <, >, ", ', and /? - Suggest proof-of-concept payloads that fit the exact context and character filtering it just observed — rather than a generic
<script>alert(1)</script>that may not even apply.
That last step is the difference between a scanner that generates noise and one that generates a next action.
Feature Breakdown
- Concurrency — Built on
ThreadPoolExecutor, so scanning a file of hundreds of URLs doesn't mean waiting on them one at a time. - Case-insensitive matching — Catches reflections even when an app normalizes casing before echoing input back.
- Unlimited reflection tracking — No arbitrary cap on how many times a parameter reflects across the response.
- HTML context + severity diagnosis — Classifies each reflection as HIGH, MEDIUM, LOW, or SECURE based on where it lands and how it's filtered.
- Automated PoC generation — Payloads are chosen to match the observed context and the characters that actually survive sanitization.
- POST support — Handles both form-encoded and JSON request bodies, not just query strings.
- Authenticated scanning — Custom cookies and headers, so authenticated surfaces aren't off-limits.
- Structurally accurate URL rebuilding — Uses tuple-lists during reconstruction, preserving parameter order and correctly handling duplicate keys — a detail that breaks naive
dict-based rebuilders. - Built-in retries and SSL warning suppression — Small things that matter when you're pointing this at real infrastructure over an unattended run.
- Structured output — A clean, one-URL-per-line file of confirmed reflections, deliberately formatted to pipe straight into tools like
dalfoxorXSStrikefor the exploitation stage.
How the Engine Is Wired Together
The scan flow starts with input — either a single URL or a file of URLs, which gets deduplicated before hitting the ThreadPoolExecutor concurrency manager. From there, each URL's query parameters are parsed into a list of tuples rather than a dictionary, which is what makes the duplicate-key and ordering guarantees possible downstream.
For every parameter, RefleX generates the unique marker, rebuilds the request (GET query string or POST body, depending on the flag), and fires it through a requests session carrying whatever headers and cookies were supplied. The response is decoded and checked for the marker.
If the marker comes back, two things happen in parallel, conceptually: a character-analysis probe sends <, >, ", ', and / individually to see whether each one survives raw, gets HTML-encoded, or gets stripped outright, and a context parser walks the response body to classify exactly where the reflection sits — text node, attribute, script block, or comment. Both results feed into the severity scoring and payload suggestion step, and everything gets printed with color-coded findings. If -o was set, confirmed reflected URLs are also written to disk.
Installation
git clone https://github.com/youwannahackme/RefleX
cd RefleX
pip install -r requirements.txtgit clone https://github.com/youwannahackme/RefleX
cd RefleX
pip install -r requirements.txtUsing It
Test a single URL:
python reflection.py -u "http://example.com/page.php?name=test&id=123"python reflection.py -u "http://example.com/page.php?name=test&id=123"Scan a POST endpoint with a JSON body:
python reflection.py -u "http://example.com/api/user?id=1" --method POST --post-type jsonpython reflection.py -u "http://example.com/api/user?id=1" --method POST --post-type jsonScan an authenticated endpoint with headers and cookies:
python reflection.py -u "http://example.com/profile?tab=settings" --headers "Authorization:Bearer token" --cookies "session=123"python reflection.py -u "http://example.com/profile?tab=settings" --headers "Authorization:Bearer token" --cookies "session=123"Batch-test a file of URLs, deduplicated automatically, across 10 threads:
python reflection.py -f urls.txt --threads 10python reflection.py -f urls.txt --threads 10Full option reference:
-h, --help Show this help message and exit
-u, --url URL Single URL to test
-f, --file FILE File containing URLs (one per line)
-o, --output OUTPUT Output file for reflected URLs (one per line)
--timeout TIMEOUT Request timeout in seconds (default: 10)
--threads THREADS Number of concurrent threads (default: 5)
--no-ssl-verify Disable SSL certificate verification
--method {GET,POST} HTTP request method (default: GET)
--post-type {form,json} POST body encoding (default: form)
--headers HEADERS Custom headers, e.g. "Authorization:Bearer token;X-Custom:val"
--cookies COOKIES Custom cookies, e.g. "session=123;admin=true"-h, --help Show this help message and exit
-u, --url URL Single URL to test
-f, --file FILE File containing URLs (one per line)
-o, --output OUTPUT Output file for reflected URLs (one per line)
--timeout TIMEOUT Request timeout in seconds (default: 10)
--threads THREADS Number of concurrent threads (default: 5)
--no-ssl-verify Disable SSL certificate verification
--method {GET,POST} HTTP request method (default: GET)
--post-type {form,json} POST body encoding (default: form)
--headers HEADERS Custom headers, e.g. "Authorization:Bearer token;X-Custom:val"
--cookies COOKIES Custom cookies, e.g. "session=123;admin=true"Reading the Output
A typical hit looks like this:
[*] Testing URL: http://example.com/search?q=test&id=123
Found 2 parameters to test
[+] REFLECTED: q (Severity: HIGH)
Context: Reflected as plain HTML text (inside <div> tag)
Snippet: ...<div>REFLTESTXYZ123</div>...
Character Analysis:
• '<' -> RAW_REFLECTION
• '>' -> RAW_REFLECTION
• '"' -> HTML_ENCODED (")
• "'" -> HTML_ENCODED (')
• '/' -> RAW_REFLECTION
Suggested PoCs:
• <script>confirm(1)</script>
• <img src=x onerror=confirm(1)>
• <svg onload=confirm(1)>
[-] Not reflected: id[*] Testing URL: http://example.com/search?q=test&id=123
Found 2 parameters to test
[+] REFLECTED: q (Severity: HIGH)
Context: Reflected as plain HTML text (inside <div> tag)
Snippet: ...<div>REFLTESTXYZ123</div>...
Character Analysis:
• '<' -> RAW_REFLECTION
• '>' -> RAW_REFLECTION
• '"' -> HTML_ENCODED (")
• "'" -> HTML_ENCODED (')
• '/' -> RAW_REFLECTION
Suggested PoCs:
• <script>confirm(1)</script>
• <img src=x onerror=confirm(1)>
• <svg onload=confirm(1)>
[-] Not reflected: idThis single block answers three questions at once: is it reflected, what's the blast radius given the context, and what should I try next. The q parameter reflects as raw HTML text with < and > passing straight through — that's why the severity lands at HIGH and the suggested payloads lean on unescaped tags. If < and > had come back HTML-encoded instead, the tool would have scored it lower and suggested attribute- or context-breakout payloads instead of a raw <script> tag.
When scanning a file, the -o flag writes just the confirmed reflected URLs — no severity labels, no snippets, just clean input for the next stage of a pipeline.
Where It Fits in a Workflow
RefleX is deliberately scoped: it finds and characterizes reflections, it doesn't try to be a full exploitation framework. That's what makes its output format useful — a flat list of confirmed-reflected URLs is exactly what tools like dalfox or XSStrike expect as input, so RefleX slots in as the triage stage: point it at a large URL list from crawling or gau/waybackurls, let it filter down to parameters that actually reflect with useful context, and hand that shortlist to a dedicated exploitation tool.
For bug bounty work specifically, that triage step is where the time actually goes. Most parameters don't reflect at all, and most that do reflect are encoded into uselessness. Automating the character-analysis and context-classification steps means the only things reaching a human are the ones actually worth a manual look.
RefleX is open source under the MIT license: github.com/youwannahackme/RefleX