Skip to content

Commit 9ff6d68

Browse files
authored
Merge pull request #4778 from asgerf/js/more-prototype-pollution
Approved by erik-krogh, mchammer01
2 parents af180d4 + ed729a1 commit 9ff6d68

File tree

49 files changed

+3693
-3079
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+3693
-3079
lines changed
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
lgtm,codescanning
2+
* We've improved the detection of prototype pollution, and the queries involved have been reorganized:
3+
* A new query "Prototype-polluting assignment" (`js/prototype-polluting-assignment`) has been added. This query
4+
highlights direct modifications of an object obtained via a user-controlled property name, which may accidentally alter `Object.prototype`.
5+
* The query previously named "Prototype pollution" (`js/prototype-pollution`) has been renamed to "Prototype-polluting merge call".
6+
This highlights indirect modification of `Object.prototype` via an unsafe `merge` call taking a user-controlled object as argument.
7+
* The query previously named "Prototype pollution in utility function" (`js/prototype-pollution-utility`) has been renamed to "Prototype-polluting function".
8+
This query highlights the implementation of an unsafe `merge` function, to ensure a robust API is exposed downstream.
9+
* The above queries have been moved to the Security/CWE-915 folder, and assigned the following tags: CWE-078, CWE-079, CWE-094, CWE-400, and CWE-915.
10+
* The query "Type confusion through parameter tampering" (`js/type-confusion-through-parameter-tampering`) now highlights
11+
ineffective prototype pollution checks that can be bypassed by type confusion.

javascript/config/suites/javascript/security

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@
3737
+ semmlecode-javascript-queries/Security/CWE-338/InsecureRandomness.ql: /Security/CWE/CWE-338
3838
+ semmlecode-javascript-queries/Security/CWE-346/CorsMisconfigurationForCredentials.ql: /Security/CWE/CWE-346
3939
+ semmlecode-javascript-queries/Security/CWE-352/MissingCsrfMiddleware.ql: /Security/CWE/CWE-352
40-
+ semmlecode-javascript-queries/Security/CWE-400/PrototypePollution.ql: /Security/CWE/CWE-400
41-
+ semmlecode-javascript-queries/Security/CWE-400/PrototypePollutionUtility.ql: /Security/CWE/CWE-400
40+
+ semmlecode-javascript-queries/Security/CWE-915/PrototypePollutingAssignment.ql: /Security/CWE/CWE-915
41+
+ semmlecode-javascript-queries/Security/CWE-915/PrototypePollutingFunction.ql: /Security/CWE/CWE-915
42+
+ semmlecode-javascript-queries/Security/CWE-915/PrototypePollutingMergeCall.ql: /Security/CWE/CWE-915
4243
+ semmlecode-javascript-queries/Security/CWE-400/RemotePropertyInjection.ql: /Security/CWE/CWE-400
4344
+ semmlecode-javascript-queries/Security/CWE-502/UnsafeDeserialization.ql: /Security/CWE/CWE-502
4445
+ semmlecode-javascript-queries/Security/CWE-506/HardcodedDataInterpretedAsCode.ql: /Security/CWE/CWE-506

javascript/ql/src/Security/CWE-843/TypeConfusionThroughParameterTampering.ql

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ import DataFlow::PathGraph
1515

1616
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
1717
where cfg.hasFlowPath(source, sink)
18-
select sink.getNode(), source, sink, "Potential type confusion for $@.", source.getNode(),
19-
"HTTP request parameter"
18+
select sink.getNode(), source, sink,
19+
"Potential type confusion as $@ may be either an array or a string.", source.getNode(),
20+
"this HTTP request parameter"
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
Most JavaScript objects inherit the properties of the built-in <code>Object.prototype</code> object.
9+
Prototype pollution is a type of vulnerability in which an attacker is able to modify <code>Object.prototype</code>.
10+
Since most objects inherit from the compromised <code>Object.prototype</code> object, the attacker can use this
11+
to tamper with the application logic, and often escalate to remote code execution or cross-site scripting.
12+
</p>
13+
14+
<p>
15+
One way to cause prototype pollution is by modifying an object obtained via a user-controlled property name.
16+
Most objects have a special <code>__proto__</code> property that refers to <code>Object.prototype</code>.
17+
An attacker can abuse this special property to trick the application into performing unintended modifications
18+
of <code>Object.prototype</code>.
19+
</p>
20+
</overview>
21+
22+
<recommendation>
23+
<p>
24+
Use an associative data structure that is resilient to untrusted key values, such as a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">Map</a>.
25+
In some cases, a prototype-less object created with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create">Object.create(null)</a>
26+
may be preferable.
27+
</p>
28+
<p>
29+
Alternatively, restrict the computed property name so it can't clash with a built-in property, either by
30+
prefixing it with a constant string, or by rejecting inputs that don't conform to the expected format.
31+
</p>
32+
</recommendation>
33+
34+
<example>
35+
<p>
36+
In the example below, the untrusted value <code>req.params.id</code> is used as the property name
37+
<code>req.session.todos[id]</code>. If a malicious user passes in the ID value <code>__proto__</code>,
38+
the variable <code>todo</code> will then refer to <code>Object.prototype</code>.
39+
Finally, the modification of <code>todo</code> then allows the attacker to inject arbitrary properties
40+
onto <code>Object.prototype</code>.
41+
</p>
42+
43+
<sample src="examples/PrototypePollutingAssignment.js"/>
44+
45+
<p>
46+
One way to fix this is to use <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">Map</a> objects to associate key/value pairs
47+
instead of regular objects, as shown below:
48+
</p>
49+
50+
<sample src="examples/PrototypePollutingAssignmentFixed.js"/>
51+
52+
</example>
53+
54+
<references>
55+
<li>MDN:
56+
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto">Object.prototype.__proto__</a>
57+
</li>
58+
<li>MDN:
59+
<a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map">Map</a>
60+
</li>
61+
</references>
62+
</qhelp>
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* @name Prototype-polluting assignment
3+
* @description Modifying an object obtained via a user-controlled property name may
4+
* lead to accidental mutation of the built-in Object prototype,
5+
* and possibly escalate to remote code execution or cross-site scripting.
6+
* @kind path-problem
7+
* @problem.severity warning
8+
* @precision high
9+
* @id js/prototype-polluting-assignment
10+
* @tags security
11+
* external/cwe/cwe-078
12+
* external/cwe/cwe-079
13+
* external/cwe/cwe-094
14+
* external/cwe/cwe-400
15+
* external/cwe/cwe-915
16+
*/
17+
18+
import javascript
19+
import semmle.javascript.security.dataflow.PrototypePollutingAssignment::PrototypePollutingAssignment
20+
import DataFlow::PathGraph
21+
22+
from Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
23+
where cfg.hasFlowPath(source, sink)
24+
select sink, source, sink,
25+
"This assignment may alter Object.prototype if a malicious '__proto__' string is injected from $@.",
26+
source.getNode(), "here"

javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.qhelp renamed to javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.qhelp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929

3030
<p>
3131
Only merge or assign a property recursively when it is an own property of the <em>destination</em> object.
32-
Alternatively, blacklist the property names <code>__proto__</code> and <code>constructor</code>
32+
Alternatively, block the property names <code>__proto__</code> and <code>constructor</code>
3333
from being merged or assigned to.
3434
</p>
3535
</recommendation>
@@ -39,7 +39,7 @@
3939
This function recursively copies properties from <code>src</code> to <code>dst</code>:
4040
</p>
4141

42-
<sample src="examples/PrototypePollutionUtility.js"/>
42+
<sample src="examples/PrototypePollutingFunction.js"/>
4343

4444
<p>
4545
However, if <code>src</code> is the object <code>{"__proto__": {"isAdmin": true}}</code>,
@@ -51,13 +51,13 @@
5151
are merged recursively:
5252
</p>
5353

54-
<sample src="examples/PrototypePollutionUtility_fixed.js"/>
54+
<sample src="examples/PrototypePollutingFunction_fixed.js"/>
5555

5656
<p>
57-
Alternatively, blacklist the <code>__proto__</code> and <code>constructor</code> properties:
57+
Alternatively, block the <code>__proto__</code> and <code>constructor</code> properties:
5858
</p>
5959

60-
<sample src="examples/PrototypePollutionUtility_fixed2.js"/>
60+
<sample src="examples/PrototypePollutingFunction_fixed2.js"/>
6161
</example>
6262

6363
<references>

javascript/ql/src/Security/CWE-400/PrototypePollutionUtility.ql renamed to javascript/ql/src/Security/CWE-915/PrototypePollutingFunction.ql

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
/**
2-
* @name Prototype pollution in utility function
3-
* @description Recursively assigning properties on objects may cause
4-
* accidental modification of a built-in prototype object.
2+
* @name Prototype-polluting function
3+
* @description Functions recursively assigning properties on objects may be
4+
* the cause of accidental modification of a built-in prototype object.
55
* @kind path-problem
66
* @problem.severity warning
77
* @precision high
88
* @id js/prototype-pollution-utility
99
* @tags security
10+
* external/cwe/cwe-078
11+
* external/cwe/cwe-079
12+
* external/cwe/cwe-094
1013
* external/cwe/cwe-400
11-
* external/cwe/cwe-471
14+
* external/cwe/cwe-915
1215
*/
1316

1417
import javascript
@@ -276,26 +279,26 @@ class PropNameTracking extends DataFlow::Configuration {
276279
}
277280

278281
override predicate isBarrierGuard(DataFlow::BarrierGuardNode node) {
279-
node instanceof BlacklistEqualityGuard or
280-
node instanceof WhitelistEqualityGuard or
282+
node instanceof DenyListEqualityGuard or
283+
node instanceof AllowListEqualityGuard or
281284
node instanceof HasOwnPropertyGuard or
282285
node instanceof InExprGuard or
283286
node instanceof InstanceOfGuard or
284287
node instanceof TypeofGuard or
285-
node instanceof BlacklistInclusionGuard or
286-
node instanceof WhitelistInclusionGuard or
288+
node instanceof DenyListInclusionGuard or
289+
node instanceof AllowListInclusionGuard or
287290
node instanceof IsPlainObjectGuard
288291
}
289292
}
290293

291294
/**
292295
* Sanitizer guard of form `x === "__proto__"` or `x === "constructor"`.
293296
*/
294-
class BlacklistEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNode {
297+
class DenyListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNode {
295298
override EqualityTest astNode;
296299
string propName;
297300

298-
BlacklistEqualityGuard() {
301+
DenyListEqualityGuard() {
299302
astNode.getAnOperand().getStringValue() = propName and
300303
propName = unsafePropName()
301304
}
@@ -310,10 +313,10 @@ class BlacklistEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNod
310313
/**
311314
* An equality test with something other than `__proto__` or `constructor`.
312315
*/
313-
class WhitelistEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNode {
316+
class AllowListEqualityGuard extends DataFlow::LabeledBarrierGuardNode, ValueNode {
314317
override EqualityTest astNode;
315318

316-
WhitelistEqualityGuard() {
319+
AllowListEqualityGuard() {
317320
not astNode.getAnOperand().getStringValue() = unsafePropName() and
318321
astNode.getAnOperand() instanceof Literal
319322
}
@@ -427,10 +430,10 @@ class TypeofGuard extends DataFlow::LabeledBarrierGuardNode, DataFlow::ValueNode
427430
/**
428431
* A check of form `["__proto__"].includes(x)` or similar.
429432
*/
430-
class BlacklistInclusionGuard extends DataFlow::LabeledBarrierGuardNode, InclusionTest {
433+
class DenyListInclusionGuard extends DataFlow::LabeledBarrierGuardNode, InclusionTest {
431434
UnsafePropLabel label;
432435

433-
BlacklistInclusionGuard() {
436+
DenyListInclusionGuard() {
434437
exists(DataFlow::ArrayCreationNode array |
435438
array.getAnElement().getStringValue() = label and
436439
array.flowsTo(getContainerNode())
@@ -447,8 +450,8 @@ class BlacklistInclusionGuard extends DataFlow::LabeledBarrierGuardNode, Inclusi
447450
/**
448451
* A check of form `xs.includes(x)` or similar, which sanitizes `x` in the true case.
449452
*/
450-
class WhitelistInclusionGuard extends DataFlow::LabeledBarrierGuardNode {
451-
WhitelistInclusionGuard() {
453+
class AllowListInclusionGuard extends DataFlow::LabeledBarrierGuardNode {
454+
AllowListInclusionGuard() {
452455
this instanceof TaintTracking::PositiveIndexOfSanitizer
453456
or
454457
this instanceof TaintTracking::MembershipTestSanitizer and

javascript/ql/src/Security/CWE-400/PrototypePollution.qhelp renamed to javascript/ql/src/Security/CWE-915/PrototypePollutingMergeCall.qhelp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434
and then copied into a new object:
3535
</p>
3636

37-
<sample src="examples/PrototypePollution1.js"/>
37+
<sample src="examples/PrototypePollutingMergeCall1.js"/>
3838

3939
<p>
4040
Prior to lodash 4.17.11 this would be vulnerable to prototype pollution. An attacker could send the following GET request:
@@ -47,7 +47,7 @@
4747
Fix this by updating the lodash version:
4848
</p>
4949

50-
<sample src="examples/PrototypePollution_fixed.json"/>
50+
<sample src="examples/PrototypePollutingMergeCall_fixed.json"/>
5151

5252
<p>
5353
Note that some web frameworks, such as Express, parse query parameters using extended URL-encoding
@@ -56,7 +56,7 @@
5656
The example below would also be susceptible to prototype pollution:
5757
</p>
5858

59-
<sample src="examples/PrototypePollution2.js"/>
59+
<sample src="examples/PrototypePollutingMergeCall2.js"/>
6060

6161
<p>
6262
In the above example, an attacker can cause prototype pollution by sending the following GET request:

javascript/ql/src/Security/CWE-400/PrototypePollution.ql renamed to javascript/ql/src/Security/CWE-915/PrototypePollutingMergeCall.ql

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
11
/**
2-
* @name Prototype pollution
2+
* @name Prototype-polluting merge call
33
* @description Recursively merging a user-controlled object into another object
4-
* can allow an attacker to modify the built-in Object prototype.
4+
* can allow an attacker to modify the built-in Object prototype,
5+
* and possibly escalate to remote code execution or cross-site scripting.
56
* @kind path-problem
67
* @problem.severity error
78
* @precision high
89
* @id js/prototype-pollution
910
* @tags security
10-
* external/cwe/cwe-250
11+
* external/cwe/cwe-078
12+
* external/cwe/cwe-079
13+
* external/cwe/cwe-094
1114
* external/cwe/cwe-400
15+
* external/cwe/cwe-915
1216
*/
1317

1418
import javascript
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
let express = require('express');
2+
3+
express.put('/todos/:id', (req, res) => {
4+
let id = req.params.id;
5+
let items = req.session.todos[id];
6+
if (!items) {
7+
items = req.session.todos[id] = {};
8+
}
9+
items[req.query.name] = req.query.text;
10+
res.end(200);
11+
});

0 commit comments

Comments
 (0)