Server-side templates provide an easy method of managing the dynamic generation of HTML code. But they can also fall victim to server-side template injection (SSTI). Take a look at the basics of server-side web templates, and how to detect, identify and mitigate SSTI in web applications.
Server-side templates allow developers to pre-populate a web page with custom user data directly on the server. After all, it is often faster to make all the requests within a server than to make extra browser-to-server roundtrips for them. This is different from client-side templating, where browsers tend to load a template that affects the overall end-user experience.
Feature-rich web applications often embed user input in web templates to offer flexible functionality and shortcuts. This creates a vulnerability easily mistaken for cross-site scripting (XXS).
How Do Server-Side Templates Work?
The most common approach for web frameworks to generate HTML dynamically is using templates. A template contains the static parts of the desired HTML output, as well as some special syntax describing how it will insert dynamic content.
The template system isn’t safe from untrusted template authors. For example, a site shouldn’t allow its users to provide their own templates, since template authors can perform XSS attacks and access properties of template variables that may contain sensitive information.
The template engines then process template files created by the developers, which helps to populate dynamic data into web pages. The template engine creates the HTML output response when an HTTP request comes in.
Some of the server-side template engines that are most often used are Smarty, Mako, Twig and Jinja2.
- For PHP: Smarty, Twig
- Java: Velocity, Freemaker
- Python: Jinja, Mako, Tornado
Jinja, also known as Jinja2, is a popular Python template engine written as a self-contained open-source project.
For example:
Hello {{user.name}}
These provide Django templates that adopt the model, view and templates framework, instead of the more popular model, view and controller framework, as part of a larger web framework. This can make them difficult to reuse in projects outside their coupled library.
Server-Side Template Injection
So, how does an SSTI vulnerability in a web application work? The attacker injects malicious input or invalid syntax into a template to execute commands on the server-side.
This vulnerability occurs when the template engine contains embedded invalid user input, which can lead to a remote code execution (RCE) attack.
Example:
Template = ‘Username:’ + USER_INPUT
render(template)
Here, the USER_INPUT is part of the template. This allows the user to input a username or any input parameter of a web application. Because the templates can accept arbitrary code, a user might be able to input some differential calculus like:
Username: {{ 7 * 7 }}
Username 49
In the above example, the server-side template engine processes {{7*7}} to give an output of 49. This shows that the web application is vulnerable to SSTI.
The below screenshots shows an example of how to add an SSTI payload in an input parameter ‘name,’ which results in an output of 49 that is evaluated server-side.
A malicious user can even enter a malicious code to compromise the system to gain complete remote access:
Ex - bio: {{ malicious code() }}
The following chart shows the path for those finding and exploiting SSTI.
How to Detect Server-Side Template Injection
Defenders can find or detect SSTI via automated scripts or through manual methods. Two of the manual methods are:
- Plain text detection method
- Code context detection method
Detect Plaintext:
To detect SSTI in a plain text context, the tester can use some of the common template expressions in the form of a payload that is used by various template engines. They can then observe the server’s HTTP responses in the error message.
Here are some common template expression examples:
=${7*3}
={{7*3}}
=<%= 7*3 %>
Detect Code Context
To test the code context, the tester needs to construct a payload to fetch a blank or error response from the server.
In the below request, the tester inserts an HTTP GET parameter into the variable ‘personal_greeting’ in a template statement.
personal_greeting=username
Hello user01
The payload server responds with a blank ‘Hello:’
personal_greeting=username
Hello
The next step is breaking out of the template statement and injecting the HTML tag after using the below payload.
personal_greeting=username}}<tag>
Hello user01 <tag>
How to Identify Server-Side Template Engines
After the tester detects the point of injection, they can identify the template engine on the basis of various mathematical or template expressions.
Identifying the SSTI depends upon the malformed/malicious payload crafted to be used in the user input. As a result, the server may display the error message or flag an exception.
Example 1:
POST /some-endpoint HTTP/1.1
Host: victim-website.com
parameter=${{<%[%'"}}%\.
To detect the vulnerability, the tested can inject the above shown polyglot payload (the sequence of special characters) at the user input parameter. If the vulnerability exists, the server’s response with an error message can reflect the underlying template engine.
The below screenshot shows an error output from an application that indicates the kind of template engine used.
The probe {{7*’7′}} would result in 49 in Twig, 7777777 in Jinja2, and neither if no template language is in use.
Twig:
Custom_email={{7*’7’}}
49
Jinja:
Custom_email={{7*’7’}}
7777777
How to Remediate SSTI
With SSTI, once the threat actors identify the template engine, they can also gain further control of the server by using the remote code execution exploit. The untrusted users can identify the objects, methods and properties. Such exposure can further lead to information disclosure about application passwords, application programming interface keys and more.
Therefore, an SSTI vulnerability targeting the template engines and allowing untrusted users to edit templates introduces an array of serious risks. It is important for developers to take remediation steps that depend upon the different template engines in place.
The following remediation steps are abstract and can be applied to any template engine.
Sanitization
Templates should not be created from user-controlled input. User input should be passed to the template using template parameters. Sanitize the input before passing it into the templates by removing unwanted and risky characters before parsing the data. This minimizes the vulnerabilities for any malicious probing of your templates.
Sandboxing
If allowing certain risky characters is a business requirement to render certain attributes of a template, assume that malicious code execution is inevitable. Then, sandboxing the template environment in a docker container is probably a safer option. With this option, you can use docker security to craft a secure environment that limits any malicious activities.
With these things in mind, you’ll be able to look out for and remediate SSTI vulnerabilities while still taking advantage of what server-side templates have to offer.
Software Engineer, PTC IBM