the function deepFromFlat of underscore.deep is vulnerable to prototype pollution in clever/underscore.deep
Reported on
Feb 2nd 2022
Prototype Pollution in Clever/underscore.deep
Reported on Feb 2nd 2022 | Timothee Desurmont
Description
Vulnerability type: CWE-1321
Version 0.5.1 of underscore.deep is vulnerable to prototype pollution; the function deepFromFlat in underscore.deep.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).
Impact
Common Consequences
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. |
Occurrences
underscore.deep.coffee L126-L139
Proof of Concept
- Create the following PoC file:
// poc_.js
const _ = require('underscore.deep');
console.log(`[+] Before prototype pollution : ${{}.polluted}`);
_.deepFromFlat({ "__proto__.polluted": true });
// _.deepFromFlat({ "constructor.prototype.polluted": true });
console.log(`[+] After prototype pollution : ${{}.polluted}`);
- Execute the following commands in terminal:
npm i underscore.deep # Install the package
node poc.js # Run the PoC
- Check the Output of the PoC files:
[+] Before prototype pollution : undefined
[+] After prototype pollution : true
Proof of Fix
By using a denylist of dangerous attributes, this weakness can be eliminated.
- Replace the deepFromFlat function with the following code:
// underscore.deep.js
deepFromFlat: deepFromFlat = function(o) {
var k, key, oo, part, parts, t;
oo = {};
t = void 0;
parts = void 0;
part = void 0;
for (k in o) {
t = oo;
parts = k.split(".");
key = parts.pop();
while (parts.length) {
part = parts.shift();
//denylist of dengerous attributes
if (["__proto__", "constructor", "prototype"].includes(part)) continue;
t = t[part] = t[part] || {};
}
t[key] = o[k];
}
return oo;
}
- Check the Output of the PoC file:
[+] Before prototype pollution : undefined
[+] After prototype pollution : undefined
The vulnerability has been patched.
SECURITY.md
2 years ago
Hi @Admin, any update regarding this vulnerability finding, can we post the link to this private report on their Github page?
Hi there! We're going to be looking into this further. Stay tuned.
First off, thank you for reaching out and submitting a patch. I believe I've had to tweak a few things to get the library working for Node v12: https://github.com/Clever/underscore.deep/pull/30.
Second, I've attached my patch below.
diff --git a/test/deepFromFlat.coffee b/test/deepFromFlat.coffee
index 2579672..4630320 100644
--- a/test/deepFromFlat.coffee
+++ b/test/deepFromFlat.coffee
@@ -20,3 +20,13 @@ describe '_.deepFromFlat', ->
_(tests).each (test) ->
it "deepens #{JSON.stringify test.input}", ->
assert.deepEqual _.deepFromFlat(test.input), test.output
+
+ it "does not merge special `Object` properties", ->
+ _.deepFromFlat({ "__proto__.polluted1": true })
+ _.deepFromFlat({ "constructor.prototype.polluted2": true })
+ p1 = {}.polluted1
+ p2 = {}.polluted2
+ assert.strictEqual(p1, undefined)
+ assert.strictEqual(p2, undefined)
+ delete {}.polluted1
+ delete {}.polluted2
diff --git a/underscore.deep.coffee b/underscore.deep.coffee
index cae9019..7114ea4 100644
--- a/underscore.deep.coffee
+++ b/underscore.deep.coffee
@@ -136,6 +136,7 @@ module.exports =
key = parts.pop()
while parts.length
part = parts.shift()
+ continue if part in ["__proto__", "constructor", "prototype"]
t = t[part] = t[part] or {}
t[key] = o[k]
oo
I believe this covers the prototype pollution case while sticking to the CoffeeScript conventions. Some questions that are top of mind, though:
Q1: Can you apply this patch to your local branch and confirm the tests pass? Can you confirm that disabling the newly added line of protection causes the test to fail?
Q2: Are there other keywords that we should be wary of? Or does covering prototype
shield from other keywords being modified?
I'll do some additional research for this to validate, but again, thank you for reaching out.
Hi Mark,
Q1: yes the deepFromFlat test pass when I apply the patch, and fails without it.
Q2: no you should be pretty much covered with these 3 keywords.
I can recommend you the following links for your research:
- CWE-1321: Improperly Controlled Modification of Object Prototype Attributes
- Olivier Arteau. "Prototype pollution attack in NodeJS application". 2018-05-15
- Github Advisories on related topic, you will often find a link to the vulnerability report.
I missed something,
Replace these two lines in your test file:
delete {}.polluted1
delete {}.polluted2
By the following:
delete Object.prototype.polluted1
delete Object.prototype.polluted2
Cheers! I'll make sure to apply that to the patch, and work on the draft security advisory, with you as invited. I'm going to take the username of @Sampaguitas
to be the one to invite.
Two additional questions: Q1: Have you filed a CVE for this yet? Q2: What's the email associated with your HackerOne account? We would like to invite you there and report the bug there so that we can reward a bounty for finding this for us.
Q1 I've answered for myself, it'll go out with the Security Advisory that I'll be publishing soon. Q2 is still open.
@maintainer - do you still require a CVE? We can assign and publish this on your behalf. Otherwise, could you let me know the CVE ID and we will add it to the report?
Jamie, we've requested a CVE via GitHub: https://github.com/Clever/underscore.deep/security/advisories/GHSA-8j79-hfj5-f2xm. Not sure when that will go live, but I'll reach out when it does.
Seems like GitHub just got back to us, the CVE is CVE-2022-31106
, which shows up on https://github.com/Clever/underscore.deep/security/advisories/GHSA-8j79-hfj5-f2xm. I can't change that on the right-hand sidebar, so I'll leave that to you Jamie.
Hi @Mark, That is great ! I have resubmitted the report on h1 as you suggested, But could not find the proper vulnerability type or scope in the drop down options... Let me know if you would like me to edit. here is the link: https://hackerone.com/bugs?report_id=1618405