---
title: "Find Reflected XSS on a Domain"
canonical: https://trickest.com/library/xss-scanner
description: "Collect a domain's archived URLs, keep parameters that reflect input, and fuzz each one for reflected and DOM cross-site scripting."
---

# Find Reflected XSS on a Domain

## Overview

Cross-site scripting survives because the parameter that reflects input is rarely
the one anybody documented. It is on a page shipped three years ago, reachable
from a link in an old email, still wired to the same template. Testing the
application you know about misses it every time.

This works from the application's history instead. You get the archived URL set
for a domain, narrowed to the parameters that historically carry reflected
values, then fuzzed and confirmed, so what lands in the report is a payload that
actually executed rather than a parameter that looks suspicious. Running it after
each release turns it into
[continuous security testing](/use-cases/continuous-security-testing), because
the regression you care about is a fix that quietly came undone.

The fuzzing step is distributed, which is what makes a large URL set finish in
reasonable time.

## Pipeline

1. Read the target domain.
2. [gau](/tools/gau) collects archived URLs from public sources.
3. [urldedupe](/tools/urldedupe) collapses URLs that differ only by parameter
   value.
4. [gf](/tools/gf) keeps the ones whose parameters match known XSS patterns.
5. [qsreplace](/tools/qsreplace) substitutes a probe value into each parameter.
6. The candidate list is capped so a large archive cannot run away.
7. [dalfox](/tools/dalfox) fuzzes each candidate across the fleet and verifies
   which payloads execute.
8. Confirmed findings are written to a PDF report.

## Inputs

- **Target domain.** One domain. You do not supply a URL list, because the whole
  candidate set is built from the domain's own archived history.
- **Parameter patterns.** [gf](/tools/gf) decides which parameters are worth testing
  using its XSS pattern set. Left alone, that default is what keeps the fuzzer off
  parameters that have never reflected anything.
- **Candidate cap.** The list handed to the fuzzer is capped before it runs, so a domain
  with a large archive cannot turn one run into an open-ended fuzz. Widen it only when
  you know the scope can take it.

## Outputs

- **A PDF report.** Confirmed findings only, each with the parameter, the payload, and
  the evidence that it executed. A parameter that reflects without executing does not
  appear, which is what keeps the file short enough to act on.
- **A filtered candidate list.** The archived URLs that survived deduplication and
  pattern matching, which is a reusable target set for other parameter testing.
- **The raw archived URL set.** Everything the archive collection step returned for the
  domain, before any filtering, so you can widen the pattern set and rerun without
  paying for collection twice.

## Sample output

From a completed run against `testphp.vulnweb.com`, an application published as a
deliberately vulnerable test target.

The archive returns far more than is worth testing, which is why the filter runs
before the fuzzer. [gf](/tools/gf) kept 66 URLs out of the archived set:

```jsonl
{"url": "http://testphp.vulnweb.com/listproducts.php?id=1", "param": "id", "stage": "archive"}
{"url": "http://testphp.vulnweb.com/listproducts.php?cat=1&page=1", "param": "cat,page", "stage": "archive"}
{"url": "http://testphp.vulnweb.com/search.php?order=order&query=query", "param": "order,query", "stage": "archive"}
{"url": "http://testphp.vulnweb.com/search?q=aaa", "param": "q", "stage": "archive"}
{"url": "http://testphp.vulnweb.com/bxss/vuln.php?id=FUZZ", "param": "id", "stage": "fuzz-target"}
{"url": "http://testphp.vulnweb.com/hpp/params.php?gt=FUZZ&p=FUZZ", "param": "gt,p", "stage": "fuzz-target"}
{"url": "http://testphp.vulnweb.com/listproducts.php?cat=1FUZZ&page=1FUZZ", "param": "cat,page", "stage": "fuzz-target"}
```

Those collapse into 55 fuzz targets once [qsreplace](/tools/qsreplace) swaps each
value for the marker:

Worth noticing in that list: several archived URLs carry SQL and script payloads
in their query strings, because the archive recorded somebody else's scan traffic
against this host years ago. The filter treats them as parameters like any other.

[dalfox](/tools/dalfox) reported nothing on this run. It names the parameter, the
payload and the evidence of execution when it does fire, and a parameter that
reflects but does not execute is not reported, which keeps the PDF short enough to
act on.

## FAQ

### Does this find stored cross-site scripting?

No. It tests parameters for reflected and DOM cross-site scripting, which is what an
archive-and-fuzz approach can reach. Stored requires a value that is saved and then
rendered on a later page load, and nothing here does that.

### Why archived URLs rather than a crawl?

A crawler sees what is linked today. The archive holds the page that shipped three years
ago, is still wired to the same template, and is still reachable from a link in an old
email. That is usually where the reflecting parameter is.

### How is this different from running dalfox myself?

[dalfox](/tools/dalfox) needs a candidate list, and building one is the first six steps.
This collects the archive, collapses URLs that differ only by parameter value, keeps the
ones matching known XSS patterns, then fans the fuzzing across the fleet instead of
walking the list one URL at a time.

### Is it safe to run against a third party?

Collection reads public archives and sends nothing to the target. Fuzzing does send
payloads to the live application, so run the second half only inside a scope that allows
active testing, such as a bounty program that lists the domain.

### What does a run cost?

The size of the archive sets the ceiling, but not the bill. Deduplication and pattern
filtering cut the list before the expensive step and the cap bounds it, so runtime tracks
the number of candidate parameters rather than the number of URLs the archive returned.

## Related workflows

- [Find Open Redirects on a Domain](/library/open-redirect-scanner). Reach for this instead when
  the parameter you found controls a destination rather than page content.
- [Find SSRF in HTTP Parameters](/library/ssrf-scanner). Reach for this instead when the parameter is a
  URL the server fetches and the payload never reaches a browser at all.
- [Find Secrets in Wayback Responses](/library/wayback-secrets-finder). Reach for this instead when
  the same archived URL corpus is worth grepping for leaked keys before you spend a run
  fuzzing it.

---
_Markdown source of https://trickest.com/library/xss-scanner._
