What Is SSTI (Server-Side Template Injection)?
A server-side template injection attack (SSTI) is when a threat actor exploits a template’s native syntax and injects malicious payloads into the template. The compromised template is then executed server-side. A template engine generates a web page by combining a fixed template with volatile data.
Attackers use the server-side template injection technique to directly insert user input into templates, allowing them to introduce arbitrary directives that alter the template engine’s behavior. It can allow threat actors to gain full control of a targeted server.
What Is the Impact of Server-Side Template Injection?
Server-side template injection vulnerabilities could expose a website to various attacks, depending on the type of template engine and the way it works with the application. In rare cases, these vulnerabilities do not pose a real security risk. However, in most cases the impact of an SSTI attack is severe.
In the most severe cases, attackers can remotely execute code and fully take over the backend servers. They can then use these servers to launch additional attacks on internal infrastructure. Even when attackers cannot execute code remotely, they might be able to read sensitive data or files stored on the server. Attackers with read access can still use SSTI as the basis for many other attacks.
How to Detect SSTI Vulnerabilities
Plain Text vs. Code-Based SSTI Detection
An SSTI vulnerability may occur in two contexts requiring distinct detection techniques.
Plain text SSTI detection
Testers can detect SSTI vulnerabilities in a plain text context by using template expressions as the payload used by different template engines. The engines can then view the server’s HTTP responses in an error message.
Some examples of common template expressions include:
Code-based SSTI detection
Testers can test for SSTI vulnerabilities in a code context by constructing payloads to retrieve an error or blank server response.
For example, a tester can insert a GET parameter in an HTTP request using the personal_greeting variable in the template statement. The payload server will respond with a blank “Hello”:
personal_greeting=username Hello user01
Next, the tester can inject an HTML tag to break out of the statement after using the following payload:
personal_greeting=username}}<tag> Hello user01 <tag>
When the tester identifies the injection point, it is possible to identify the template engine based on the relevant template expressions.
The malicious or malformed payload used in the input determines whether the tester will identify an SSTI. The server might display an error message or flag the exception.
For example, the tester can detect a vulnerability by injecting the following payload in the user input parameter:
POST /some-endpoint HTTP/1.1 Host: victim-website.com parameter=${{<%[%'"}}%\.
If there is a vulnerability, the server will respond with an error message reflecting the template engine.
Identifying the Template Engine
Once testers detect the template injection, they must identify which template engine is used. This step may be simple, with the tester submitting an invalid syntax that induces the template engine to identify itself in the resulting error message. In some cases, this technique is insufficient because something suppresses the error messages. It is also unsuited to automation.
Alternatively, testers can automate the identification step with a decision tree in Burp Suite. The tree can map language-specific payloads, with red and green arrows representing failed and successful responses. Sometimes, a single payload might have several successful responses, such as the {{7*’7’}} probe resulting in 7777777 in Jinja2 and 49 in Twig.
Exploiting the Vulnerability
After identifying the template injection and underlying template engine, the testers must try reading the documentation. This step is important for identifying zero-day exploits and verifying if an attacker could exploit the SSTI vulnerability.
The main points of interest are:
- The “For Template Authors” section—this section addresses basic syntax.
- The “Security Considerations” section—the application’s developer likely skipped this section, which could provide useful security insights.
- Built-in feature list—this should cover the built-in functions, methods, variables, and filters.
- Extension and plugin list—this might include external features enabled by default.
If there is no apparent exploit at this stage, the tester should then check the extent of the environment’s accessibility. This step is likely to reveal default objects that the template engine provides and application-specific objects that the developer passed to the template. Some template systems might expose a namespace or “self” object that allows the developer to list the methods and attributes of the object.
The objects supplied by the developer are more likely to have sensitive data and can vary between multiple templates in the application. Testers should therefore apply this process separately to each template.
By now, the tester should clearly understand the available attack surface. The final step is to apply conventional security auditing methods to review every function and identify vulnerabilities that an attacker could exploit. Testers should approach this step as part of the overall application security. Certain functions might allow attackers to exploit features specific to the application.
Server-Side Template Injection Attack Prevention
It is usually not acceptable to completely remove the template engine from a server, because it supports application changes without disrupting ongoing operations. Therefore, it is important to learn how to use templates securely while preventing SSTI.
Restrict ‘Edit’ Access
Templates that are open to everyone are easy targets for hackers. Therefore, it is best to restrict access by applying access rules to template files.
It is critical to prevent templates from being modified by anyone other than developers and administrators. Moreover, templates used in production should only be accessed by the specific administrator in charge of the server or application—not by developers. This can reduce the risk of supply chain attacks and insider threats.
Sanitize Inputs
Sterilization can significantly reduce the risk of an SSTI attack. A template should check all expected inputs for disruptive elements. If possible, the template should use an allowlist approach to only allow inputs that are expected by users, and deny everything else. A common way to do this is to use regular expressions to create a list of allowed input patterns.
While input sanitization is important, it is limited, and attackers have many creative ways of circumventing it and creating a malicious input that fits the allowed pattern. Therefore this solution cannot guarantee complete protection.
Sandboxing
Sandboxing is a more secure approach than sanitization. It is a precautionary approach that creates a safe, isolated environment for users. Within this environment, there are no dangerous functions or modules, and access to other data is restricted. This means that if a vulnerability is found or a user attempts an attack, the damage is limited.
Sandboxing a template is a highly effective measure but is difficult to implement. In addition, attackers can sidestep sandboxing by taking advantage of misconfigurations, or attempting privilege escalation to break outside the sandboxed environment.
Logic-less Templates
Perhaps the most secure approach is to use logic-less templates. These are templates that completely separate code interpretation from visual representation. An example of a logic-less template engine is Mustache. Logicless templates use control flow statements to ensure that controls are data-driven by default, enabling integration with application logic. In this setup, the possibility of remote code execution becomes quite remote.
Application Security with Imperva
Imperva provides Runtime Application Self-Protection (RASP), which can prevent real-time attack detection and prevention from your application runtime environment wherever your applications go. RASP stops external attacks and injections and reduces your vulnerability backlog.
Beyond RASP, Imperva provides comprehensive protection for applications, APIs, and microservices:
Web Application Firewall – Prevent attacks with world-class analysis of web traffic to your applications.
API Security – Automated API protection ensures your API endpoints are protected as they are published, shielding your applications from exploitation.
Advanced Bot Protection – Prevent business logic attacks from all access points – websites, mobile apps and APIs. Gain seamless visibility and control over bot traffic to stop online fraud through account takeover or competitive price scraping.
DDoS Protection – Block attack traffic at the edge to ensure business continuity with guaranteed uptime and no performance impact. Secure your on premises or cloud-based assets – whether you’re hosted in AWS, Microsoft Azure, or Google Public Cloud.
Attack Analytics – Ensures complete visibility with machine learning and domain expertise across the application security stack to reveal patterns in the noise and detect application attacks, enabling you to isolate and prevent attack campaigns.
Client-Side Protection – Gain visibility and control over third-party JavaScript code to reduce the risk of supply chain fraud, prevent data breaches, and client-side attacks.