Reflected XSS Lab: Make a Search Box Run Your JavaScript

✨ AI Summary
BookShop's search page repeats whatever you searched for: "Results for …". It is a friendly touch, and it is also an open door. Because the page writes your words straight into its HTML, you can search for HTML instead of a book title, and the browser will run it. Cross-site scripting is the most reported web bug in bug bounty programmes, and this lab is the gentlest way in.
Lab at a glance: Beginner · 50 XP · about 10 minutes · runs in your browser, no signup or setup. Try it yourself first; the full solution is behind a spoiler below.

What this lab teaches

Cross-site scripting (XSS) happens when a website puts your input into a page as markup instead of as text. Once your input is markup, it can contain elements and event handlers, and those run JavaScript in the page with the site’s full trust.

“Reflected” means the payload is not stored anywhere. It travels in the request, usually the URL, and comes straight back in the response. An attacker sends a victim a crafted link; the victim’s browser runs the attacker’s script on the real site.

How BookShop's search works

When you search, the page builds a heading like “Results for your query” and inserts it with innerHTML. Nothing is escaped, so angle brackets you type become real HTML tags. The lab is solved when your injected code calls the lab’s solve function, which proves arbitrary JavaScript ran.

The attack approach

  • Search for a normal word first and find where it appears in the page. That spot is your injection point.
  • Check whether your angle brackets come back as real HTML. Search for a word wrapped in bold tags and see if it renders bold.
  • A plain script tag will not work here. Browsers deliberately do not run script elements added through innerHTML.
  • Event-handler attributes are different. An element that fails to load, for example, fires its error handler, and that handler is JavaScript you control.

Full walkthrough (spoiler)

Stuck after a few attempts? Open the steps below. Solving it yourself first is worth more XP, and more learning.

Show the step-by-step solution
  1. Search for something harmless like book and confirm your query appears in the results heading.
  2. Search for an image tag whose source is deliberately broken, so it can never load.
  3. Give that image an error handler that calls the lab’s solve function, using the function name and flag shown in the lab objective.
  4. Submit the search. The broken image fails to load, the error handler fires, your JavaScript runs, and the lab marks you solved.

Why it works

The browser has no way to know which parts of the page the developer wrote and which parts came from your search box. Both arrive as one block of HTML. Your image tag is as real to the browser as the site’s own logo, so its error handler gets the same permissions as the site’s own code: it can read the page, act as the logged-in user and send data anywhere.

Core principlePut user input into the page as text, never as HTML. Escape it, or use textContent instead of innerHTML.

Where this happens in the real world

XSS (CWE-79) topped MITRE’s 2024 CWE Top 25 list of the most dangerous software weaknesses, and it is consistently among the most reported bug classes on bug bounty platforms. Reflected XSS is typically used to steal session data, perform actions as the victim, or show a fake login form on the real domain, which makes phishing far more convincing.

How to defend against it

  • Escape output for its context. HTML-encode <, >, quotes and ampersands before putting input into HTML.
  • Use safe DOM APIs. textContent and setAttribute instead of innerHTML.
  • Let your framework do it. React, Vue and Angular escape by default; avoid their raw-HTML escape hatches.
  • Add a Content Security Policy that blocks inline scripts and event handlers, as a second layer.
  • Do not rely on blocklists that strip the word “script”. As this lab shows, there are dozens of other ways in.
Mr Elite tip: When you hunt for XSS on real targets (with permission), test where your input lands before you try payloads. Inside a tag, inside an attribute and inside JavaScript each need a different approach. The attribute-context lab is the next step.

Try it, then go further

Reading about it is the easy part. Open the lab, try your own approach before peeking at the spoiler, and see how many attempts it takes.

❓ Frequently asked questions

What is reflected XSS?
A cross-site scripting bug where input from the request, such as a search term in the URL, is written back into the page as HTML without escaping, so an attacker-crafted link can run JavaScript in the victim's browser.
Why does a script tag not work in this lab?
Browsers intentionally do not execute script elements inserted with innerHTML. Event-handler attributes on other elements still run, which is why attackers use them.
What is the difference between reflected and stored XSS?
Reflected XSS lives in a single request and needs the victim to open a crafted link. Stored XSS is saved by the site, for example in a comment, and runs for everyone who views it.
How do I fix XSS?
Escape user input for the context it is placed in, prefer textContent over innerHTML, use a framework that escapes by default, and add a Content Security Policy.
⚡
Join free to earn XP for reading this article Track your progress, build streaks and compete on the leaderboard.
Join Free
Lokesh N. Singh aka Mr Elite
Lokesh N. Singh aka Mr Elite
Founder, Securityelites · AI Red Team Educator
Founder of Securityelites and creator of the SE-ARTCP credential. Working penetration tester focused on AI red team, prompt injection research, and LLM security education.
About Lokesh ->

Leave a Comment

Your email address will not be published. Required fields are marked *