DOM XSS in innerHTML sink using source location.search
DOM XSS: untrusted location.search is written to innerHTML, enabling script execution. I tested <script>alert("hello")</script> and <img src=x onerror=alert(1)>. innerHTML parses/executes HTML; img onerror fires if load fails.
🎯 Objective
Find and confirm a DOM-based XSS where data from location.search (the URL query string) is written into the page with innerHTML. Exploit by causing alert() to run from a crafted URL.
🧭 Scope / Setup
- Tooling: Browser address bar (paste URL with payload), optional proxy to capture/refine.
- Target: Blog search functionality that reads
location.searchand assigns it to an element viainnerHTML. - Precondition: Authorized lab environment.
🔎 Approach
- Add a payload in the URL query string (e.g.
?q=<payload>) and load the page. - If the app takes
location.searchand doessomeDiv.innerHTML = decodedSearch, the payload is injected into the DOM and will execute immediately in the browser context. - Confirm by observing
alert()or checking the rendered DOM (View Source / DevTools).
🧪 Test Payloads (used)
Script tag (direct):
<script>alert("hello")</script>Attribute/event payload (commonly effective in many DOM sinks):
<img src=x onerror=alert(1)>
I used the script payload above in the query string to confirm execution.
🔬 Why innerHTML is dangerous (short)
innerHTML replaces or sets the HTML inside an element as raw HTML. If attacker-controlled input reaches innerHTML without encoding/sanitization, any embedded HTML/JS will be parsed and executed by the browser — e.g. <script>…</script> runs immediately.
❓ What happens with <img src=x onerror=...> (explanation)
- When the browser parses
<img src="x" onerror="alert(1)">it tries to load resourcex. - The load fails (invalid URL), which triggers the
onerrorevent on thatimgelement. - The
onerrorattribute contains JavaScript and is executed by the browser when the error event fires — soalert(1)runs. - Why
onerroris useful:- It works even if
<script>tags are removed/filtered by simple sanitizers. - It's short and often bypasses naive filters that look specifically for
<script>.
- It works even if
- Caveats:
- Some sanitizers remove inline event attributes (
on*) or block them with strict CSP. - CSP
script-srcor attribute restrictions can prevent execution; this lab has no CSP.
- Some sanitizers remove inline event attributes (
✅ Outcome
- If the page assigns untrusted
location.searchintoinnerHTMLand you seealert()run (via<script>or<img onerror=...>), the DOM XSS is confirmed. - If the payload is shown escaped or appears as text, the app is encoding/sanitizing (or there is encoding/decoding/mozibake issues) and you’ll need to examine the exact transformation.
🛡️ Mitigations (concise)
- Avoid
innerHTMLwith untrusted data. UsetextContentor DOM APIs (createElement,appendChild) to insert text safely. - If HTML must be allowed, use a well-tested HTML sanitizer/whitelist library that strips scripts and event attributes.
- Apply CSP as defense-in-depth (but don’t rely on it alone).
- Normalize and validate URL input before use; treat
location.*as untrusted.
📝 Notes
- DOM sinks differ:
innerHTMLexecutes HTML/JS, whiletextContentorinnerTextdo not. - Test payloads in the address bar (e.g.
https://target/?q=%3Cimg%20src=x%20onerror=alert(1)%3E) and check the DOM with DevTools. - Always test in authorized labs only.
