Skip to content

Commit 337be9c

Browse files
committed
ssti query and help updated
1 parent 09922e5 commit 337be9c

2 files changed

Lines changed: 25 additions & 20 deletions

File tree

javascript/ql/src/experimental/Security/CWE-94/ServerSideTemplateInjection.qhelp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
<overview>
77
<p>
8-
Server-Side Template Injection vulnerabilities occur when user input is embedded
8+
Server-Side Template Injection vulnerabilities occur when user input is embedded
99
in a template in an unsafe manner allowing attackers to access the template context and
1010
run arbitrary code on the application server.
1111
</p>
@@ -21,21 +21,38 @@ render engine with sandbox options.
2121

2222
<example>
2323
<p>
24-
The following example shows a page being rendered with user input allowing attackers to access the
24+
The following example shows a page being rendered with user input allowing attackers to access the
2525
template context and run arbitrary code on the application server.
26+
Pug template engine (and other template engines) provides Interpolation feature - insertion of variable values into a string of some kind.
27+
For example, `Hello #{user.username}!`, could be used for printing username from scoped variable user, but `user.username` expression will be executed as valid javascript code.
28+
Unsafe injection of user input provides attacker ability to inject conteqnt like #{some_js_expression}.
29+
Injection of `#{global.process.exit(1)}` leads to code execution of `global.process.exit(1)` by server.
30+
Working exploit (as curl command):
31+
curl -i -s -k -X $'POST' -H $'Host: 127.0.0.1:5061' -H $'Connection: close' -H $'Content-Length: 40' -H $'Content-Type: application/x-www-form-urlencoded' --data-binary $'name=%23%7Bglobal.process.exit%281%29%7D' $'http://127.0.0.1:5061/'
2632
</p>
2733

2834
<sample src="examples/ServerSideTemplateInjection.js" />
2935
</example>
3036

37+
<example>
38+
<p>
39+
As the example of safe usage of rendering engine, please see example below.
40+
In opposite to first example, instead of concatenation of provided user input with the template
41+
it is possible to provide user input as a context - user input will be safely insterted
42+
and rendered inside correspondent placeholders.
43+
</p>
44+
45+
<sample src="examples/ServerSideTemplateInjectionSafe.js" />
46+
</example>
47+
3148
<references>
3249
<li>
3350
OWASP:
3451
<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>.
3552
</li>
3653
<li>
3754
PortSwigger Research Blog:
38-
<a href="https://portswigger.net/research/server-side-template-injection">Server Side Template Injection</a>.
55+
<a href="https://portswigger.net/research/server-side-template-injection">Server-Side Template Injection</a>.
3956
</li>
4057
</references>
4158
</qhelp>

javascript/ql/src/experimental/Security/CWE-94/ServerSideTemplateInjection.ql

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,12 @@ abstract class ServerSideTemplateInjectionSink extends DataFlow::Node { }
2626
class SSTIPugSink extends ServerSideTemplateInjectionSink {
2727
SSTIPugSink() {
2828
exists(CallNode compile, ModuleImportNode renderImport, Node sink |
29-
(renderImport = moduleImport("pug") or renderImport = moduleImport("jade")) and
29+
renderImport = moduleImport(["pug", "jade"]) and
3030
(
3131
compile = renderImport.getAMemberCall("compile") and
32-
compile.flowsTo(sink) and
3332
sink.getStartLine() != sink.getASuccessor().getStartLine()
3433
or
35-
compile = renderImport.getAMemberCall("render") and compile.flowsTo(sink)
34+
compile = renderImport.getAMemberCall("render")
3635
) and
3736
this = compile.getArgument(0)
3837
)
@@ -43,34 +42,23 @@ class SSTIDotSink extends ServerSideTemplateInjectionSink {
4342
SSTIDotSink() {
4443
exists(CallNode compile, Node sink |
4544
compile = moduleImport("dot").getAMemberCall("template") and
46-
compile.flowsTo(sink) and
4745
sink.getStartLine() != sink.getASuccessor().getStartLine() and
4846
this = compile.getArgument(0)
4947
)
5048
}
5149
}
5250

5351
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-
}
52+
SSTIEjsSink() { this = moduleImport("ejs").getAMemberCall("render").getArgument(0) }
6153
}
6254

6355
class SSTINunjucksSink extends ServerSideTemplateInjectionSink {
6456
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-
)
57+
this = moduleImport("nunjucks").getAMemberCall("renderString").getArgument(0)
7058
}
7159
}
7260

7361
from DataFlow::PathNode source, DataFlow::PathNode sink, ServerSideTemplateInjectionConfiguration c
7462
where c.hasFlowPath(source, sink)
75-
select sink.getNode(), source, sink, "$@ flows to here and is used in an XPath expression.",
63+
select sink.getNode(), source, sink, "$@ flows to here and unsafely used as part of rendered template",
7664
source.getNode(), "User-provided value"

0 commit comments

Comments
 (0)