6

I have come across the following JS code in multiple web applications. I think the reason for the popularity of this code snippet is this accepted answer on SO. Developers seem to be using this code a lot of switching menu tabs:

var url = document.location.toString();
if (url.match('#')) {
    $('.nav-tabs a[href="#' + url.split('#')[1] + '"]').tab('show');
}

It basically reads from document.location and passes anything after a # to $() without any sanitation.

Even Burp Suite's Active Scanner reports it as a True Positive.

enter image description here

I've tried to exploit it, but it seems like a false positive to me. My hypothesis is that since JS is converting the URL as a String, any payload (including special chars) will be treated as a string.

Posting this here just to make sure if you guys can think of any other way to exploit it to cause a DOM based XSS?

Rahil Arora
  • 4,259
  • 2
  • 23
  • 41

2 Answers2

5

This code snippet is vulnerable to XSS in jQuery prior to 1.9 (and in conjunction with the jQuery Migrate plugin). Even now, it remains at least a bad practice.

The string argument to $() (shortcut for jQuery()) can be parsed either as a CSS selector or HTML code. Parsing the string as HTML implies an XSS vulnerability, just as document.write() would. On the other hand, injecting into a CSS selector is not directly exploitable in current browsers.

The changelog for v1.9 explains how they changed the parsing behavior:

Prior to 1.9, a string would be considered to be an HTML string if it had HTML tags anywhere within the string. This has the potential to cause inadvertent execution of code and reject valid selector strings. As of 1.9, a string is only considered to be HTML if it starts with a less-than ("<") character. The Migrate plugin can be used to restore the pre-1.9 behavior.

Since the string from your snippet ('.nav-tabs a[href="#' + <payload> + '"]') does not start with a <, you will not be able to inject your own code, when the page is running a current version of jQuery.

However, you should still sanitize the output. Sometimes, different modules (e.g. Wordpress plugins) come with their own jQuery library that might inadvertently override the newer version and reintroduce the legacy behavior. A simple fix would be to reject the string if location.hash is not alphanumeric or not contained in a list of predefined links.

Arminius
  • 43,922
  • 13
  • 140
  • 136
  • This is corrent, but the site may also be vulnerable is a new version of jQuery is used together with jQuery-migrate, as jQuery-migrate reintroduced the same bugs: – Erlend May 26 '16 at 10:04
  • For a demo see: http://research.insecurelabs.org/jquery/test/ – Erlend May 26 '16 at 10:05
  • @Erlend This fact is included in the quote from my answer. I'll try to emphasize it a little more. – Arminius May 26 '16 at 12:11
1

This was a pretty common XSS attack vector in jQuery, and can still affect sites using an outdated version of jQuery.

https://bugs.jquery.com/ticket/9521

Basically, if a string was an invalid selector, jQuery would assume it was HTML, and parse it as HTML. In jQuery version 1.9 jQuery mitigated this risk by only parsing a string as HTML if it started with <, so it should not be possible in modern jQuery.

Unfortunately many sites still use outdated versions of jQuery (including Stack Exchange), which enabled the semi-recent WordPress XSS attack due to a mistake in an HTML file for their font icon library.

Alexander O'Mara
  • 8,774
  • 6
  • 34
  • 38