// LEARNER HUB — ARTICLE

Understanding XSS: Reflected, Stored, and DOM-Based

By Johnthebandit // Genesis Vault

Cross-Site Scripting shows up on almost every "top vulnerability" list ever published, and it's still one of the first things a new hunter learns — partly because it's visual and satisfying (you get a popup, an alert box, proof it worked), and partly because it teaches a lesson that applies to nearly every other web vulnerability class: never trust anything the browser renders that a user could have influenced.

The Three Flavors

Reflected XSS happens when user input from a request — a search query, a URL parameter — gets echoed back into the page's HTML without being sanitized. The attack only fires when a victim clicks a crafted link, since the payload lives in the request itself rather than being stored anywhere.

Stored XSS is the more dangerous sibling. The payload gets saved somewhere persistent — a comment, a username, a support ticket — and executes for every single person who later views that content. No crafted link required; the victim just has to load a normal page.

DOM-based XSS is subtler still. The vulnerable code never touches the server at all — client-side JavaScript reads something attacker-controlled (a URL fragment, document.referrer, local storage) and writes it into the page using an unsafe sink like innerHTML. You can have a perfectly sanitized backend and still be fully exploitable purely in the browser.

Why "Just Escape the Input" Isn't Enough

Most developers know to escape user input somewhere. The problem is context. Data that's safely escaped for placement inside an HTML text node can still be dangerous inside an attribute, inside a <script> block, or inside a URL. Each context has different characters that matter. A filter that strips <script> tags does nothing against a payload delivered through an onerror attribute on an image tag, or through a javascript: URI in an href.

What a Real Finding Looks Like

A convincing XSS proof-of-concept in bug bounty isn't just an alert box — that gets you told "low impact, please demonstrate real risk." A strong report shows what an attacker could actually do with it: exfiltrating a session cookie or auth token to an external listener, performing an action as the victim (changing their email, transferring funds, posting on their behalf), or chaining the XSS with a CSRF-protected endpoint to bypass that protection entirely, since script running in the victim's own browser already has whatever tokens the page has.

Where People Get Stuck

The most common mistake newer hunters make is stopping the moment a basic payload gets filtered. Modern frameworks and WAFs block the obvious <script>alert(1)</script> on sight. Real testing means understanding the templating engine or framework being used, checking whether it auto-escapes by default (React and most modern frameworks do, in most contexts), and looking specifically for the places developers had to opt out of that protection — dangerouslySetInnerHTML, v-html, raw template interpolation — because that's exactly where the safety net has a hole in it.

Practicing It Properly

Genesis Vault's Stored XSS lab is built around a realistic scenario — a blog comment field with a persistent payload — precisely because that's the pattern you'll see most in real applications: user-generated content, rendered for other users, with weak or missing output encoding.

// Want to practice this hands-on? Get 50 VC instantly on signup.
[ JOIN THE VAULT ]
← Back to all articles