Skip to content

Commit

Permalink
Python : Add query to detect Server Side Template Injection
Browse files Browse the repository at this point in the history
  • Loading branch information
porcupineyhairs committed Jul 20, 2020
1 parent 499e349 commit 2513950
Show file tree
Hide file tree
Showing 64 changed files with 924 additions and 6 deletions.
32 changes: 32 additions & 0 deletions python/ql/src/experimental/CWE-074/TemplateInjection.ql
Original file line number Diff line number Diff line change
@@ -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"
27 changes: 27 additions & 0 deletions python/ql/src/experimental/semmle/python/templates/Airspeed.qll
Original file line number Diff line number Diff line change
@@ -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 }
}
46 changes: 46 additions & 0 deletions python/ql/src/experimental/semmle/python/templates/Bottle.qll
Original file line number Diff line number Diff line change
@@ -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 }
}
27 changes: 27 additions & 0 deletions python/ql/src/experimental/semmle/python/templates/Chameleon.qll
Original file line number Diff line number Diff line change
@@ -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 }
}
37 changes: 37 additions & 0 deletions python/ql/src/experimental/semmle/python/templates/Cheetah.qll
Original file line number Diff line number Diff line change
@@ -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 }
}
36 changes: 36 additions & 0 deletions python/ql/src/experimental/semmle/python/templates/Chevron.qll
Original file line number Diff line number Diff line change
@@ -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 }
}
Original file line number Diff line number Diff line change
@@ -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`)
*/
Original file line number Diff line number Diff line change
@@ -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 }
}
51 changes: 51 additions & 0 deletions python/ql/src/experimental/semmle/python/templates/Genshi.qll
Original file line number Diff line number Diff line change
@@ -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 }
}
49 changes: 49 additions & 0 deletions python/ql/src/experimental/semmle/python/templates/Jinja.qll
Original file line number Diff line number Diff line change
@@ -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 }
}
Loading

0 comments on commit 2513950

Please sign in to comment.