Skip to content

Server-Side Template Injection (SSTI): detecting and fixing Jinja2, Twig, and Freemarker flaws

A technical walkthrough of Server-Side Template Injection (SSTI): syntax differences, sandbox escapes, and escalating template injection to Remote Code Execution (RCE).

By BugSnaps Security Research · · 8 min read

Server-Side Template Injection (SSTI) occurs when user-supplied input is embedded directly into a template file and evaluated by a server-side template engine (such as Jinja2 in Python, Twig in PHP, or Freemarker in Java) rather than being passed as a separate template context variable.

The path from template evaluation to Remote Code Execution

Template engines often possess powerful reflection and object-introspection capabilities. When an attacker injects template syntax, they can traverse class hierarchies to reach underlying operating system execution primitives:

  • Polyglot mathematical probes: injecting expressions like `{{7*7}}` or `${7*7}` to identify whether template evaluation occurs and determine the underlying engine.
  • Jinja2 class traversal: climbing Python object hierarchies via `{{ ''.__class__.__mro__[1].__subclasses__() }}` to locate `subprocess.Popen` and execute arbitrary shell commands.
  • Twig and Smarty escapes: invoking built-in filters and execution helpers like `{{_self.env.registerUndefinedFilterCallback('exec')}}{{_self.env.getFilter('id')}}`.

Remediation: context passing vs string concatenation

Never concatenate user input directly into template strings. Always pass user input as context parameters to pre-compiled static template files: `render_template('invoice.html', customer_name=user_input)` rather than `render_template_string('Hello ' + user_input)`.

Treat template injection with maximum severity. In almost every major engine, SSTI can be reliably escalated into full remote command execution.

Run a real pentest on your app - free.

Sign in, prove you own the domain, and MyPentest maps and tests it. No credit card.