Sitemap
ysec

ysec

Cybersecurity research, vulnerability analysis, and attack techniques. Exploring how systems work, where they fail, and why. sharing what i learned!

The JavaScript Shenanigans

9 min readNov 1, 2026

--

reflected dom xss

Before diving into DOM-based XSS, let's break down the two terms that make up 
its name: the DOM (Document Object Model) and XSS (Cross-Site Scripting).
Understanding these concepts individually will make it easier to understand how DOM-based XSS works.

understanding the dom

components of the dom

document

So basically The  document represents the webpage's HTML 
that is loaded in our browser.
HTML (HyperText Markup Language) is used to define the structure of a
webpage using elements, tags, attributes, and other markup.

object

object, on the other hand, represents an actual thing.
For example, let's say our computer is an object.It has properties such as:
- Brand
- Operating system
- Storage capacity
1. Brand: "Dell"
2. Operating system: "Windows 11"
3. Storage: "512GB"
In JavaScript, an object is a value that represents something and can contain properties and methods.For example, the `document` is an object.

model

model in simple terms is the representation of something.The dom is a 
representation of the webpage.
It represents the webpage's HTML structure as objects arranged in a tree.
Press enter or click to view image in full size

understanding source

sources is where the untrusted user data enter the application.
For example, URL parameters such as ?search=, search bars,
and window.location.search.

understanding the sinks

Sinks are sensitive functions or DOM properties where untrusted data from a
source is used, rendered, or evaluated. Examples include eval(),innerHTML,
and document.write().

understanding xss

XSS arises when a web application takes user input and fails to validate or
handle it properly, causing the web browser to execute malicious JavaScript
on the client side. The impact of this is that a threat actor may be able
to steal sessions and, in addition to that phish users as well.
Press enter or click to view image in full size

reflected xss

Reflected DOM XSS happens when an application takes user input, reflects 
it in the server's response, and then processes it unsafely through client-side JavaScript.

Here's how it works:

1. An attacker sends a request containing malicious input.
2. The server includes that input in its response.
3. JavaScript in the browser picks up and processes the input.
4. If it's handled unsafely, it can lead to JavaScript execution in the victim's browser.

In simple terms, the server reflects the input, but the problem occurs when the browser-side JavaScript handles it unsafely.

different contexts in xss

Before getting into the labs, we must understand the different contexts our input 
can land in and how they may lead to XSS if user-controlled input isn't handled properly.

There are mainly three contexts you need to know about: HTML-based, attribute-based, and JavaScript-based.

1. HTML-based context: Input lands between HTML tags.
Example: <h1>USER_INPUT</h1>

2. Attribute-based context: Input lands inside an HTML attribute.
Example: <input value="USER_INPUT">

3. JavaScript-based context: Input lands inside JavaScript code.
Example: <script>var search = 'USER_INPUT';</script>

reflected-dom xss port swigger academy lab

lets understand how to hunt for dom xss vuln,we'll take an example of a lab in
port swigger academy
Press enter or click to view image in full size
Now, our goal in this lab is to execute the JavaScript function `alert()`,
which displays a popup in the browser and accepts an argument,
such as a string or a number.

understanding the functionality of the web app

so this web app is a blogging site,that has certain blogs and within those
blogs we have functionalities like comment,name,email,website(where we can give a url)
Press enter or click to view image in full size
additionaly we have a search bar functionality aswell.
Press enter or click to view image in full size

testing the search-bar functionality


Let's start by entering a benign input into the search bar and observing
how the application handles it. We want to understand where our input appears
in the page and identify which context it is being inserted into.
Press enter or click to view image in full size
As we can see below, our input is landing inside the HTML heading tags `<h1></h1>`.

My immediate thought process is: Can I inject my own custom HTML tags and
see how the application handles them? I also want to check whether there
are any security measures in place, such as HTML encoding.
For example, let's try using the underline tag `<u></u>` to see how the 
application handles it. If our text appears underlined in the search results,
we know that the application is allowing our custom HTML tags to be rendered.
Press enter or click to view image in full size
Press enter or click to view image in full size
So, we should always inspect the rendered HTML in the browser's DevTools,
as it gives us a clearer picture of how our input is actually being handled
and represented in the DOM.
As we can see, our input is being heavily mitigated through encoding,
preventing our custom HTML tags from being rendered.So, there isn't much we
can do with this particular injection point.

However, we also have a search function and a JavaScript
file called searchResults.js if we view page source.

Let's inspect the JavaScript and see how the application processes our input behind the scenes.

analysing the code

The source → flow → sink method

Press enter or click to view image in full size
function search(path) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
eval('var searchResultsObj = ' + this.responseText);
displaySearchResults(searchResultsObj);
}
};
xhr.open("GET", path + window.location.search);
xhr.send();

function displaySearchResults(searchResultsObj) {
var blogHeader = document.getElementsByClassName("blog-header")[0];
var blogList = document.getElementsByClassName("blog-list")[0];
var searchTerm = searchResultsObj.searchTerm
var searchResults = searchResultsObj.results

var h1 = document.createElement("h1");
h1.innerText = searchResults.length + " search results for '" + searchTerm + "'";
blogHeader.appendChild(h1);
var hr = document.createElement("hr");
blogHeader.appendChild(hr)

for (var i = 0; i < searchResults.length; ++i)
{
var searchResult = searchResults[i];
if (searchResult.id) {
var blogLink = document.createElement("a");
blogLink.setAttribute("href", "/post?postId=" + searchResult.id);

if (searchResult.headerImage) {
var headerImage = document.createElement("img");
headerImage.setAttribute("src", "/image/" + searchResult.headerImage);
blogLink.appendChild(headerImage);
}

blogList.appendChild(blogLink);
}

blogList.innerHTML += "<br/>";

if (searchResult.title) {
var title = document.createElement("h2");
title.innerText = searchResult.title;
blogList.appendChild(title);
}

if (searchResult.summary) {
var summary = document.createElement("p");
summary.innerText = searchResult.summary;
blogList.appendChild(summary);
}

if (searchResult.id) {
var viewPostButton = document.createElement("a");
viewPostButton.setAttribute("class", "button is-small");
viewPostButton.setAttribute("href", "/post?postId=" + searchResult.id);
viewPostButton.innerText = "View post";
}
}

var linkback = document.createElement("div");
linkback.setAttribute("class", "is-linkback");
var backToBlog = document.createElement("a");
backToBlog.setAttribute("href", "/");
backToBlog.innerText = "Back to Blog";
linkback.appendChild(backToBlog);
blogList.appendChild(linkback);
}
}
Instead of analysing the entire function at once, we'll trace the code 
line by line and only analyse whats relevant.

investigating the source

Our first potential candidate for source is: window.location.search in the line 

xhr.open("GET", path + window.location.search);

window.location.search: retrieves the query string from the current URL.

For example:

/blog?search=fishy_banana

Here, ?search=fishy_banana is appended to path to form the URL for the HTTP GET request.

But What does xhr.open() do?

It configures the HTTP request by specifying the method and URL.

investigating the sink

eval('var searchResultsObj = ' + this.responseText);
eval() is a built-in JavaScript function that takes a string and evaluates 
it as JavaScript code. The name eval stands for evaluate.
For example:
Press enter or click to view image in full size
this.responseText contains the response body returned by the server
after the HTTP request completes.

Now let's break down the following line:
eval('var searchResultsObj = ' + this.responseText);
1. this.responseText: Provides the data returned by the server.
2. 'var searchResultsObj = ': A string that declares a JavaScript variable named searchResultsObj.
3. Concatenates the declaration string with the server's response.
4. eval(): Evaluates the resulting string as JavaScript code.

The result is that the server's response is assigned to the
searchResultsObj variable.

But why does it matter?

Because the server's response is being passed directly into eval().
If that response contains attacker-controlled JavaScript, it could lead to XSS.

The full flow

Press enter or click to view image in full size

Solving the lab


Now that we have a fair understanding of how the code works and how the data flows through the application, let's start solving the lab.

We'll open Burp Suite, intercept the search request, and inspect the HTTP traffic to see how our input is handled by the server.

From there, we'll start experimenting with different inputs and observe how the server responds.
Press enter or click to view image in full size
As we can see in Burp Suite, the server responds with an HTTP 200 OK status and returns a JSON response.

The response contains two properties:

1. results: An empty array, meaning no search results were found.
2. searchTerm: Contains our search term, in our case fishy_bannana

The important observation here is that our input is being reflected in the server's JSON response.

This is interesting because the response is later accessed through this.responseText
and passed to `eval()`

Fuzzing the Search parameter using intruder


Now that we have identified our source and potential sink,
let's investigate how the application handles special characters.

We'll use Burp Suite Intruder to automate this process.

First, highlight the search parameter value and press Ctrl + I to send the request to Intruder.
Then, mark the parameter value as our payload position.

We'll use the following characters as our initial test cases:

'
"
\
<
>
&
\\
Press enter or click to view image in full size

looking at the response

Press enter or click to view image in full size
After fuzzing the search parameter, we can observe
something interesting in the server responses.

The double quote " is being escaped with a backslashh `\`, appearing as `\"`
in the JSON response.

This is an example of JSON escaping. The backslash
prevents the double quote from being interpreted as the end of the JSON string.

This tells us that the server is handling at least this special
character by escaping it before returning the response.

Understanding Character Escaping

Let's understand character escaping using our JavaScript console.

letss say we want to include double quotes inside a string.
console.log("Hello "users");
As we can see this causes a syntax error because JavaScript
interprets the second double quote as the end of the string.

fix

To fix this, we escape the inner double quotes using a backslash (`\`):
Press enter or click to view image in full size

Crafting our Payload

Now that we've understood how character escaping works, 
let's craft our payload and see how the application handles it.
We already know that the server escapes double quotes by 
adding a backslash (`\`) before them.

Now, let's test another possibility.

What happens if we include our own backslash (`\`) before the double quote?

Will the server escape both characters, and how will JavaScript interpret the resulting response?

Let's send the request through Burp Suite and inspect the response to find out.
Press enter or click to view image in full size
{"results":[],"searchTerm":"\\"fishy_bananna"}
 We use \" to break out of the string, add alert(document.window) as raw
executable code, then use } to close the object and // to comment out the server's
leftover syntax. payload ==> \"-alert(document.window)}//

why we didn’t considered other candidates

innerText: Although it receives the search term, it treats the value as text rather than interpreting it as HTML.

innerHTML: It might look interesting, but in this case it only receives
a hardcoded `<br/>` string. No user-controlled input reaches it.

Mitigations

Press enter or click to view image in full size

🚀 That’s a wrap!

Get ysec’s stories in your inbox

Join Medium for free to get updates from this writer.

If you made it this far, appreciate you taking the time to read!

More web security labs, weird vulnerabilities, and cybersecurity rabbit holes coming soon.

Follow along for more write-ups.

🔗 LinkedIn — www.linkedin.com/in/mohammad-yahya-aamir-5b1841387

--

--