Skip to content
Closed

ssti #3886

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>Server Side Template Injection in Jinja2 2.10</p>
</overview>

<recommendation>
<p>An issue was discovered in Jinja2 2.10.The from_string function is prone to Server Side Template Injection (SSTI) where it takes the "source" parameter as a template object, renders it, and then returns it. The attacker can exploit it with {{INJECTION COMMANDS}} in a URI.The from_string function is prone to Server Side Template Injection (SSTI) where it takes the "source" parameter as a template object, renders it, and then returns it. The attacker can exploit it with {{INJECTION COMMANDS}} in a URI.</p>
</recommendation>

<references>
<li>References:<br>
<a href="https://access.redhat.com/security/cve/cve-2019-8341">
CVE-2019-8341 - Red Hat Customer Portal</a>.

</li>
<!-- LocalWords: CWE -->
</references>

</qhelp>
42 changes: 42 additions & 0 deletions python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @name SSTI in Jinja2 (CVE-2019-8341)
* @description Server-side template injection occurs when user-controlled input is embedded into a server-side template, allowing users to inject template directives.
* @kind SSTI
*/

import python
import semmle.python.security.Paths
import semmle.python.web.HttpRequest
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted


predicate isObjectAttribute(Expr e, string objectName, string methodName) {
e.(Call).getFunc().(Attribute).getName().toString() = methodName
and e.(Call).getFunc().(Attribute).getObject().toString() = objectName
}


class SSTIStringEvaluationNode extends TaintSink {
override string toString() { result = "SSTI in Jinja2 (CVE-2019-8341)" }
SSTIStringEvaluationNode() {
exists(Expr e | isObjectAttribute(e, "Jinja2", "from_string"))
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}


class SSTIConfiguration extends TaintTracking::Configuration {
SSTIConfiguration() { this = "Server Side Template Injection" }

override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}

override predicate isSink(TaintTracking::Sink sink) { sink instanceof SSTIStringEvaluationNode }
}

from SSTIConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "$@ flows to here and is interpreted as code.", src.getSource(), "A user-provided value"

19 changes: 19 additions & 0 deletions python/ql/src/experimental/CVE-2019-8341/examples/example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from flask import Flask, request
from jinja2 import Environment

app = Flask(__name__)
Jinja2 = Environment()

@app.route("/page")
def page():

name = request.values.get('name')
output = Jinja2.from_string('Hello ' + name + '!').render()
return output

if __name__ == "__main__":
app.run(host='0.0.0.0', port=8001)

#RUN:/bin/python3 example.py
#POC:http://example_ip:port/page?name=%7B%25%20for%20c%20in%20%5B%5D.__class__.__base__.__subclasses__()%20%25%7D%0A%7B%25%20if%20c.__name__%20%3D%3D%20%27catch_warnings%27%20%25%7D%0A%20%20%7B%25%20for%20b%20in%20c.__init__.__globals__.values()%20%25%7D%0A%20%20%7B%25%20if%20b.__class__%20%3D%3D%20%7B%7D.__class__%20%25%7D%0A%20%20%20%20%7B%25%20if%20%27eval%27%20in%20b.keys()%20%25%7D%0A%20%20%20%20%20%20%7B%7B%20b%5B%27eval%27%5D(%27__import__(%22os%22).popen(%22id%22).read()%27)%20%7D%7D%0A%20%20%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endif%20%25%7D%0A%20%20%7B%25%20endfor%20%25%7D%0A%7B%25%20endif%20%25%7D%0A%7B%25%20endfor%20%25%7D

12 changes: 12 additions & 0 deletions python/ql/src/experimental/test/examples/ssti_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from flask import Flask,render_template,request,render_template_string

app=Flask(__name__)

@app.route('/',methods=['GET','POST'])
def test():
code = request.args.get('test')
html = '<html>%s</html>'%code
return render_template_string(html)

#poc:http://example_ip:port/?test={{%27%27.__class__.__mro__[2].__subclasses__()[40](%27/etc/passwd%27).read()}}

28 changes: 28 additions & 0 deletions python/ql/src/experimental/test/ssti.qhelp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!DOCTYPE qhelp PUBLIC
"-//Semmle//qhelp//EN"
"qhelp.dtd">
<qhelp>

<overview>
<p>Server Side Template Injection is possible when an attacker injects template directive as user input that can execute arbitrary code on the server.</p>
</overview>

<recommendation>
<p>Server-side template injection occurs when user-controlled input is embedded into a server-side template, allowing users to inject template directives.</p>
<p>This allows an attacker to inject malicious template directives and possibly execute arbitrary code on the affected server. </p>
<p>Some popular and widely used template engines are Smarty, Twig, Jinja2, FreeMarker, Velocity.</p>
</recommendation>

<references>
<li>References:<br>
<a href="https://portswigger.net/research/server-side-template-injection">
Server-Side Template Injection</a>.
<br>
<a href="https://www.cnblogs.com/Rasang/p/12181654.html">
Eeample</a>.

</li>
<!-- LocalWords: CWE -->
</references>

</qhelp>
36 changes: 36 additions & 0 deletions python/ql/src/experimental/test/ssti.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @name Server-Side Template Injection.
* @description Server-side template injection occurs when user-controlled input is embedded into a server-side template, allowing users to inject template directives.
* @kind ssti
*/

import python
import semmle.python.security.Paths
import semmle.python.web.HttpRequest
import semmle.python.dataflow.TaintTracking
import semmle.python.security.strings.Untrusted


class StringEvaluationNode extends TaintSink {
override string toString() { result = "ssti in Jinja2" }
StringEvaluationNode() {
exists(Call call ,Name name | call.getFunc() = name and name.getId() = "render_template_string")
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}


class CodeInjectionConfiguration extends TaintTracking::Configuration {
CodeInjectionConfiguration() { this = "Code injection configuration" }

override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}

override predicate isSink(TaintTracking::Sink sink) { sink instanceof StringEvaluationNode }
}

from CodeInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "$@ flows to here and is interpreted as code.", src.getSource(),
"A user-provided value"