jQuery Basics – The $() Function
$() is identical to, and the most common written form of the jQuery() function, it returns a jQuery object: essentially chunk of content to be written to DOM. In most use cases, a jQuery function will take a selector, element, or object as a parameter. The selector, denoted by a hash (#), is an identifier for existing html content in the current DOM. In the following example we will use the jQuery html() function to modify an element with the #myDivTag selector:
<script src="https://code.jquery.com/jquery-1.10.2.js"></script> <div id="myDivTag">My old div tag text!</div> <script> $( "#myDivTag" ).html("<b>My new div tag text!</b>"); </script>
"My old div tag text!" does not show. The jQuery modifies the DOM at runtime to replace the text of our div element:
This capability is not new. In the old world, the above code would be written similar to the example below:
<div id="myDivTag">My div tag text!</div> <script> document.getElementById("myDivTag").innerHTML = "My new div tag text!"; </script>
As you can see, the jQuery function is similar to the getElementById() function. But there is an important difference: jQuery accepts more than just a selector ID, including HTML and script content. Below shows an example of this:
Our ‘Hello world!’ is now bound to browser DOM and visible on screen.
jQuery and Application Pentesting
Application pentesters may already see the attack vectors in the examples above, however there’s a point we cannot stress enough: jQuery() and some element specific sub-functions are execution sinks.
jQuery’s “XSS Vulnerability”
<html><body> <script src="https://code.jquery.com/jquery-1.6.1.js"></script> <script> $(window.location.hash).appendTo("body"); </script> </body></html>
On the page below we can introduce arbitrary script directly into the browser DOM, this even bypasses Chrome’s
This XSS vector is so common that jQuery eventually changed the selector handling characteristics to prevent such attacks. A change was soon put in place to block HTML strings starting with a
#. This requirement defeats XSS vectors from the window.location.hash property as content will always start with a hash.
In the following example using jQuery 1.6.1, an XSS bug is simulated. This passes script beginning with a # character as it would when being consumed from the location.hash property:
The code successfully executes. In the example below we upgrade jQuery to 1.6.3 and run the same code:
The code no longer runs because the string starts with a # character. Not long after this change an additional behavior change was made to further fine tune the html detection of jQuery. In version 1.9.0b1 it became mandatory for html content to start with a
< character. The discussion can be found here.
jQuery’s AJAX $.get() Response Handling Weakness
The jQuery ajax $.get() function (not to be confused with the .get() function) is used to make, as you might have guessed, ajax GET requests. It was found that in versions prior to 1.12.0 would automatically evaluate response content, potentially executing script if it were contained in a response. Unlike the selector handling issue described above, we believe this behavior to be considered dangerous and potentially unexpected to even savvy developers. The important follow up to that statement is the scenarios in which this issue may manifest are far more unlikely than that of the previous issue. This behavior may facilitate two potential vulnerabilities in an application:
- Applications making cross domain requests to untrusted domains may inadvertently execute script which may otherwise be perceived as safe content.
- Requests to trusted API endpoints may be leveraged in XSS attacks if script can be injected into data sources.
Like almost all modern software, jQuery aims to be powerful and versatile. There are countless safe and legitimate functions which can contribute to security vulnerabilities when misused. The jQuery issues described here were all a result of software which functioned as designed but was implemented improperly.