The JavaScript Shenanigans
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 capacity1. 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.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.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 academyNow, 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)additionaly we have a search bar functionality aswell.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.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.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
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: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
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.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:
'
"
\
<
>
&
\\looking at the response
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 (`\`):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.{"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
🚀 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






