Skip to content

Commit 9268050

Browse files
authored
Merge pull request github#5369 from erik-krogh/tempObjInj
Approved by asgerf
2 parents a9c292e + b039267 commit 9268050

File tree

17 files changed

+449
-128
lines changed

17 files changed

+449
-128
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
lgtm,codescanning
2+
* The `js/template-object-injection` query has been added. It highlights places where an attacker can pass special parameters to a template engine.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
<overview>
6+
<p>
7+
Directly using user-controlled objects as arguments to template engines might allow an attacker to do
8+
local file reads or even remote code execution.
9+
</p>
10+
</overview>
11+
12+
<recommendation>
13+
<p>
14+
Avoid using user-controlled objects as arguments to a template engine. Instead, construct the object explicitly with
15+
the specific properties needed by the template.
16+
</p>
17+
</recommendation>
18+
19+
<example>
20+
<p>
21+
In the example below a server uses the user-controlled <code>profile</code> object to
22+
render the <code>index</code> template.
23+
</p>
24+
<sample src="examples/TemplateObjectInjection.js" />
25+
<p>
26+
However, if an attacker adds a <code>layout</code> property to the <code>profile</code> object then
27+
the server will load the file specified by the <code>layout</code> property, thereby allowing an attacker
28+
to do local file reads.
29+
</p>
30+
<p>
31+
The fix is to have the server construct the object, and only add the properties that are needed by the template.
32+
</p>
33+
<sample src="examples/TemplateObjectInjection_fixed.js" />
34+
</example>
35+
36+
<references>
37+
<li>
38+
blog.shoebpatel.com: <a href="https://blog.shoebpatel.com/2021/01/23/The-Secret-Parameter-LFR-and-Potential-RCE-in-NodeJS-Apps/">The Secret Parameter, LFR, and Potential RCE in NodeJS Apps</a>.
39+
</li>
40+
<li>
41+
cwe.mitre.org: <a href="https://cwe.mitre.org/data/definitions/73.html">CWE-73: External Control of File Name or Path</a>
42+
</li>
43+
44+
</references>
45+
</qhelp>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* @name Template Object Injection
3+
* @description Instantiating a template using a user-controlled object is vulnerable to local file read and potential remote code execution.
4+
* @kind path-problem
5+
* @problem.severity error
6+
* @precision high
7+
* @id js/template-object-injection
8+
* @tags security
9+
* external/cwe/cwe-073
10+
* external/cwe/cwe-094
11+
*/
12+
13+
import javascript
14+
import DataFlow::PathGraph
15+
import semmle.javascript.security.dataflow.TemplateObjectInjection::TemplateObjectInjection
16+
17+
from DataFlow::Configuration cfg, DataFlow::PathNode source, DataFlow::PathNode sink
18+
where cfg.hasFlowPath(source, sink)
19+
select sink.getNode(), source, sink, "Template object injection due to $@.", source.getNode(),
20+
"user-provided value"
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
var app = require('express')();
2+
app.set('view engine', 'hbs');
3+
4+
app.post('/', function (req, res, next) {
5+
var profile = req.body.profile;
6+
res.render('index', profile);
7+
});
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
var app = require('express')();
2+
app.set('view engine', 'hbs');
3+
4+
app.post('/', function (req, res, next) {
5+
var profile = req.body.profile;
6+
res.render('index', {
7+
name: profile.name,
8+
location: profile.location
9+
});
10+
});

javascript/ql/src/experimental/Security/CWE-073/TemplateObjectInjection.ql

Lines changed: 0 additions & 49 deletions
This file was deleted.

javascript/ql/src/experimental/Security/CWE-073/documentation-examples/TemplateObjectInjection.js

Lines changed: 0 additions & 10 deletions
This file was deleted.

javascript/ql/src/experimental/Security/CWE-073/documentation-examples/TemplateObjectInjection_fixed.js

Lines changed: 0 additions & 10 deletions
This file was deleted.

javascript/ql/src/semmle/javascript/frameworks/Express.qll

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -738,16 +738,32 @@ module Express {
738738
* as the value of a template variable.
739739
*/
740740
private class TemplateInput extends HTTP::ResponseBody {
741-
RouteHandler rh;
741+
TemplateObjectInput obj;
742742

743743
TemplateInput() {
744+
obj.getALocalSource().(DataFlow::ObjectLiteralNode).hasPropertyWrite(_, this.flow())
745+
}
746+
747+
override RouteHandler getRouteHandler() { result = obj.getRouteHandler() }
748+
}
749+
750+
/**
751+
* An object passed to the `render` method of an HTTP response object.
752+
*/
753+
class TemplateObjectInput extends DataFlow::Node {
754+
RouteHandler rh;
755+
756+
TemplateObjectInput() {
744757
exists(DataFlow::MethodCallNode render |
745758
render.calls(rh.getAResponseExpr().flow(), "render") and
746-
this = render.getOptionArgument(1, _).asExpr()
759+
this = render.getArgument(1)
747760
)
748761
}
749762

750-
override RouteHandler getRouteHandler() { result = rh }
763+
/**
764+
* Gets the route handler that uses this object.
765+
*/
766+
RouteHandler getRouteHandler() { result = rh }
751767
}
752768

753769
/**
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/**
2+
* Provides a taint-tracking configuration for reasoning about
3+
* template object injection vulnerabilities.
4+
*
5+
* Note, for performance reasons: only import this file if
6+
* `TemplateObjectInjection::Configuration` is needed, otherwise
7+
* `TemplateObjectInjectionCustomizations` should be imported instead.
8+
*/
9+
10+
import javascript
11+
12+
/**
13+
* Provides a taint tracking configuration for reasoning template object injection vulnerabilities.
14+
*/
15+
module TemplateObjectInjection {
16+
import TemplateObjectInjectionCustomizations::TemplateObjectInjection
17+
private import semmle.javascript.security.TaintedObject
18+
19+
/**
20+
* A taint tracking configuration for reasoning about template object injection vulnerabilities.
21+
*/
22+
class TemplateObjInjectionConfig extends TaintTracking::Configuration {
23+
TemplateObjInjectionConfig() { this = "TemplateObjInjectionConfig" }
24+
25+
override predicate isSource(DataFlow::Node source, DataFlow::FlowLabel label) {
26+
source.(Source).getAFlowLabel() = label
27+
}
28+
29+
override predicate isSink(DataFlow::Node sink, DataFlow::FlowLabel label) {
30+
sink instanceof Sink and label = TaintedObject::label()
31+
}
32+
33+
override predicate isSanitizer(DataFlow::Node node) { node instanceof Sanitizer }
34+
35+
override predicate isSanitizerGuard(TaintTracking::SanitizerGuardNode guard) {
36+
guard instanceof TaintedObject::SanitizerGuard
37+
}
38+
39+
override predicate isAdditionalFlowStep(
40+
DataFlow::Node src, DataFlow::Node trg, DataFlow::FlowLabel inlbl, DataFlow::FlowLabel outlbl
41+
) {
42+
TaintedObject::step(src, trg, inlbl, outlbl)
43+
}
44+
}
45+
}

0 commit comments

Comments
 (0)