SSTI: Server Side Template injection vulnerability

Server-side template injection (SSTI) vulnerabilities tend to be less researched than other types of flaws. However, their impact is significant and often leads to remote code execution (RCE). They are therefore flaws that should not be underestimated.

In what contexts do SSTI vulnerabilities occur? How to detect them and how to prevent them?

This is what we will see in this article.

What is a server-side template injection (SSTI) vulnerability?

Some web applications use template engines to separate the visual presentation (HTML, CSS…) from the application logic (PHP, Python…). These engines allow the creation of template files in the application. Templates are a mixture of fixed data (layout) and dynamic data (variables). When the application is used, the template engine will replace the variables contained in a template with values and will transform the template into a web page (HTML) and then send it to the client.

A server-side template injection (SSTI) vulnerability occurs when user data is embedded directly in a template and then interpreted by the template engine. This allows attackers to inject arbitrary directives to manipulate the template engine.

In what situations does this vulnerability occur?

The source of the problem is that the data transmitted by users is interpreted directly by the template engine (as dynamic data), instead of being integrated as fixed data.

This type of vulnerability is most often found on sites that want to offer advanced customisation features such as wikis, blogs, marketing applications or CMS.

An SSTI vulnerability usually occurs when a template is modified to obtain a customised result for a specific need and users have access to the template modification functions.

A concrete example might be an SSTI vulnerability in a web application that provides users with a way to manage the templates of emails that are sent by the application. A basic template is defined for the email but the user can modify certain fields. The template engine then generates the final email. If the user injects expressions into the template and they are evaluated by the template engine, the functionality will be vulnerable. The user could then attempt to take control of the server.

Note that an SSTI flaw does not systematically lead to an RCE and a server compromise, it depends on the situation (how the application uses the template, the location of the injection, etc.).

It may also allow other attacks such as reading files on the server, information leakage or privilege escalation. In rare cases, the vulnerability is not exploitable. 

How to find an SSTI vulnerability?

SSTI flaws are less often discovered, as they are less known and less researched than Cross Site Scripting(XSS) flaws for example. Their identification takes place in two parts:

  • A first phase of detection of the vulnerability.
  • A second phase to identify the template engine used.

1. Detecting the vulnerability

The first step is to determine whether an application is vulnerable.

An effective approach is to fuzz the target in all data fields with a payload containing special characters often used by template engines.

For example: ${{<%[%'"}}@{%\.#{<%=

If an error message is returned in the server responses, this indicates that the application is potentially vulnerable. Analysing the error message could help determine which template engine is being used.

Example of an error message with the Tornado template engine in python when injecting the “{{” payload into a vulnerable parameter:

Internal Server Error

No handlers could be found for logger "tornado.application" Traceback (most recent call last): File "<string>", line 15, in <module> File "/usr/lib/python2.7/dist-packages/tornado/template.py", line 317, in __init__ "exec", dont_inherit=True) File "<string>.generated.py", line 4 _tt_tmp = user.nickname{{ # <string>:1 ^ SyntaxError: invalid syntax

Template injections can occur in two different contexts:

Plaintext context: Many template engines allow content to be entered directly into HTML tags or using the template engine syntax. This content will be interpreted on the server side before the server returns an HTTP response. In this case, it is recommended to send mathematical formulas in the tested fields (depending on the template engine, the syntax may change). Here are some examples:

  • {{7*7}}
  • ${7*7}
  • ${{7*7}}
  • <%= 7*7 %>
  • #{7*7}

If one of these formulas is interpreted, the server response will contain “49”. This means that the mathematical operation was interpreted in a “Plaintext” context and that an SSTI vulnerability is present.

Context code: When user inputs are directly inserted into an expression evaluated by the template engine. In this case, the idea is to modify the relevant parameter while trying to generate an error.

For more details on contexts, you can refer to this article.

2. Identify the template engine used

This is a key step that will enable the exploitation phase to be carried out.

Many template engines exist. Among the most popular, we can mention Twig for PHP, Freemarker for Java, ERB for Ruby, Jinja for Python…

Most template engines use very different syntax from HTML code to avoid confusion.

The identification method is similar to what was mentioned in the “Detecting the vulnerability” section: successfully generating an error message often makes it possible to identify the template engine used.

However, when this method fails (for example if the error messages are not displayed in the server responses), it is possible to proceed by elimination by testing simple payloads (arithmetic operation) with different syntaxes corresponding to different template engines. The analysis of the server responses should determine whether or not the operation transmitted was interpreted. Depending on the syntax used, this will give an indication of the template engine used.

You can find here a list of payloads according to templating languages.

Note: If you know the programming language used to develop the application, this can help you determine the template engines used and allow you to target the tests for the identification phase.

Once you know the template engine used, you can start to exploit the flaw to achieve the highest possible impact. The first step is to read the documentation associated with the template engine to determine what actions exist. Next, you need to determine which objects and functions you have access to on the vulnerable application. Once you have a general idea of what can be done, all that is left to do is to try to exploit the vulnerability by trying to get a command execution, read files, etc.

Example of a payload that can execute arbitrary code when Freemarker is used:

${"freemarker.template.utility.Execute"?new()("id")}

How to prevent SSTI flaws?

Considering the potential severity of the SSTI vulnerability, one might ask why use a template engine. It has advantages and in particular, it facilitates modifications. Indeed, a design change can be made without touching the code flow and autonomously by the designers/integrators. The changes are independent of the code and logical processing. There is no need to modify the working of the application.

Similarly, developers can make changes to the code and logical elements without affecting the display of data or breaking the graphic interface.

This makes it easier to maintain the project. However, good configuration is essential.

The best way to avoid SSTI vulnerabilities is to never allow users to modify or create templates. However, when there is a business need, solutions exist:

Sanitization

This involves detecting and removing potentially malicious content before using it in the template. It is therefore necessary to efficiently analyse the data transmitted by users. To do this, various means are possible (use of regex, white lists of authorised expressions, etc.).

However, this solution is not 100% reliable. A configuration error can leave your environment vulnerable.

Sandboxing

The aim is to provide a closed environment, where risky modules and features are disabled. If user-supplied data is interpreted, it cannot provide access to other features or data.

Unfortunately, sandbox environments are difficult to set up and can be bypassed by misconfiguration or oversights. It is a bit of a cat-and-mouse game between defenders and attackers.

Logic less template

Logic-less engine templates exist, which separate visual rendering and code interpretation as much as possible. Mustache is one of the most popular.

It has no explicit control flow statements, all control being data-driven, and it is impossible to integrate application logic into Mustache templates.

The RCE attacks are thus no longer possible and this greatly reduces the risk of attacks.

Conclusion

The vast majority of SSTI vulnerabilities that we encounter during penetration tests on a web platform lead to arbitrary code execution and server compromise. This is therefore a vulnerability that should not be overlooked. Recommendations can be complex to implement and testing is required to confirm their effectiveness.