Reflekt doesn't just find reflections — it understands where your input lands
and verifies if it's actually exploitable.
Features | Install | Usage | How It Works | Output | Workflow
Most XSS scanners spray payloads like <script>alert(1)</script> and grep the response. This triggers WAFs, generates noise, and misses context-specific vulnerabilities.
Reflekt takes a different approach:
Traditional Scanner Reflekt
Inject payload -----> Grep Inject canary -----> Find reflection
| | | |
v v v v
"Found XSS!" 90% false pos Detect context Build smart probe
| |
v v
Attribute? Script? Send & verify
HTML? DOM sink? in correct context
| |
v v
Craft minimal Confirmed. Zero noise.
context probe
|
|
git clone https://github.com/yourusername/reflekt.git
cd reflekt
pip install requestsThat's it. Pure Python 3.8+, single dependency.
python3 reflekt.py -u "https://target.com/search?q=test&lang=en"python3 reflekt.py -l urls.txt -t 50 -o results.txtUsage: reflekt.py [-u URL | -l FILE] [options]
Required (one of):
-u, --url Single URL to scan
-l, --list File containing URLs (one per line)
Options:
-t, --threads Number of concurrent threads [default: 1]
-o, --output Save results to file
--timeout Request timeout in seconds [default: 10]
# Quick single target
python3 reflekt.py -u "https://example.com/page?id=1&name=test"
# Large list with 50 threads
python3 reflekt.py -l parameterized_urls.txt -t 50 -o findings.txt
# Slow targets — longer timeout, fewer threads
python3 reflekt.py -l urls.txt -t 10 --timeout 20 -o results.txt
# Pipe results cleanly (progress on stderr, results on stdout)
python3 reflekt.py -l urls.txt -t 50 2>/dev/null > results.txt +---------------------+
| Target URL |
| ?id=1&name=foo |
+---------+-----------+
|
+---------v-----------+
| Inject Canaries |
| id=buggedout1AbC |
| name=buggedout2xYz |
+---------+-----------+
|
+---------v-----------+
| Fetch Response |
| (TLS session reuse) |
+---------+-----------+
|
+---------v-----------+
| Find Reflections |
| Where did canary |
| land in the HTML? |
+---------+-----------+
|
+-------------+-------------+
| | |
+----v----+ +----v----+ +----v----+
| HTML | | Attr | | Script |
| <p>.. | | value=" | | var x=' |
| Probe: | | Probe: | | Probe: |
| <x7 | | "x7 | | ';//x7 |
+----+----+ +----+----+ +----+----+
| | |
+-------------+-------------+
|
+---------v-----------+
| Send Probe & |
| Verify Context |
| |
| - Quote not encoded?|
| - Not escaped? |
| - Correct context? |
+---------+-----------+
|
+---------v-----------+
| Confirmed XSS |
+---------------------+
Each parameter gets a unique canary — a random string that won't appear naturally in the page:
Original: ?name=john&role=admin
Injected: ?name=buggedout1kQ9m&role=buggedout2pX7z
Reflekt analyzes the HTML structure around each reflected canary:
| Context | What It Looks Like | Probe Sent |
|---|---|---|
| HTML Body | <div>CANARY</div> |
canary<x7 |
Attribute " |
<input value="CANARY"> |
canary"x7 |
Attribute ' |
<input value='CANARY'> |
canary'x7 |
| Attribute unquoted | <input value=CANARY> |
canary>x7 |
Script ' |
var x = 'CANARY'; |
canary';//x7 |
Script " |
var x = "CANARY"; |
canary";//x7 |
Script ` |
var x = `CANARY`; |
canary`;//x7 |
| Script raw | var x = CANARY; |
canary;//x7 |
This is what makes Reflekt different. It doesn't just check "is my string in the response?" — it verifies exploitability:
Attribute Breakout:
Probe: buggedout1abc"x7
Check 1: Is " literal? (not " or " or ")
Check 2: Does it break the attribute context?
Result: CONFIRMED only if both pass
Script Breakout:
Probe: buggedout1abc';//x7
Check 1: Is ' inside a <script> block?
Check 2: Is ' NOT escaped as \' ?
Check 3: If fail -> try </script> tag breakout (fallback)
Result: CONFIRMED only if unescaped in script context
HTML Injection:
Probe: buggedout1abc<x7
Check 1: Does < appear in response?
Check 2: Is it OUTSIDE <script> blocks? (not just in JS string)
Result: CONFIRMED only if injectable in HTML body
| Context | Detection | Why It Matters |
|---|---|---|
| HTML Comment | <!-- CANARY --> |
Skipped — not exploitable |
| srcdoc | <iframe srcdoc="CANARY"> |
HTML entities get decoded by browser |
| DOM Sink | data-x="CANARY" + innerHTML=dataset.x |
JS decodes entities at runtime |
| Script tag breakout | Fallback when JS quote escape fails | </script><img onerror=...> |
URL | parameter | context
https://target.com/search?q=buggedout1abc%22x7 | q | attribute(")
https://target.com/page?name=buggedout1abc%3Cx7 | name | html
https://target.com/app?data=buggedout1abc%27%3B//x7 | data | script(')
https://target.com/view?msg=...%3C/script%3E%3Cx7 | msg | script(</tag>)
| Output | Meaning | Exploitability |
|---|---|---|
html |
HTML body — tag injection works | <img onerror=...> |
attribute(") |
Double-quoted attribute breakout | " onfocus=... autofocus=" |
attribute(') |
Single-quoted attribute breakout | ' onfocus=... autofocus=' |
attribute(unquoted) |
Unquoted attribute | onfocus=... |
script(') |
JS single-quoted string breakout | ';alert();// |
script(") |
JS double-quoted string breakout | ";alert();// |
script()` |
JS template literal breakout | `;alert();// |
script(</tag>) |
Script tag closure | </script><img onerror=...> |
srcdoc |
iframe srcdoc injection | Entity-decoded HTML |
dom_sink |
innerHTML via dataset | Entity-decoded DOM XSS |
# 1. Collect parameterized URLs
katana -u https://target.com -d 3 -f qurl | tee all_urls.txt
waybackurls target.com | grep "=" >> all_urls.txt
# 2. Deduplicate
sort -u all_urls.txt > unique_urls.txt
# 3. Scan with Reflekt
python3 reflekt.py -l unique_urls.txt -t 50 -o xss_results.txt
# 4. Review confirmed findings
cat xss_results.txt| Feature | Benefit |
|---|---|
| TLS Session Reuse | Thread-local connection pools — no redundant handshakes |
| Content-Type Filter | Skips JSON, XML, plain text (XSS not possible) |
| Smart Dedup | One probe per param+context combo, not per reflection |
| Minimal Probes | 1-3 char probes instead of full payloads — faster, stealthier |
Pull requests welcome! For major changes, please open an issue first.
MIT License — free to use in your security assessments.