Template Injection [SSTI]

1. Introduction:

Server-side template injection is when an attacker is able to use native template syntax to inject a malicious payload into a template, which is then executed server-side.

Template engines are designed to generate web pages by combining fixed templates with volatile data. This allows attackers to inject arbitrary template directives in order to manipulate the template engine, often enabling them to take complete control of the server.

You can read more about this type of vulnerability, from an attacker's perspective, here.

2. Typical vulnerable code:

There are several libraries/ frameworks that make use of templates, here are a few: Jinja, Flask, Mako, Twig. The following code snippet showcases Flask's vulnerability:

from flask import Flask,request,render_template_string 
from urllib.parse import unquote

app = Flask(__name__)

@app.route("/")
def main_page():
    return "Hey there, this is my cool weeb site."

@app.errorhandler(404) 
def page_not_found(error): 
    url = unquote(request.url) 
    return render_template_string("<h1>URL %s not found</h1><br/>" % url), 404 
    
if __name__ == '__main__': 
    app.run(debug = False, host = '0.0.0.0')

The given input is being rendered and reflected into the response. This is easily mistaken for a simple XSS vulnerability, but it's easy to difference if you try set mathematical operations within the template expression: {{7*7}}.

Showcasing that {{7*7}} gets rendered as 49 proves the point that our application is vulnerable to SSTI. We will however not go into more details regarding further exploitation, however you can refer to this awesome guide for that.

3. Mitigations:

If user-supplied templates are a business requirement, how should they be implemented? The lowest risk approach is to simply use a trivial template engine such as Mustache, or Python's Template. Separating the logic from rendering as much as possible can greatly reduce your exposure to the most dangerous template-based attacks. Another, complementary approach is to concede that arbitrary code execution is inevitable(regardless of filtering/ whitelisting/ blacklisting)and sandbox it inside a locked-down Docker container.

4. Takeaways:

Not using user-supplied templates saves you of this possible exploitation. Should you really have to use template rendering however, an alternative can be sandboxing the actual rendering in a custom way, though you can think of the major drawbacks here.

Last updated