Skip to content

Commit 68b5750

Browse files
committed
JS SSTI CWE-094
1 parent d8f32e3 commit 68b5750

3 files changed

Lines changed: 150 additions & 0 deletions

File tree

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<!DOCTYPE qhelp PUBLIC
2+
"-//Semmle//qhelp//EN"
3+
"qhelp.dtd">
4+
<qhelp>
5+
6+
<overview>
7+
<p>
8+
Server Side Template Injection vulnerabilities occur when user input is embedded
9+
in a template in an unsafe manner allowing attackers to access the template context and
10+
run arbitrary code on the application server.
11+
</p>
12+
</overview>
13+
14+
<recommendation>
15+
<p>
16+
Avoid including user input in any expression or template which may be dynamically rendered.
17+
If user input must be included, use context-specific escaping before including it or run
18+
render engine with sandbox options.
19+
</p>
20+
</recommendation>
21+
22+
<example>
23+
<p>
24+
The following example shows html page being rendered with user input allowing attackers to access the
25+
template context and run arbitrary code on the application server.
26+
</p>
27+
28+
<sample src="examples/ServerSideTemplateInjection.js" />
29+
</example>
30+
31+
<references>
32+
<li>
33+
OWASP:
34+
<a href="https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/07-Input_Validation_Testing/18-Testing_for_Server_Side_Template_Injection">Server Side Template Injection</a>.
35+
</li>
36+
<li>
37+
PortSwigger Research Blog:
38+
<a href="https://portswigger.net/research/server-side-template-injection">Server Side Template Injection</a>.
39+
</li>
40+
</references>
41+
</qhelp>
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
/**
2+
* @name Server Side Template Injection
3+
* @description Rendering templates with unsanitized user input allows a malicious user arbitrary
4+
* code execution.
5+
* @kind path-problem
6+
* @problem.severity error
7+
* @precision high
8+
* @id js/code-injection
9+
* @tags security
10+
* external/cwe/cwe-094
11+
*/
12+
13+
import javascript
14+
import DataFlow
15+
16+
class ServerSideTemplateInjectionConfiguration extends TaintTracking::Configuration {
17+
ServerSideTemplateInjectionConfiguration() { this = "ServerSideTemplateInjectionConfiguration" }
18+
19+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
20+
21+
override predicate isSink(DataFlow::Node sink) { sink instanceof ServerSideTemplateInjectionSink }
22+
}
23+
24+
abstract class ServerSideTemplateInjectionSink extends DataFlow::Node { }
25+
26+
class SSTIPugSink extends ServerSideTemplateInjectionSink {
27+
SSTIPugSink() {
28+
exists(CallNode compile, ModuleImportNode renderImport, Node sink |
29+
(renderImport = moduleImport("pug") or renderImport = moduleImport("jade")) and
30+
(
31+
compile = renderImport.getAMemberCall("compile") and
32+
compile.flowsTo(sink) and
33+
sink.getStartLine() != sink.getASuccessor().getStartLine()
34+
or
35+
compile = renderImport.getAMemberCall("render") and compile.flowsTo(sink)
36+
) and
37+
this = compile.getArgument(0)
38+
)
39+
}
40+
}
41+
42+
class SSTIDotSink extends ServerSideTemplateInjectionSink {
43+
SSTIDotSink() {
44+
exists(CallNode compile, Node sink |
45+
compile = moduleImport("dot").getAMemberCall("template") and
46+
compile.flowsTo(sink) and
47+
sink.getStartLine() != sink.getASuccessor().getStartLine() and
48+
this = compile.getArgument(0)
49+
)
50+
}
51+
}
52+
53+
class SSTIEjsSink extends ServerSideTemplateInjectionSink {
54+
SSTIEjsSink() {
55+
exists(CallNode compile, Node sink |
56+
compile = moduleImport("ejs").getAMemberCall("render") and
57+
compile.flowsTo(sink) and
58+
this = compile.getArgument(0)
59+
)
60+
}
61+
}
62+
63+
class SSTINunjucksSink extends ServerSideTemplateInjectionSink {
64+
SSTINunjucksSink() {
65+
exists(CallNode compile, Node sink |
66+
compile = moduleImport("nunjucks").getAMemberCall("renderString") and
67+
compile.flowsTo(sink) and
68+
this = compile.getArgument(0)
69+
)
70+
}
71+
}
72+
73+
from DataFlow::PathNode source, DataFlow::PathNode sink, ServerSideTemplateInjectionConfiguration c
74+
where c.hasFlowPath(source, sink)
75+
select sink.getNode(), source, sink, "$@ flows to here and is used in an XPath expression.",
76+
source.getNode(), "User-provided value"
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const express = require('express')
2+
var bodyParser = require('body-parser');
3+
const app = express()
4+
app.use(bodyParser.json());
5+
app.use(bodyParser.urlencoded({ extended: true }));
6+
7+
//Dependent of Templating engine
8+
var jade = require('pug');
9+
const port = 5061
10+
11+
function getHTML(input) {
12+
var template = `
13+
doctype
14+
html
15+
head
16+
title= 'Hello world'
17+
body
18+
form(action='/' method='post')
19+
label(for='name') Name:
20+
input#name.form-control(type='text', placeholder='' name='name')
21+
button.btn.btn-primary(type='submit') Submit
22+
p Hello `+ input
23+
var fn = jade.compile(template);
24+
var html = fn();
25+
console.log(html);
26+
return html;
27+
}
28+
29+
app.post('/', (request, response) => {
30+
var input = request.param('name', "")
31+
var html = getHTML(input)
32+
response.send(html);
33+
})

0 commit comments

Comments
 (0)