September 2, 2026
DOM XSS in document.write Sink Using location.search - PortSwigger Lab Write-Up
Introduction

By Om Barot
3 min read
Introduction
Cross-Site Scripting (XSS) occurs when an application includes attacker-controlled input in a web page without properly handling it. DOM-based XSS is a client-side vulnerability where JavaScript processes untrusted data and places it into the page in an unsafe way.
In this PortSwigger Web Security Academy lab, the vulnerable application uses the URL's location.search value and passes it into a document.write sink. The objective is to identify this data flow and execute JavaScript in the browser.
Lab Overview
Lab: DOM XSS in document.write sink using source location.search
Vulnerability: DOM-based Cross-Site Scripting (DOM XSS)
Source: location.search
Sink: document.write
Platform: PortSwigger Web Security Academy
Understanding the Vulnerability
The important part of this lab is understanding the relationship between a source and a sink.
Source: location.search
location.search contains the query string from the current URL.
For example:
https://example.com/?search=Hackhttps://example.com/?search=HackThe JavaScript application can access:
location.searchlocation.searchwhich contains the query portion of the URL.
Because the value comes from the URL, it can potentially be controlled by an attacker.
Sink: document.write
document.write() writes content directly into the HTML document.
If attacker-controlled data reaches document.write() without appropriate handling, HTML or JavaScript may be interpreted by the browser.
The basic dangerous data flow is:
Attacker-controlled URL
โ
location.search
โ
document.write()
โ
Browser interprets injected HTML/JavaScriptAttacker-controlled URL
โ
location.search
โ
document.write()
โ
Browser interprets injected HTML/JavaScriptStep 1 - Inspect the Application
The lab initially displays a blog search page.
I entered a normal search term:
HackHackThe application returned:
1 search results for 'Hack'1 search results for 'Hack'This indicated that the search parameter was being reflected into the page.
Step 2 - Test Whether the Search Parameter Is Reflected
The URL contained the search parameter:
?search=Hack?search=HackChanging the value changed the text displayed on the page.
This is an important observation because it shows that data from the URL is being used by the application.
However, reflection alone does not automatically mean that XSS exists. We need to determine how the application processes that value.
Step 3 - Inspect the Page with Developer Tools
I opened the browser's Developer Tools and inspected the page.
The search result heading contained the user-controlled value:
<h1>1 search results for 'Hack'</h1><h1>1 search results for 'Hack'</h1>The application was therefore placing the search parameter into the page.
Step 4 - Test for HTML/JavaScript Injection
To determine whether the input was being interpreted as HTML, I modified the search value with an SVG-based XSS payload:
"><svg onload=alert(1)>"><svg onload=alert(1)>The resulting request placed the payload into the search parameter.
The browser then executed the JavaScript contained in the SVG element and displayed an alert.
Step 5 - Confirm JavaScript Execution
The browser displayed an alert containing:
11This confirmed that the supplied input was not being treated purely as text.
Instead, the injected SVG element was interpreted as HTML, and its onload event executed JavaScript.
Why Did the XSS Work?
The vulnerability exists because attacker-controlled data flows from the URL into an unsafe DOM manipulation function.
The important components are:
1. Attacker-controlled source
location.searchlocation.searchThe attacker can influence the URL query string.
2. Unsafe sink
document.write()document.write()document.write() can cause supplied HTML to be parsed as part of the document.
3. Missing output handling
If the application does not properly encode or sanitize the input before sending it to the sink, specially crafted HTML can be interpreted by the browser.
The resulting flow is:
location.search
โ
User-controlled input
โ
document.write()
โ
HTML parsing
โ
JavaScript executionlocation.search
โ
User-controlled input
โ
document.write()
โ
HTML parsing
โ
JavaScript executionKey Lesson
The main lesson from this lab is that DOM XSS is not necessarily caused by server-side reflection.
The vulnerable behavior can happen entirely inside the browser.
When testing for DOM XSS, look for:
- Sources - places where attacker-controlled data enters JavaScript.
- Sinks - functions that process or insert that data into the DOM.
- Data flow - whether the source can reach the sink without safe handling.
Common sources include:
location.search
location.hash
location.href
document.URLlocation.search
location.hash
location.href
document.URLExamples of potentially dangerous sinks include:
document.write()
innerHTML
outerHTMLdocument.write()
innerHTML
outerHTMLThe exact risk depends on how the data reaches the sink and how the application handles it.
Conclusion
This PortSwigger lab demonstrated a DOM-based XSS vulnerability involving location.search and document.write.
By manipulating the search parameter and testing how the application processed the supplied value, I was able to demonstrate that HTML could be injected and JavaScript executed in the browser.
The key takeaway is simple:
Always trace untrusted input from its source to its final DOM sink.
Finding a reflected parameter is only the beginning. Understanding how the browser processes that input is what allows you to determine whether a real DOM XSS vulnerability exists.