A few weeks ago, I described a cross-site scripting vulnerability in the popular handlebars.js library in my blog post here. A number of other JavaScript libraries and applications were also affected because of copy-and-pasted code and a tendency for developers to include and distribute the JavaScript source files directly in their projects. After following our responsbile disclosure policy and working directly with a subset of the developers that responded to ensure that they fixed the vulnerability, I am now publishing the complete list of libraries I originally found along with a detailed write-up about the original vulnerability. Despite our best efforts trying to contact all library owners and giving them 30 days to fix the issue (a period of time we think is reasonable for open-source projects), as of now only 16% of the libraries have been fixed.
Study Results
As I mentioned in my last post copy-and-paste code is prolific in the JavaScript community. So when I went hunting for libraries that used a copy of the vulnerable code from the XSS issue in handlebars.js library, I was not disappointed.
I identified 35 JavaScript libraries on NPMJS.org that contained a hard-copy of the vulnerable version of handlebars and I found 2 Java libraries on Maven Central. In the case of the Java libraries (jmustache and mustache.java), I found that they contained an independent implementation of the mustache templating system rather than the vulnerable file itself but similar to the issue in the original handlebars.js library I found that these Java libraries were also missing the =
character when escaping HTML.
Below you can see the escaped characters in JavaScript (handlebars.js) on the left hand side compared to the ones in Java (jmustache and mustache.java) as found in the vulnerable libraries:
The Java implementation looks slightly different than the JavaScript implementation but both of them do not include the =
character in the list of escaped characters and so have the same XSS vulnerability.
We privately disclosed this vulnerablility to the developers of all JavaScript libraries sending them our responsible disclosure policy on the 3rd of December 2015 and the Java libraries on the 21st December 2015. Out of the 37 people we contacted only 12 replied i.e 68% of the people never responded at all. In order to get some idea of the widespread nature of this issue, I counted the total number of downloads for all affected libraries for the month of January 2016 which shows 41,498 downloads. That is a lot of vulnerable software.
I would like to thank the following 6 libraries for fixing the issue and upgrading to the latest version of handlebars.js:
JavaScript libraries fixed
Name | Fixed Version |
---|---|
catberry-handlebars | 2.0.1 |
panel.js | 1.8.1 |
bauplan | 0.1.1 |
ember-precompilex | 0.2.11 |
Java libraries fixed
Name | Fixed Version |
---|---|
com.samskivert : jmustache | 1.12 |
com.github.spullara.mustache.java : compiler | The latest version is available from https://github.com/spullara/mustache.java/tree/master/compiler |
In addition, there were 2 Ruby Gems identified by the Ruby Advisory Database that contained the same vulnerable handlebars.js file and have now been fixed:
Name | Fixed Version |
---|---|
handlebars-source | 4.0.0 |
mustache-js-rails | 2.0.3 |
We urge all users of these libraries to upgrade to the latest version in order to mitigate this issue from their projects.
Here are the top 10 most popular libraries that are still vulnerable, all with over 100 downloads in the last month:
Name | Downloads January 2016 |
---|---|
yui | 34974 |
handlebars-runtime | 1569 |
grunt-ember-handlebars | 1242 |
require-handlebars-plugin | 1075 |
selleck | 583 |
assembly | 492 |
nginb | 277 |
grunt-static-handlebars | 209 |
ember-precompile | 206 |
mimosa-ember-handlebars | 97 |
To see the full list of libraries that are still vulnerable, please refer to this spreadsheet https://docs.google.com/spreadsheets/d/1dqH7FECg4b5t9VbqJrFAnAKkDiZQjVJ9YJ4WXdF85M4.
Since this vulnerability affects multiple languages, we have created an artifact in our catalog for each of them - JavaScript, Ruby and Java. If you are a SourceClear user, you are already protected against this issue. You can use our agent, terminal application, Maven or Gradle plugins to scan and detect this vulnerability in your projects. The full technical write up about the original issue is also available below.
Summary
Cutting and pasting open-source code is common. When that code contains vulnerabilities many libraries are affected and many developers will download and use vulnerable code. Even when notified the majority of authors do not fix the vulnerable libraries.
Technical Write-Up
Handlebars is a templating language that is used by many applications to generate HTML code. This vulnerability in handlebars is caused by the missing =
from the set of characters that are escaped when an variable is inserted into the HTML.
Explanation of how the vulnerability impacts the library
The fix commit to the handlebars library.
The fix adds the =
character to the escaped characters. These characters are used as a part of the escapeChar
method and ultimately the escapeExpression
method. The escapeExpression
method is used by the handlebars compiler during the construction of an Abstract Syntax Tree (AST).
The expression is checked to determine if it shoud be escaped in the MustacheStatement
of the AST located in lib/handlebars/compilers/compiler.js.
Note: We are still talking in terms of the handlebars package. Handlebars was based off of the mustache templating language and some variables names remain the same.
If the expression needs to be escaped, the appendEscaped
method which is located in lib/handlebars/compiler/javascript-compiler.js), is invoked which uses the escapeExpression
method. All text will be escaped, unless the noEscape
expression is set.
Prior to adding the =
character to the list of escaped characters, the library was suspectible to cross-site scripting if a program used the handlebars templating language to put a user-controlled value directly into an unquoted attribute of an HTML tag such as <a href src={{foo}}>Click me!</a>
. Here, the foo
attribute would not correctly be escaped.
Walkthrough of a Proof of Concept
In order to demonstrate this vulnerability, we created a small sample HTML application that uses handlebars. Our example takes user-supplied input from the parameters and inserts it into the foo
value in the context.
In order to exploit this example, an attacker must attach their JavaScript following another character and a space. Without the space, handlebars assumes that the entire message is what is supposed to go into the src
tag of the HTML.
For example: If an attacker were to input test.com onmouseover=alert("Gotchya!")
, it would be interpreted as
<a href="" src="//www-stage.veracode.com/test.com" onmouseover="alert("GOTCHYA!")">Click me!</a>
Thus, executing the JavaScript when a user's mouse rolls over the "Click me!" link. Although the example shows that the ""
characters are escaped in the alert box, the browser still interprets them as the quoting characters, creating the valid JavaScript.
However, there are some constraints about how the XSS payload is passed through. If an attacker were to input onmouseover=alert("Gotchya!")
as the parameter, this would be escaped by handlebars and turned into
<a href="" src="//www-stage.veracode.com/alert%28%26quot%3BGOTCHYA%21%26quot%3B%29">Click me!</a>
and thus the JavaScript would not be executed because invalid JavaScript is created.
Additionally, if the JavaScript passed through in the HTML attribute contains any excess spaces, they would also turn into their own tags and result in the JavaScript not being executed. For example, the parameter test.com onmouseover=alert("GOT YOU")
would be interepreted as
<a href="" src="//www-stage.veracode.com/test.com" onmouseover="alert("GOT" you")="">Click me!</a>
Once the fix is applied, the payload test.com onmouseover=alert("Gotchya!")
would become:
<a href="" src="//www-stage.veracode.com/test.com" onmouseover=alert("Gotchya!")="">Click me!</a>
As you can see the string is correctly escaped.