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 whichsafeLoad
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.