Manual Testing for Server-Side Template Injection (SSTI)

Server-Side Template Injection (SSTI) is a critical vulnerability that allows an attacker to inject malicious template code into a web application, potentially leading to remote code execution, data exfiltration, or unauthorized access. This post is a technical deep dive into manually testing for SSTI during web application penetration tests. We’ll explore the underlying concepts, examine common payloads, and work through practical examples using different templating engines.


1. Understanding SSTI

What is a Template Engine?

Modern web applications often use template engines to render dynamic content. Engines such as Jinja2 (Python), Twig (PHP), and others allow developers to embed logic in HTML. SSTI arises when user input is unsafely passed to these template engines, enabling the execution of unintended template code.

Why is SSTI Dangerous?

An SSTI vulnerability can expose sensitive internal functions and objects. For instance, in Python’s Jinja2, an attacker might traverse the class hierarchy to access functions that interact with the operating system, potentially leading to full system compromise.


2. Manual Testing Methodology

Step 1: Identify Potential Injection Points

Start by analyzing the application for inputs that might be processed by a templating engine. Common candidates include:

Step 2: Insert Basic Payloads

Insert simple payloads that trigger expression evaluation. A classic example is:

{{7*7}}

If the application is vulnerable, the rendered page might display “49” where you inserted the payload.

Step 3: Analyze the Output

If the payload is reflected in the response and computed (e.g., “49” appears), you likely have an SSTI vulnerability. Note if the payload appears unaltered or escapes HTML characters, as these behaviors can indicate different levels of risk or filtering.

Step 4: Escalate with More Advanced Payloads

Once a basic injection is confirmed, test with payloads that attempt to access server internals. For example, with Jinja2 you might try:

{{''.__class__.__mro__[2].__subclasses__()}}

This payload accesses the Python object hierarchy to list classes, which can help you identify exploitable objects such as file handlers or OS command executors.


3. Practical Examples

Example 1: Testing with cURL

Assume you have a vulnerable endpoint http://example.com/search that directly renders user input within a template. Use cURL to test:

curl -G "http://example.com/search" --data-urlencode "query={{7*7}}"

Expected Output:
If vulnerable, the rendered page should display “49” in the location where the query parameter is reflected.

Example 2: Manual Testing in Burp Suite

  1. Intercept a Request:
    Capture a request where the input might be rendered in the HTML response.

  2. Modify the Request:
    Replace the input value with a payload such as {{7*7}} and forward the request.

  3. Analyze the Response:
    Look for computed output. If “49” is visible, you have a confirmed SSTI injection point.

Example 3: Advanced Payload Exploration in Jinja2

After confirming SSTI, you may attempt to enumerate sensitive classes to facilitate further exploitation. Use the following payload:

{{ ''.__class__.__mro__[2].__subclasses__() }}

Interpretation:

Note: The output can be extensive. In practical testing, use filters to search for classes that enable file I/O or command execution. For example, in Python, you might search for the <class 'os._wrap_close'> or similar entries that can lead to further exploitation.

Example 4: Evaluating Payload Impact with Python

Sometimes, it’s helpful to simulate payloads in a controlled environment. Below is a Python snippet to mimic a simplified template engine scenario:

from jinja2 import Template

# Simulated vulnerable template rendering
def render_user_input(user_input):
    template = Template("User input: " + user_input)
    return template.render()

# Test with a basic payload
payload = "{{7*7}}"
result = render_user_input(payload)
print("Rendered Output:", result)

Expected Outcome:
If the application is vulnerable, the output should be:

Rendered Output: User input: 49

This controlled test helps you understand how the templating engine interprets the payload.


4. Deep Dive: Techniques for Further Exploitation

Enumerating the Class Hierarchy

After confirming SSTI, the next step is often to navigate through the template engine’s internal objects. This can involve:

Bypassing Input Sanitization

Some applications may sanitize or encode output. Techniques include:

Combining Payloads

Once you have a basic understanding of the available objects, combine payloads to execute system commands. For example, in Jinja2, if you can access the os module indirectly, you might execute:

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

This payload:

Disclaimer: Always ensure that testing like this is conducted on systems you have explicit permission to test.


5. Mitigation and Best Practices

While the focus here is on identifying and exploiting SSTI vulnerabilities, it’s equally important to understand how to prevent them:


6. Conclusion

Server-Side Template Injection remains a potent vulnerability in web applications that rely on dynamic content rendering. Through careful manual testing, penetration testers can uncover these vulnerabilities using basic payloads, advanced payload exploration, and controlled environment testing. By understanding the mechanics behind SSTI and employing rigorous testing methodologies, security professionals can better protect applications from exploitation.

This guide has walked through the manual testing process, provided practical examples with cURL, Burp Suite, and Python, and highlighted both the risks and remediation strategies. As with all security testing, ensure you have proper authorization and adhere to legal and ethical guidelines during your assessments.

Happy testing, and stay secure!

Ready to see how Numorian can help your business?

Contact us today to learn more about our services and how we can support your business.