Python : Add query to detect Server Side Template Injection

This commit is contained in:
Porcupiney Hairs
2020-05-04 01:56:37 +05:30
parent 2e5af67626
commit 49df4169cf
64 changed files with 918 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
/**
* @name Server Side Template Injection
* @description Using user-controlled data to create a template can cause security issues.
* @kind path-problem
* @problem.severity error
* @precision high
* @id py/template-injection
* @tags security
* external/cwe/cwe-074
*/
import python
import semmle.python.security.Paths
/* Sources */
import semmle.python.web.HttpRequest
/* Sinks */
import experimental.semmle.python.templates.Ssti
class TemplateInjectionConfiguration extends TaintTracking::Configuration {
TemplateInjectionConfiguration() { this = "Template injection configuration" }
override predicate isSource(TaintTracking::Source source) {
source instanceof HttpRequestTaintSource
}
override predicate isSink(TaintTracking::Sink sink) { sink instanceof SSTISink }
}
from TemplateInjectionConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "This Template depends on $@.", src.getSource(),
"a user-provided value"

View File

@@ -0,0 +1,27 @@
/** Provides classes which model the `airspeed` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `airspeed.Template` */
ClassValue theAirspeedTemplateClass() { result = Value::named("airspeed.Template") }
/**
* Sink representing the `airspeed.Template` class instantiation argument.
*
* import airspeed
* temp = airspeed.Template(`"sink"`)
*/
class AirspeedTemplateSink extends SSTISink {
override string toString() { result = "argument to airspeed.Template()" }
AirspeedTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theAirspeedTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,46 @@
/** Provides classes which model the `bottle` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `bottle.SimpleTemplate` */
ClassValue theBottleSimpleTemplateClass() { result = Value::named("bottle.SimpleTemplate") }
/**
* Sink representing the `bottle.SimpleTemplate` class instantiation argument.
*
* from bottle import SimpleTemplate
* template = SimpleTemplate(`sink`)
*/
class BottleSimpleTemplateSink extends SSTISink {
override string toString() { result = "argument to bottle.SimpleTemplate()" }
BottleSimpleTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theBottleSimpleTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* Sink representing the `bottle.template` function call argument.
*
* from bottle import template
* tmp = template(`sink`)
*/
class BottleTemplateSink extends SSTISink {
override string toString() { result = "argument to bottle.template()" }
BottleTemplateSink() {
exists(CallNode call |
call.getFunction() = theBottleModule().attr("template").getAReference() and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,27 @@
/** Provides classes which model the `Chameleon` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `chameleon.PageTemplate` */
ClassValue theChameleonPageTemplateClass() { result = Value::named("chameleon.PageTemplate") }
/**
* Sink representing the `chameleon.PageTemplate` class instantiation argument.
*
* from chameleon import PageTemplate
* template = PageTemplate(`sink`)
*/
class ChameleonTemplateSink extends SSTISink {
override string toString() { result = "argument to Chameleon.PageTemplate()" }
ChameleonTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theChameleonPageTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,37 @@
/** Provides classes which model the `Cheetah3` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `Cheetah.Template.Template` */
ClassValue theCheetahTemplateClass() { result = Value::named("Cheetah.Template.Template") }
/**
* Sink representing the instantiation argument of any class which derives from
* the `Cheetah.Template.Template` class .
*
* from Cheetah.Template import Template
* class Template3(Template):
* title = 'Hello World Example!'
* contents = 'Hello World!'
* t3 = Template3("sink")
*
* This should also detect cases of the following type :
*
* from Cheetah.Template import Template
* t3 = Template("sink")
*/
class CheetahTemplateInstantiationSink extends SSTISink {
override string toString() { result = "argument to Cheetah.Template.Template()" }
CheetahTemplateInstantiationSink() {
exists(CallNode call, ClassValue cv |
cv.getASuperType() = theCheetahTemplateClass() and
call.getFunction().pointsTo(cv) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,36 @@
/** Provides classes which model the `chevron` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the Value representing `chevron.render` function */
Value theChevronRenderFunc() { result = Value::named("chevron.render") }
/**
* Sink representing the `chevron.render` function call argument.
*
* import chevron
* tmp = chevron.render(`sink`,{ 'key' : 'value' })
*/
class ChevronRenderSink extends SSTISink {
override string toString() { result = "argument to chevron.render()" }
ChevronRenderSink() {
exists(CallNode call |
call.getFunction() = theChevronRenderFunc().getAReference() and
call.getArg(0) = this
)
// TODO: this should also detect :
// import chevron
// args = {
// 'template': 'sink',
// 'data': {
// 'mustache': 'World'
// }
// }
// chevron.render(**args)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,36 @@
/** Provides classes which model the `DjangoTemplate` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
ClassValue theDjangoTemplateClass() { result = Value::named("django.template.Template") }
/**
* Sink representng `django.template.Template` class instantiation argument.
*
* from django.template import Template
* template = Template(`sink`)
*/
class DjangoTemplateTemplateSink extends SSTISink {
override string toString() { result = "argument to Django.template()" }
DjangoTemplateTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theDjangoTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
// TODO
/**
* Sinks representng the django.template.Template class instantiation.
*
* from django.template import engines
*
* django_engine = engines["django"]
* template = django_engine.from_string(`sink`)
*/

View File

@@ -0,0 +1,26 @@
/** Provides classes which model templates in the`flask` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
Value theFlaskRenderTemplateClass() { result = Value::named("flask.render_template_string") }
/**
* Sink representng `flask.render_template_string` function call argument.
*
* from flask import render_template_string
* render_template_string(`sink`)
*/
class FlaskTemplateSink extends SSTISink {
override string toString() { result = "argument to flask.render_template_string()" }
FlaskTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theFlaskRenderTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,51 @@
/** Provides classes which model the `Genshi` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `Genshi.template.TextTemplate` */
ClassValue theGenshiTextTemplateClass() { result = Value::named("genshi.template.TextTemplate") }
/** returns the ClassValue representing `Genshi.template.MarkupTemplate` */
ClassValue theGenshiMarkupTemplateClass() {
result = Value::named("genshi.template.MarkupTemplate")
}
/**
* Sink representing the `genshi.template.TextTemplate` class instantiation argument.
*
* from genshi.template import TextTemplate
* tmpl = TextTemplate('sink')
*/
class GenshiTextTemplateSink extends SSTISink {
override string toString() { result = "argument to genshi.template.TextTemplate()" }
GenshiTextTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theGenshiTextTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* Sink representing the `genshi.template.MarkupTemplate` class instantiation argument.
*
* from genshi.template import MarkupTemplate
* tmpl = MarkupTemplate('sink')
*/
class GenshiMarkupTemplateSink extends SSTISink {
override string toString() { result = "argument to genshi.template.MarkupTemplate()" }
GenshiMarkupTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theGenshiMarkupTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,49 @@
/** Provides classes which model the `Jinja2` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `jinja2.Template` */
ClassValue theJinja2TemplateClass() { result = Value::named("jinja2.Template") }
/** returns the ClassValue representing `jinja2.Template` */
Value theJinja2FromStringValue() { result = Value::named("jinja2.from_string") }
/**
* Sink representing the `jinja2.Template` class instantiation argument.
*
* from jinja2 import Template
* template = Template(`sink`)
*/
class Jinja2TemplateSink extends SSTISink {
override string toString() { result = "argument to Jinja2.template()" }
Jinja2TemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theJinja2TemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}
/**
* Sink representing the `jinja2.Template` class instantiation argument.
*
* from jinja2 import Template
* template = Template(`sink`)
*/
class Jinja2FromStringSink extends SSTISink {
override string toString() { result = "argument to Jinja2.from_string()" }
Jinja2FromStringSink() {
exists(CallNode call |
call.getFunction().pointsTo(theJinja2FromStringValue()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,27 @@
/** Provides classes which model the `Mako` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `mako.template.Template` */
ClassValue theMakoTemplateClass() { result = Value::named("mako.template.Template") }
/**
* Sink representing the `mako.template.Template` class instantiation argument.
*
* from mako.template import Template
* mytemplate = Template("hello world!")
*/
class MakoTemplateSink extends SSTISink {
override string toString() { result = "argument to mako.template.Template()" }
MakoTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theMakoTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}

View File

@@ -0,0 +1,7 @@
import semmle.python.dataflow.TaintTracking
/**
* A generic taint sink that is vulnerable to template inclusions.
* The `temp` in `Jinja2.Template(temp)` and similar.
*/
abstract class SSTISink extends TaintSink { }

View File

@@ -0,0 +1,13 @@
/** Imports all files which model potential SSTI sinks */
import experimental.semmle.python.templates.Airspeed
import experimental.semmle.python.templates.Bottle
import experimental.semmle.python.templates.Chameleon
import experimental.semmle.python.templates.Cheetah
import experimental.semmle.python.templates.Chevron
import experimental.semmle.python.templates.DjangoTemplate
import experimental.semmle.python.templates.FlaskTemplate
import experimental.semmle.python.templates.Genshi
import experimental.semmle.python.templates.Jinja
import experimental.semmle.python.templates.Mako
import experimental.semmle.python.templates.TRender

View File

@@ -0,0 +1,27 @@
/** Provides classes which model the `TRender` package. */
import python
import semmle.python.web.HttpRequest
import experimental.semmle.python.templates.SSTISink
/** returns the ClassValue representing `trender.TRender` */
ClassValue theTRenderTemplateClass() { result = Value::named("trender.TRender") }
/**
* Sink representing the `trender.TRender` class instantiation argument.
*
* from trender import TRender
* template = TRender(`sink`)
*/
class TRenderTemplateSink extends SSTISink {
override string toString() { result = "argument to trender.TRender()" }
TRenderTemplateSink() {
exists(CallNode call |
call.getFunction().pointsTo(theTRenderTemplateClass()) and
call.getArg(0) = this
)
}
override predicate sinks(TaintKind kind) { kind instanceof ExternalStringKind }
}