From 7d8c5d3857484282dc86790bd9c28e0f032b55bb Mon Sep 17 00:00:00 2001 From: xyer <1449246384@qq.com> Date: Fri, 3 Jul 2020 13:47:37 +0800 Subject: [PATCH 1/2] ssti --- .../test/examples/ssti_example.py | 12 +++++++ python/ql/src/experimental/test/ssti.qhelp | 28 +++++++++++++++ python/ql/src/experimental/test/ssti.ql | 36 +++++++++++++++++++ 3 files changed, 76 insertions(+) create mode 100644 python/ql/src/experimental/test/examples/ssti_example.py create mode 100644 python/ql/src/experimental/test/ssti.qhelp create mode 100644 python/ql/src/experimental/test/ssti.ql diff --git a/python/ql/src/experimental/test/examples/ssti_example.py b/python/ql/src/experimental/test/examples/ssti_example.py new file mode 100644 index 000000000000..6560159fb725 --- /dev/null +++ b/python/ql/src/experimental/test/examples/ssti_example.py @@ -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 = '%s'%code + return render_template_string(html) + +#poc:http://example_ip:port/?test={{%27%27.__class__.__mro__[2].__subclasses__()[40](%27/etc/passwd%27).read()}} + diff --git a/python/ql/src/experimental/test/ssti.qhelp b/python/ql/src/experimental/test/ssti.qhelp new file mode 100644 index 000000000000..1a695f8c3f25 --- /dev/null +++ b/python/ql/src/experimental/test/ssti.qhelp @@ -0,0 +1,28 @@ + + + + +

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

+
+ + +

Server-side template injection occurs when user-controlled input is embedded into a server-side template, allowing users to inject template directives.

+

This allows an attacker to inject malicious template directives and possibly execute arbitrary code on the affected server.

+

Some popular and widely used template engines are Smarty, Twig, Jinja2, FreeMarker, Velocity.

+
+ + +
  • References:
    + +Server-Side Template Injection. +
    + +Eeample. + +
  • + +
    + +
    diff --git a/python/ql/src/experimental/test/ssti.ql b/python/ql/src/experimental/test/ssti.ql new file mode 100644 index 000000000000..d1f0494a4a13 --- /dev/null +++ b/python/ql/src/experimental/test/ssti.ql @@ -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" From 773a1ec98b6a66ce475d1a0e4cdacc7747931fe5 Mon Sep 17 00:00:00 2001 From: xyer <1449246384@qq.com> Date: Wed, 8 Jul 2020 11:43:40 +0800 Subject: [PATCH 2/2] CVE-2019-8341 --- .../CVE-2019-8341/cve-2019-8341.qhelp | 23 ++++++++++ .../CVE-2019-8341/cve-2019-8341.ql | 42 +++++++++++++++++++ .../CVE-2019-8341/examples/example.py | 19 +++++++++ 3 files changed, 84 insertions(+) create mode 100644 python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.qhelp create mode 100644 python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.ql create mode 100644 python/ql/src/experimental/CVE-2019-8341/examples/example.py diff --git a/python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.qhelp b/python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.qhelp new file mode 100644 index 000000000000..c7ebc9f045db --- /dev/null +++ b/python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.qhelp @@ -0,0 +1,23 @@ + + + + +

    Server Side Template Injection in Jinja2 2.10

    +
    + + +

    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.

    +
    + + +
  • References:
    + +CVE-2019-8341 - Red Hat Customer Portal. + +
  • + +
    + +
    diff --git a/python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.ql b/python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.ql new file mode 100644 index 000000000000..00e92f9d27f8 --- /dev/null +++ b/python/ql/src/experimental/CVE-2019-8341/cve-2019-8341.ql @@ -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" + diff --git a/python/ql/src/experimental/CVE-2019-8341/examples/example.py b/python/ql/src/experimental/CVE-2019-8341/examples/example.py new file mode 100644 index 000000000000..771c24904818 --- /dev/null +++ b/python/ql/src/experimental/CVE-2019-8341/examples/example.py @@ -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 +