WSA: Apprentice DOM Based XSS
Table of Contents
Introduction
If you have played around CTFs it is highly likely1 that you have encountered one of the common forms of cross-site scripting (XSS):
- Reflected
- Stored
And for good reason, since they are (in these scenarios) easy to spot and can usually be bundled with other techniques for more damage.
One other technique, the overlooked one as stated here, is DOM Based XSS, which occurs without any extra client-server interaction. In fact, DOM Based XSS takes advantage of the code that is included in the web page, and relies on user input to produce a certain outcome. The code does not (need to?) contact the web server, but manipulates the page’s contents based on a query.
In the first example, taken from Web Security Academy by PortSwigger, that query is a search field, but it could be a parameter, or … a hashchange as shown in the last lab. Feel free to make any corrections/suggestions in the comment section.
In document.write
lab
function trackSearch(query) { document.write('<img src="/resources/images/tracker.gif?searchTerms='+query+'">'); } var query = (new URLSearchParams(window.location.search)).get('search'); if(query) { trackSearch(query); }
" onload="alert() <img src="/resources/images/tracker.gif?searchTerms=" onload="alert()">
In inner.html
lab
After navigating the site just a little bit we see that in the search page there is this embedded script snippet:
function doSearchQuery(query) { document.getElementById('searchMessage').innerHTML = query; } var query = (new URLSearchParams(window.location.search)).get('search'); if(query) { doSearchQuery(query); }
We see that it takes the parameter search
, and adds its contents
(should they exist) in the innerHTML
(namely contents) of an element
with id searchMessage
. Seeing the response more carefully:
<h1><span>0 search results for '</span><span id="searchMessage"></span><span>'</span></h1>
Testing this payload should be more than enough
<script>alert()</script>
While it gets inserted into the page it does not get executed – another payload should be used. And truly, this second one resulted in a solved lab.
<img src="fail" onerror="alert(1)">
In jQuery anchor href lab
$(function() { $('#backLink').attr("href", (new URLSearchParams(window.location.search)).get('returnPath')); });
This occupies an <a>
element’s href
, found by an id="backLink"
.
The thing is that the function being used attr()
seems to do a better
sanitization than what we have already seen, escaping “, and ‘, not
allowing with a plain payload to work
#does not work testaki'> <script>alert()</script> #produced result <a id="backLink" href="/testaki'> <script>alert()</script>">Back</a>
Just a little bit of searching (honestly, it was the first
stackoverflow post for me), reminded me of a type of link I had
forgotten: javascript:
javascript:alert(1); #produced result <a id="backLink" href="javascript:alert(1);">Back</a>
This, when clicking the button causes the alert()
popup window and
successfully marks the lab as solved.
In jQuery selector sink using a hashchange event lab
First time on a room with a Go to exploit server
button.
In the website’s home page there is this interesting js snippet:
$(window).on('hashchange', function(){ var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')'); if (post) post.get(0).scrollIntoView(); });
Not knowing JavaScript, but barely understanding through transferring whatever I could from other languages, this snippet had some work for me:
- What is a
hashchange
event:- If you have ever seen anchors in urls (the snippets starting with
#
- feel free to click on a section title in this post and check the url) you know the entry point - A
hashchange
event occurs when that parameter changes
- If you have ever seen anchors in urls (the snippets starting with
- What is
$()
in general? It is a selector (source)- Searching in StackOverflow, we see that we can add a , to use
multiple selectors (even though I am not sure how this could help
for the time being, since it will only get fed into a
scrollIntoView()
)
- Searching in StackOverflow, we see that we can add a , to use
multiple selectors (even though I am not sure how this could help
for the time being, since it will only get fed into a
- What exactly does
slice(1)
do? It returns the string from index 1 to the end, effectively removing#
.
Getting back to the lab: using links of the following form allows to find the post with that word, and move the screen to that.
https://0aa700fb03d82670856aa9f50084000f.web-security-academy.net/#Reverse https://0aa700fb03d82670856aa9f50084000f.web-security-academy.net/#Watching https://0aa700fb03d82670856aa9f50084000f.web-security-academy.net/#Speak
Now that we know what it does, how can we escape from it? My naive payload
does not work, since decodeURIComponent
obviously returns a string and
does not just make a substitution.
# what I inserted /#')');alert(1); // # what I hoped for var post = $('section.blog-list h2:contains(' + ')'); alert(1)// + ')');
Searching for hashchange XSS
seems to only include results related to
this lab … :(
Maybe take advantage of the available web server, so that it calls that one instead?
After tinkering for some time, I viewed the solution. It seems silly
to me, because I can not understand how this would not work on any
website. I mean, as far as I can see it does not take advantage of
hashchange
at all, just inserts faulty code at the end of the web
page.
Summary
I think I’m missing something so, that’s enough labs for today, time for some research and theory.
Footnotes:
όπως γνωστός κοσμήτορας ανέφερε τις προηγούμενες ημέρες…