February 4, 2024

JQuery: Always use dataType for $.ajax

I recently investigated a XSS vulnerability reported via Bugcrowd. I could reproduce the issue, I just didn’t understand it. The vulnerability looked like this:

  1. Attacker uploads a JavaScript file onto Confluence as attachment

  2. Manipulate a Confluence macro via REST API to have invalid parameters.

  3. Now, when the macro is edited/used, the code in the uploaded JavaScript is executed.

  4. The code snipped looked something like this:

// AJS is the Atlassian JavaScript library entry point. AJS.$ refers to the included JQuery library
AJS.$.ajax({
    url: 'url-manipulated-by-attacker-to-downloads-the-attachment-file',
    type: 'GET',
    success: function (response) {
        // some basic processing
    }
})

What I could not understand is why the JavaScript of the attachment executed? The code does not add anything to DOM elements or evals() any result. So, the next step was to add a debugger; statement to the uploaded JavaScript to see from where it is called. From the call stack I looked like the attachment code was called by the jQuery itself. I could npt really believe that. Maybe some Atlassian quirk? Or a vulnerable old JQuery library?

So, I decided to reproduce the issue outside of Confluence with the most basic page:

xss.js
debugger;
alert('XSS!');
index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Test Me</title>
<!--    <script src="jquery.js"></script>-->
    <script src="jquery-3.7.1.js"></script>
</head>
<body>
<h1></h1>

<script>
    $.ajax({
        url: '/xss.js',
        type: 'GET',
        success: function (response) {
            console.info("Data is", response)
        }
        async: true
    });
</script>
</body>
</html>

And when I visit the site, it promptly gets executed:

Run Duke
Figure 1. Stack Trace to the XSS

Confirmed! JQuery directly executed the fetched file within the ajax call. That is a huge surprise to me, because I never expect that the ajax call to execute any JavaScript by default.

However, the documentation does confirm this behavior. It states:

dataType (default: Intelligent Guess (xml, json, script, or html))
…​
"script": Evaluates the response as JavaScript and returns it as plain text.
…​

So, the default is to select the data type intelligently (my guess, mostly honoring the MIME type). I’m ok with that. But then the default behavior for the script type is to execute it.

NO!

NOOOOO!!!

NOOOOOOOOOO!!!!

That is a horrible default. No where does calling ajax suggest that it might execute the returned data.

JQuery Loving JavaScripts
Figure 2. JQuery Loving JavaScripts

Conclusion

Always specify the dataType for JQueries .ajax call. This ensures you get the data and jQuery can not be tricked into executing JavaScript.

Tags: JavaScript Development