OWASP Web Top 10 — A03

Server-Side Template Injection

Server-Side Template Injection (SSTI) is a critical vulnerability that occurs when user input is embedded into template engines without proper sanitization. It allows attackers to inject malicious template directives, potentially leading to remote code execution and full server compromise.

What Is Server-Side Template Injection?

Server-Side Template Injection (SSTI) is a vulnerability that arises when an application embeds user input into template engines in an unsafe manner. Template engines like Jinja2, FreeMarker, Thymeleaf, Twig, and Handlebars are designed to dynamically generate HTML, emails, or other text-based output by combining static templates with dynamic data. When user-controlled data is inserted directly into template expressions rather than being passed as template context data, attackers can inject their own template directives that execute on the server.

Unlike Cross-Site Scripting (XSS) which executes in the browser, SSTI executes on the server side, giving attackers far more power. Template engines often provide access to powerful server-side objects and methods, including file system access, network capabilities, and system command execution. This makes SSTI one of the most dangerous injection vulnerabilities, as it can quickly escalate from information disclosure to complete remote code execution.

SSTI falls under the Injection category (A03) in the OWASP Top 10 2021. While it is less common than SQL Injection or XSS, it is significantly more dangerous when present. The vulnerability is particularly prevalent in custom CMS platforms, email templating systems, reporting engines, and any application that allows users to customize templates or messages. Modern web frameworks have made SSTI less common through safe templating defaults, but legacy applications and misconfigurations continue to be exploited in the wild.

How It Works

1
Application accepts user input for template generation

The application presents a feature that allows users to customize messages, generate reports, or modify display templates. This could be a greeting message editor, email template customization, PDF report generation, or any feature where user input influences template rendering.

2
User input is embedded directly into a template string

Instead of passing user input as context data to the template engine, the application concatenates it directly into the template string itself. For example, instead of rendering a template with a variable, the code builds a template string like: "Hello " + userInput and then compiles and renders this string as a template.

3
Attacker crafts template injection payload

The attacker submits specially crafted input containing template engine syntax. For example, in Jinja2 (Python): 49, in FreeMarker (Java): ${7*7}, or in Thymeleaf: ${T(java.lang.Runtime).getRuntime().exec('whoami')}. The payload is designed to be interpreted as template code rather than plain text.

4
Template engine processes the malicious directive

When the template is rendered, the template engine parses and evaluates the injected payload as if it were legitimate template code. Simple payloads like 49 will output 49, confirming the vulnerability. More advanced payloads access internal objects, call methods, and execute arbitrary code on the server.

5
Attacker achieves remote code execution

Once template injection is confirmed, attackers escalate by accessing runtime objects, reflection APIs, or built-in functions that allow command execution. In Python-based Jinja2, attackers can access __class__, __mro__, and __subclasses__() to reach dangerous classes. In Java FreeMarker, the freemarker.template.utility.Execute class can run system commands, leading to full server compromise.

Vulnerable Code Example

Vulnerable — Java / Spring Boot (FreeMarker)
@RestController
public class GreetingController {

    @Autowired
    private Configuration freemarkerConfig;

    @GetMapping("/greet")
    public String greet(@RequestParam String name) throws Exception {
        // VULNERABLE: User input is embedded directly
        // into the template string
        String templateString = "<html><body>"
            + "<h1>Hello " + name + "!</h1>"
            + "</body></html>";

        Template template = new Template("greeting",
            new StringReader(templateString),
            freemarkerConfig);

        StringWriter writer = new StringWriter();
        template.process(new HashMap<>(), writer);

        return writer.toString();
    }
}

// An attacker can execute arbitrary code by submitting:
// name=${T(java.lang.Runtime).getRuntime().exec('whoami')}
//
// Or in FreeMarker syntax:
// name=${"freemarker.template.utility.Execute"?new()("whoami")}
//
// This executes system commands on the server.

Secure Code Example

Secure — Java / Spring Boot (Data Binding)
@RestController
public class GreetingController {

    @Autowired
    private Configuration freemarkerConfig;

    @GetMapping("/greet")
    public String greet(@RequestParam String name) throws Exception {
        // SECURE: Use a static template with data binding
        // User input is passed as template context data,
        // not embedded in the template string itself
        String templateString = "<html><body>"
            + "<h1>Hello ${userName}!</h1>"
            + "</body></html>";

        Template template = new Template("greeting",
            new StringReader(templateString),
            freemarkerConfig);

        // User input is passed as data, not code
        Map<String, Object> model = new HashMap<>();
        model.put("userName", name);

        StringWriter writer = new StringWriter();
        template.process(model, writer);

        return writer.toString();
    }
}

// With proper data binding, if an attacker submits:
// name=${7*7}
// The output will be: Hello ${7*7}!
// The template syntax is treated as literal text,
// not executable code.

Types of SSTI

Expression-based SSTI

The most common type, where attackers inject template expressions that are evaluated by the template engine. This includes simple arithmetic operations for detection (49 in Jinja2, ${7*7} in FreeMarker) and property access to leak data. Attackers can navigate object graphs, access environment variables, read configuration files, and enumerate internal application state. While not immediately leading to code execution, this type provides valuable reconnaissance information and can expose sensitive data like API keys, database credentials, or internal URLs.

Code Evaluation SSTI

The most dangerous type, where template engines provide direct code evaluation capabilities. Some template engines like Velocity, Pebble, or older versions of FreeMarker allow arbitrary code execution through built-in functions. For example, FreeMarker's Execute class, Jinja2's access to Python runtime via __import__, or Velocity's ability to instantiate Java classes. This type allows attackers to execute system commands, read/write files, establish reverse shells, and completely compromise the server without needing to escape sandboxes.

Sandbox Escape SSTI

Occurs when template engines implement security sandboxes that restrict dangerous operations, but attackers find ways to bypass these restrictions. For example, Jinja2 implements a sandbox mode that blocks dangerous attributes and modules, but attackers can use Python's introspection (__class__.__mro__[2].__subclasses__()) to access unrestricted classes. Twig's sandbox can be escaped through filter chains, and Smarty's security policies can be bypassed through double-encoding. These attacks require deep knowledge of the template engine's internals and the underlying programming language's reflection capabilities.

Impact

Server-Side Template Injection is one of the most severe web vulnerabilities due to its potential for complete server compromise. The impact depends on the template engine, server configuration, and application privileges.

Remote code execution

The most critical impact. Attackers can execute arbitrary system commands on the server, install backdoors, establish persistent access through cron jobs or startup scripts, pivot to internal networks, and deploy ransomware or cryptocurrency miners. In many cases, SSTI provides a direct path to complete server takeover without requiring additional vulnerabilities or privilege escalation.

Sensitive data disclosure

Even when code execution is not immediately achievable, SSTI allows attackers to access configuration files, environment variables, application source code, and internal object state. This can expose database credentials, API keys, encryption secrets, and proprietary business logic. Attackers can also enumerate file systems, read logs containing sensitive data, and extract information about internal infrastructure.

File system manipulation

Many template engines provide file I/O capabilities that attackers can abuse through SSTI. This allows reading arbitrary files (including /etc/passwd, SSH keys, application configuration), writing malicious files (web shells, modified application code), deleting critical files to cause denial of service, and modifying templates to create persistent XSS vulnerabilities that affect all users.

Server-side request forgery (SSRF)

Template engines often provide HTTP request capabilities or network access that can be exploited for SSRF attacks. Attackers can probe internal networks, access cloud metadata services (like AWS EC2 metadata at 169.254.169.254 to steal IAM credentials), bypass IP-based access controls, and interact with internal services that are not exposed to the internet. Combined with RCE, SSRF through SSTI can lead to complete cloud infrastructure compromise.

Prevention Checklist

Never embed user input directly in template strings

The fundamental rule: always pass user input as template context data, never concatenate it into the template string itself. Use the template engine's data binding mechanisms. If you must dynamically generate templates, ensure user input goes through the template engine's variable system (${var}) rather than being treated as template code.

Use logic-less template engines when possible

Consider using simpler, logic-less template engines like Mustache or Handlebars that don't support code execution. While they may be less feature-rich, they significantly reduce attack surface. If you need complex logic, move it to your application code rather than the template layer. For user-customizable templates, provide a limited set of safe placeholders rather than full template access.

Enable sandbox mode and security restrictions

Most modern template engines provide security features. Enable Jinja2's sandbox mode, use Twig's sandbox extension, configure FreeMarker's template loader restrictions, and disable dangerous functions like Execute. Keep template engines updated, as security vulnerabilities are regularly discovered and patched. Review the security configuration guides for your specific template engine.

Implement strict input validation and sanitization

While not a primary defense, input validation adds a defense-in-depth layer. Reject input containing template syntax characters like ${}, , <%, #{}. Use allowlists to restrict input to expected formats. Be aware that blacklisting template syntax is insufficient, as attackers can use encoding, Unicode tricks, or alternative syntax to bypass filters.

Run template rendering in restricted environments

Apply the principle of least privilege. Run your application with minimal OS-level permissions, use containerization (Docker) to limit file system and network access, employ security policies like AppArmor or SELinux, and restrict outbound network connections. This won't prevent SSTI, but limits the damage attackers can do post-exploitation.

Perform security testing and code review

Manually audit all code that generates or renders templates, paying special attention to any user-controllable input. Use static analysis tools (SAST) configured to detect template injection patterns. Include SSTI payloads in your penetration testing and security scanning. Tools like Burp Suite, OWASP ZAP, and tplmap can detect SSTI vulnerabilities during testing. Remember that automated tools may miss context-specific vulnerabilities, so manual code review is essential.

Real-World Examples

2016

Uber Template Injection

Security researcher Orange Tsai discovered a critical SSTI vulnerability in Uber's internal tools that allowed rendering arbitrary Jinja2 templates. By exploiting Python's introspection capabilities, the researcher achieved remote code execution on Uber's servers. This could have allowed reading source code, accessing internal databases, and pivoting to other internal systems. Uber awarded a bug bounty for the discovery.

2018

SaltStack CVE-2020-11651

While primarily an authentication bypass, this vulnerability in the SaltStack configuration management platform was often combined with Jinja2 SSTI to achieve code execution. Attackers could inject malicious Jinja2 templates into Salt states, which would then be executed on all managed servers. This combination allowed attackers to compromise entire data centers running thousands of servers simultaneously, making it one of the most impactful server management vulnerabilities discovered.

2019

Shopify SSTI via Email Templates

Researcher Mahmoud Gamal discovered an SSTI vulnerability in Shopify's custom email template feature. Merchants could create email templates using Liquid (Shopify's template language), but insufficient sanitization allowed injection of malicious template code. While Liquid is relatively restricted, the vulnerability still allowed accessing sensitive shop data, customer information, and potentially modifying orders. Shopify quickly patched the issue and awarded a substantial bug bounty.

2020

Spring Boot Thymeleaf RCE

Multiple Spring Boot applications were found vulnerable to SSTI through improper use of Thymeleaf's fragment expressions. When user input was used in fragment names without validation, attackers could inject ${T(java.lang.Runtime).getRuntime().exec('command')} to execute arbitrary system commands. This affected numerous enterprise applications using Spring Boot's default template configuration, highlighting how framework misuse can lead to critical vulnerabilities even in well-designed systems.

Ready to Test Your Knowledge?

Put what you have learned into practice. Try identifying and fixing Server-Side Template Injection vulnerabilities in our interactive coding challenges, or explore more security guides to deepen your understanding.