document.referrer DOM XSS: When the Previous URL Becomes Code
document.referrer is just a string the browser fills from the previous navigation. Apps use it for analytics, "back to results" links, and CSRF-ish checks. Trouble starts when that string is treated as trusted HTML or script.
You do not put the payload on the vulnerable page's URL. You put it on the page before, then navigate across. That indirection is why automated scanners miss it: they load the target URL cold, see an empty referrer, and move on.
Stop treating the referrer as a friendly breadcrumb. It is attacker-influenced whenever the victim can be convinced to click your link first.
Spot the read
Search bundles for document.referrer, document.URL, and helper names like getReferrer. Common sinks:
- Writing a "came from" breadcrumb with
innerHTML - Building a redirect target:
location.href = document.referrer - Logging to a beacon that later reflects into an admin UI
- Parsing the referrer with regex and injecting a captured group into the DOM
Open the vulnerable page from a crafted previous page. If you open it in a fresh tab with no referrer, the bug stays quiet. That silence fools people during casual poking.
Craft the previous page
Host something like:
<a id="go" href="https://target.example/settings">continue</a>
<script>
// Some apps only trust same-site referrers; others accept anything.
document.getElementById("go").click();
</script>
Your payload lives in the previous URL path or query, for example https://attacker.example/<svg/onload=...>/. When the victim lands on the target, document.referrer may contain that hostile path. Meta referrer policies and Referrer-Policy headers can strip the query or path—test with no-referrer-when-downgrade, origin, and unsafe-url behavior in mind. A finding that only works when the victim's browser sends a full referrer still counts; document the policy dependency.
I've seen "sanitizers" that block <script in the referrer but allow javascript: when the sink is a location assign. Match the sanitizer to the sink, not to a generic XSS cheatsheet.
Open redirects paired with DOM XSS
If the app does location = document.referrer without an allowlist, you get an open redirect first. Chain it: bounce the victim through your domain so the next page's referrer becomes attacker-controlled, then hit the HTML sink. Two weak spots, one clean story.
Do not claim account takeover from a bare redirect. Show cookie theft, token exfil, or another concrete effect on the target origin.
Cross-site navigations from HTTPS marketing sites sometimes strip paths under strict referrer policies. If your first attempt arrives as origin-only, try a same-site stepping stone the target already trusts—docs, status, or a subdomain that still sends a fuller referrer into the app.
What to send triage
Record the full chain: attacker URL → automatic navigation → vulnerable page → canary. Include browser and Referrer-Policy observed on the response. If the bug needs a specific policy, say so; programs hate mystery steps.
Also note whether the victim must click or whether a meta refresh / location assign is enough. Automated clicks in your PoC are fine; just label them so reviewers can reproduce without guessing.
Fix guidance: never assign document.referrer to HTML sinks; for back-links, store an internal path id; for redirects, allowlist path prefixes on your own host. Treat the referrer as hostile input every time—because it is.