Server-side Template Injection

ID

python.server_side_template_injection

Severity

critical

Resource

Injection

Language

Python

Tags

CWE:1336, NIST.SP.800-53, OWASP:2021:A3, PCI-DSS:6.5.1

Description

Template engines allow dynamic generation of content (HTML, JSON, email messages…​) merged with data. Unsafely embedding untrusted input in templates executed enables Server-Side Template Injection (SSTI), when template rendering is done at the server-side.

Rationale

Template injection can happen through a design flaw, or if the application allows users to upload templates without proper validation. Such flaws can be found in blogs, wikis, or content management tools, to name a few.

If an attacker is able to send parts of the template, or select which template to use, the rendered content could be influenced by the attacker, and the attack payload could include code execution or sensitive information leakage.

Some template engines allow a URL or path to select the template to use. If the attacker can choose URL/path to use, we are one step below remote code execution, command injection, or web defacement, depending on how the attack is executed

An application vulnerable to template injection can open the door to arbitrary code execution, if combined with local file injection techniques.

The detector will report a vulnerability if user-controlled input is concatenated in template source or the reference to a template resource without neutralization.

from flask import Flask, request
from jinja2 import Template

app = Flask(__name__)

@app.route('/')
def index():
    # Unsafe: Directly using user input in templates
    user_input = request.args.get('user_input', '')
    template = Template("Result: " + user_input) # FLAW
    return template.render()

Remediation

If possible, never let untrusted input be concatenated with other text in a template, or used in the location (path or URL) of the template resource that the template engine uses. Allowing users to upload or edit a template is also dangerous. Passing user input into templates as "context parameters" is normally a safe alternative. If the content is rendered in a browser, and the evaluated expressions from user input are not properly escaped, cross-site scripting may be possible.

If users can choose the template, use a whitelist. This means the user can choose which template to apply from a fixed list of allowed template resources.

The impact of SSTI attacks could be reduced by using a logic-less engine like Mustache or Handlebars. If this is not possible, review the template engine documentation for security advice.

Note: Some template engines offer a 'sandboxed' mode for this purpose, but such sandboxes typically fail against a determined attacker who finds ways to escape the sandbox. Do not trust such sandboxing capabilities.

See the following remediated Python script:

from flask import Flask, request
from jinja2 import Template

app = Flask(__name__)

@app.route('/')
def index():
    # Secure: Using context variables
    user_input = request.args.get('user_input', '')
    template = Template("Result: {{ input }}")
    return template.render(input=user_input)

In this example, the request data is passed through context variables, which is safe.

Configuration

The detector has the following configurable parameters:

  • sources, that indicates the source kinds to check.

  • neutralizations, that indicates the neutralization kinds to check.

Unless you need to change the default behavior, you typically do not need to configure this detector.

References