flawopen.com/Vulnerabilities/Server-Side Template Injection
Imagine a mail-order company printing letters: 'Dear {{ name }}, thank you for your order.' Normally, the printer inserts customer names like 'Alice'. But a hacker types '{{ 7 * 7 }}' into the name box, and the printer outputs '49'. Seeing that the printing machine calculates math, the hacker submits a command asking the printer to open the vault door and dump the cash. In SSTI, the template engine doesn't just print text—it executes attacker code inside the web server's runtime.
{{ 7*7 }}, ${7*7}, #{7*7}Developers accidentally build template source strings dynamically by concatenating user input (e.g. render_template_string(f'Hello {username}')) instead of passing the input as a template parameter dictionary (e.g. render_template('hello.html', name=username)). The template engine evaluates user input as code, allowing attackers to access Python's object hierarchy (__mro__) to instantiate subprocess.Popen.
# VULNERABLE: Rendering dynamically compiled template string
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/greet')
def greet():
name = request.args.get('name', 'Guest')
# Attacker passes: {{ cycler.__init__.__globals__.os.popen('id').read() }}
template = f"Welcome to the portal, {name}!
"
return render_template_string(template) # RCE on web server!
# HARDENED: Static template file with untrusted input passed as variable
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/greet')
def greet_safe():
name = request.args.get('name', 'Guest')
# Static template file treats 'name' strictly as a data string, never executable syntax
return render_template('greet.html', name=name)
# In greet.html:
# Welcome to the portal, {{ name }}!
{{ 7*7 }} and observing 49 in response.:{{ ''.__class__.__mro__[1].__subclasses__() }}.subprocess.Popen or calls os.system to execute arbitrary shell commands.:SandboxedEnvironment) when users are allowed to edit templates.