All articlesAttack Techniques

How to Test for Server-Side Template Injection (SSTI)

September 19, 2026·9 min read·Nautillo Pro Security Team

A math expression that evaluates itself in a web page is one of the quietest-looking bugs in application security — and one of the most dangerous. Server-Side Template Injection turns a templating engine meant to render text into a code execution engine. Here's how to detect it with a single harmless probe, and how far the escalation path actually goes.

How a templating feature becomes remote code execution

Template engines like Jinja2, Freemarker, Twig, Velocity, and Thymeleaf exist to separate presentation from logic — a template says "insert the user's name here," and the engine fills it in safely. SSTI happens when user input is concatenated into the template string itself, rather than passed as a safely-escaped variable. At that point, the engine doesn't just display the input — it evaluates it as template syntax.

This distinction is why SSTI is scored so much higher than reflected XSS despite looking similar at first glance: XSS executes in the victim's browser with the victim's privileges. SSTI executes on your server, and most template engines expose enough of the underlying language (Python, Java, JavaScript) that "template evaluation" escalates directly to full remote code execution.

The detection probe: a math expression the engine can't resist

The standard SSTI test is deliberately simple and safe — a numeric expression that means nothing to a human but everything to a template engine. If the input is reflected as literal text, nothing happens. If it's evaluated, the math resolves:

EngineSyntaxVulnerable response
Jinja2 / Django / Twig{{7*7}}49
Freemarker / Velocity / JSP EL${7*7}49
Thymeleaf[[7*7]]49
ERB / JSP / ASP<%= 7*7 %>49
Spring Expression Language#{7*7}49

A field that echoes back the literal string {{7*7}} is safe. A field that echoes back 49 is running your input as code.

HTTP proof-of-concept

1. Detection — the harmless math probe

POST /api/render-preview HTTP/1.1
Content-Type: application/json
{ "template_name": "{{7*7}}" }

HTTP/1.1 200 OK
{ "preview": "49" }
# → input evaluated as Jinja2/Twig template syntax = confirmed SSTI

2. Engine fingerprinting via error disclosure

When the math probe doesn't fire cleanly, a malformed template expression often leaks the engine name in a stack trace instead:

POST /api/render-preview HTTP/1.1
{ "template_name": "{{7*'7'}}" }

HTTP/1.1 500 Internal Server Error
jinja2.exceptions.TemplateSyntaxError: unexpected char ''' at 7
# → engine confirmed as Jinja2 even though the math probe errored

3. What full exploitation looks like (do not run this)

Once an engine is confirmed, published payloads escalate straight to arbitrary code execution. This is a real, documented Jinja2 sandbox-escape pattern — shown here only so you understand the actual ceiling of this vulnerability, not as something to run against a system you don't explicitly own and have authorization to test destructively:

{{ ''.__class__.__mro__[1].__subclasses__()[414]('cat /etc/passwd',shell=True,stdout=-1).communicate() }}
# → Jinja2's object introspection reaches Python's subprocess module,
#   turning "render a template" into "run any shell command"

Safe testing methodology

Detection and full exploitation are two very different risk levels. A responsible test stops at proof, not impact:

Start with the math probe, nothing else

{{7*7}} → 49 is complete, unambiguous proof that input reaches the template evaluator. No further payload is needed to confirm the vulnerability class.

Use error messages to fingerprint, don't force them

If a probe throws a 500 error, read the stack trace for the engine name rather than trying increasingly aggressive payloads to force a clean evaluation.

Never run the RCE-class payloads outside an authorized, destructive-testing engagement

Reading /etc/passwd or spawning a shell moves from 'confirmed vulnerability' to 'active exploitation' — treat that step as requiring its own explicit authorization, separate from a standard scan.

Report the confirmed engine and detection payload, not a working exploit chain

Engineering teams need to know the vulnerability exists and which template call site to fix — they don't need a copy-pasteable RCE payload sitting in a ticket.

Where to look: the highest-risk input surfaces

Email and notification template customization

High

Features letting users or admins customize email subject lines, notification text, or invoice templates often render that input directly through the app's template engine.

PDF and document generation

High

Server-side document generators frequently interpolate user-supplied fields (names, addresses, custom notes) into templates before rendering to PDF.

"Preview" or "live render" features

High

Any endpoint that renders user-provided markup or template snippets for a live preview — CMS page builders, report designers — is a direct SSTI candidate by design.

URL parameters reflected into rendered pages

Medium

Search terms, error pages, or 404 handlers that echo the requested value back through a template rather than a plain string.

Multi-tenant white-labeling / theming

Medium

Systems that let tenant admins customize page templates are especially high-value: a template injection there can potentially affect other tenants sharing the render infrastructure.

CVSS 3.1 scoring

Confirmed SSTI with a working code-execution path, scope changed (impact extends beyond the vulnerable component to the underlying server):

CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

AV:N / AC:L — Remotely exploitable, low complexity

PR:L — Low privileges (authenticated user reaching the render feature)

S:C — Scope changed: exploiting the template engine grants control beyond the component itself, over the underlying server

C:H / I:H / A:H — Full confidentiality, integrity, and availability impact once RCE is achieved

Score: 9.0 Critical

A template-engine error disclosure with no confirmed evaluation (fingerprinting only, no {{7*7}} confirmation) scores much lower — typically CVSS 5.3 Medium under A05:2021 Security Misconfiguration — since it discloses attack-surface information rather than confirming code execution.

Test your app for SSTI safely

Nautillo Pro runs scope-safe SSTI detection — math-probe evaluation and engine fingerprinting only, never RCE-class exploitation. Every confirmed finding includes the exact request, response, and CVSS score.

Related articles