DOM XSS Lab: Run Script Straight From the URL Bar

✨ AI Summary
QuickDocs reads the part of the URL after the # to highlight your search term on the page. The server never sees the fragment — but your browser does, and it writes that text straight into the page. That single unsafe write is a DOM-based XSS, and no server-side filter can catch it.
Lab at a glance: Intermediate · 80 XP · about 15 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

DOM-based XSS lives entirely in the browser. The vulnerable code takes attacker-controlled input from a client-side source (here, the URL fragment) and passes it to a dangerous sink (here, innerHTML) without escaping it.

The lesson: because the fragment after # never reaches the server, server-side filters and most WAFs are blind to this. The bug is in the JavaScript, and the fix has to be there too.

How QuickDocs highlights your search

The page reads location.hash and inserts it into the article to highlight your search term. It builds HTML from that value and assigns it with innerHTML, so any markup you put in the fragment becomes real DOM — including an element that runs script.

The attack approach

  • Find where your input appears on the page — the search term is pulled from the URL fragment.
  • Confirm the fragment is written as HTML, not as plain text, by seeing whether markup renders.
  • Edit the URL in the lab's address bar and put an element in the fragment that triggers JavaScript when it loads.
  • Aim for the element to call the lab's solve function.

Full walkthrough (spoiler)

If markup is rendering but nothing fires yet, open the steps below.

Show the step-by-step solution
  1. In the iframe's address bar, change the text after the # to some HTML and reload — notice it appears as real page content, not escaped text.
  2. Swap in an element that runs JavaScript automatically on load (an image element with a broken source and an error handler is the classic choice).
  3. Have that handler call the page's solve function, window.seLabSolve(...), with the flag the lab expects.
  4. Load the URL. The fragment is written into the DOM, the handler fires, and the lab marks you solved.

Why it works

The page trusts the URL fragment and pipes it into innerHTML, which parses whatever you give it as HTML. Inserted elements with event handlers run immediately. Because the fragment is client-side only, nothing on the server ever had a chance to sanitise it — the entire flaw is in one unsafe DOM write.

Core principleNever pass untrusted input to innerHTML. Write user text with textContent, or sanitise it, and the sink closes.

Where this happens in the real world

DOM XSS is widespread because modern apps do so much in the browser. Fragment-, location– and postMessage-based sinks are common findings in bug-bounty programmes, and they slip past server-side defences precisely because the data never leaves the client. XSS as a class sits under Injection (A03) in the OWASP Top 10 (2021).

How to defend against it

  • Use textContent, not innerHTML, whenever you display user-controlled text.
  • If you must build HTML, sanitise with a trusted library (such as DOMPurify) before inserting it.
  • Treat client-side sources as untrusted — the URL, the fragment, postMessage data and storage can all be attacker-controlled.
  • Set a Content-Security-Policy as a backstop so injected inline script is less likely to run.
Mr Elite tip: When testing your own app, grep the front-end for innerHTML, document.write and insertAdjacentHTML, then trace back to see whether any of them ever receive the URL, the fragment or message data.

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 DOM-based XSS?
A cross-site scripting bug where client-side JavaScript takes attacker-controlled input and writes it into the page unsafely, so the payload never has to touch the server.
Why can't a WAF catch it?
The vulnerable data often lives in the URL fragment (after #), which browsers never send to the server. Server-side filters and WAFs simply never see it.
Is DOM XSS less serious than other XSS?
No. It runs in the victim's browser with their session just like reflected or stored XSS, and can steal tokens, perform actions or rewrite the page.
How do I fix a DOM XSS sink?
Write user text with textContent instead of innerHTML, or sanitise with a library like DOMPurify before inserting, 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 *