Safer together: Snyk and CISPA collaborate for the greater good
Aviad Hahami
6. Juni 2022
0 Min. LesezeitGreat things happen when the academic world and the software industry work together! Today, we’d like to share a story about our recent collaboration with the CISPA Helmholtz Center for Information Security, a big science institution in Germany.
Back in January, Cris Staicu Ph.D. (Tenure-Track Faculty, CISPA), contacted us about his research on NodeJS and JavaScript. The study Staicu and his team conducted focused on interesting security issues in JavaScript and NodeJS environments, such as prototype-pollution vulnerabilities, JS sandbox escapes, and low-level C/C++ vulnerabilities in NodeJS native extensions.
Alongside CISPA, we were able to confirm a few not-so-straightforward vulnerabilities, report them to maintainers for fixes, and assign CVEs and published advisories.
In this post, we’ll walk you through a few of the vulnerabilities we found with CISPA, while also calling on other academic institutions to collaborate with us to make the open-source world safer for everyone.
NodeJS sandbox escape gadgets
The first vulnerability we worked on was a Sandbox Escape vulnerability.
For those who are unfamiliar with the term “sandbox”, it refers to an environment, process, or machine where we can execute things without worrying about security implications. For example, when we’re dealing with unsanitized client-supplied code or files.
In this case, Cris’s team found a JS library with a bypassable sandbox. The reported library, notevil
and its derivative argencoders-notevil
, allowed Cris’s team to bypass the sandbox by abusing a Prototype Pollution vulnerability.
The PoC for this issue looks like this:
1var notevil = require('notevil')
2
3notevil(`
4Object.defineProperty(({})[["__proto__"]][["__proto__"]], 'polluted', {
5 value: 'success'
6});`);
7
8console.log(polluted); // prints "success"
While the PoC is not harmful at first sight, a similar vulnerability in the same library was previously reported, and can result in RCE in NodeJS environments, or XSS in browser environments.
Since the library is already deprecated (but still used by many), Cris’s team needed our help with issuing a CVE and notifying the maintainers. If you didn’t know, Snyk is an official CNA, allowing us to issue CVEs when required.Thus, we issued CVE-2021-23771 to make future developers aware of this vulnerability.
Type-confusion in native NodeJS extensions
Since CISPA’s focus was on Type Confusion vulnerabilities between NodeJS applications and C extensions for the NodeJS engine, we collaborated on the topic. Most of CISPA’s Type Confusion experiments resulted in the V8 engine crash, so both parties wanted to dig in and understand the problem better.
As an example, let’s use the libxmljs
library. If you’re unfamiliar, libxmljs
is used for XML parsing and employs the native libxml
binary behind the scenes.
Here’s a snippet of the basic usage example for the library, copied from their README:
1// As copied from libxmljs readme
2var libxmljs = require("libxmljs");
3var xml = '<?xml version="1.0" encoding="UTF-8"?>' +
4 '<root>' +
5 '<child foo="bar">' +
6 '<grandchild baz="fizbuzz">grandchild content</grandchild>' +
7 '</child>' +
8 '<sibling>with content!</sibling>' +
9 '</root>';
10
11var xmlDoc = libxmljs.parseXml(xml); // you can now query the document
However, we were able to crash the whole application with the following PoC:
1let libxmljs = require("libxmljs");
2let xml = {toString: 23};
3try {
4 libxmljs.parseXml(xml);
5} catch(e) {
6 // never executed because of the hard crash
7}
Since the application can be crashed using user input, despite the try/catch blocks in place, we’re facing a DoS entry point.
Running the code from the previous block will yield the following stack trace:
1FATAL ERROR: v8::ToLocalChecked Empty MaybeLocal.
2 1: 0xb23a90 node::Abort() [node]
3 2: 0xa3823c node::FatalError(char const*, char const*) [node]
4 3: 0xd13ffa v8::Utils::ReportApiFailure(char const*, char const*) [node]
5 4: 0x7fa0bc5eec9b libxmljs::XmlDocument::FromXml(Nan::FunctionCallbackInfo<v8::Value> const&) [/opt/node_modules/libxmljs/build/Release/xmljs.node]
6 5: 0x7fa0bc5ea208 [/opt/node_modules/libxmljs/build/Release/xmljs.node]
7 6: 0xd7019e [node]
8 7: 0xd715bf v8::internal::Builtin_HandleApiCall(int, unsigned long*, v8::internal::Isolate*) [node]
9 8: 0x16138f9 [node]
10Aborted
Further debugging with GDB helped us understand that the underlying engine expects to get a String type argument, but – as we found – when passed an object, it will attempt to stringify it.
In the above example, the toString
method is not invokable (since it’s just a number). The V8 can’t handle that and raises an exception. However, since this exception is in the V8, the try/catch blocks are not effective — resulting in a full application crash.
We approached the maintainers alongside CISPA and responsibly disclosed the vulnerability, tracking it as CVE-2022-21144.
Call for collaborations
We’d love to collaborate with more academic institutions like CISPA, and partnering with Snyk comes with several helpful advantages. It's one thing to discover vulnerabilities, but it's another thing to disclose vulnerabilities — especially when the vulnerabilities are in open-source libraries. Our dedicated team of analysts and researchers will help you verify and understand the full impact of a given vulnerability, and our process guarantees that the vulnerability will be disclosed responsibly, discreetly, and professionally. And, since Snyk is a CNA, we have a special tracking system and can sync to the reporter and maintainers, keeping both parties informed at all times.
So, feel free to contact us the next time you discover a new vulnerability. We'll be happy to help with the confirmation and disclosure processes.
Stay Secure!