mirror of
https://github.com/github/codeql.git
synced 2025-12-20 02:44:30 +01:00
Python : Add query to detect Server Side Template Injection
This commit is contained in:
32
python/ql/src/experimental/CWE-074/TemplateInjection.ql
Normal file
32
python/ql/src/experimental/CWE-074/TemplateInjection.ql
Normal 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"
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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`)
|
||||
*/
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -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
python/ql/src/experimental/semmle/python/templates/Jinja.qll
Normal file
49
python/ql/src/experimental/semmle/python/templates/Jinja.qll
Normal 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 }
|
||||
}
|
||||
27
python/ql/src/experimental/semmle/python/templates/Mako.qll
Normal file
27
python/ql/src/experimental/semmle/python/templates/Mako.qll
Normal 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 }
|
||||
}
|
||||
@@ -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 { }
|
||||
13
python/ql/src/experimental/semmle/python/templates/Ssti.qll
Normal file
13
python/ql/src/experimental/semmle/python/templates/Ssti.qll
Normal 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
|
||||
@@ -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 }
|
||||
}
|
||||
Reference in New Issue
Block a user