Hello Hackers, I Hope you guys are doing well and hunting lots of bugs and dollars !
Server Side Template Injection (SSTI), is a very impactful and most ignored bug, generally, we kind of miss it while performing security assessments. In this article, we are going to cover the basics of this vulnerability, its impact, and a brief methodology to hunt this.
The very first thing we should understand about template, template engines, and rendering functions, so let's understand them one by one.
Template engines allow dynamic content to be injected into static templates using placeholders and expressions. Some Common template engines are jinja2(python), Twig (PHP), Freemarker (Java), and Mustache (JavaScript), etc.
So the idea is to display dynamic content with the static content. You must have observed that whenever any application take your user name as input and display you ads or any personalised stuff.
Have you ever wondered how it works, have they written your name in code, the answer is no, because they are using templates, that dynamically generate personalised output.
Had lunch, {{ name }}!So it is like they are taking user input and passing it to the template and rendering the content. Taking untrusted user input is always risky when not handled properly.
The complete game is here about Dynamic Content Rendering, where user-provided data is inserted into a template like HTML, JSON, etc at runtime.
Here Template engines are middleware between user-provided data and what users view. The backend processing takes place at template engines only depending on the application logic. Different languages support different template engines like Handlebars (JavaScript), Twig (PHP), and Jinja2 (Python) etc.
When user input is directly passed into a template engine without proper sanitisation or validation, which allows malicious users to inject and execute arbitrary template code in the server-side templating engine.
from flask import Flask, request, render_template_stringapp = Flask(__name__)
@app.route('/')
def index():
user_input = request.args.get('name') # Takes user input from the URL.
template = f"Hello, {user_input}!" # Passing user input into template.
return render_template_string(template) # function used to dynamically rendering.
if __name__ == '__main__':
app.run(debug=True)
The above-mentioned is a vulnerable code snippet of flask + jinja2, which takes user input from the URL and directly passes it into the template without any sanitisation or validation.
This can be easily exploited, as the name parameter in the URL is user-controllable. An arbitrary user can inject a malicious SSTI payload that can be http://xcheater.medium.com/?name={{ 5 * 5}} and the result will be 25. This can be further escalated and may lead to remote code execution (RCE).
Additionally, In some cases, you might not immediately see the output of the injected payload, but there can be the possibility of blind or second order SSTI vulnerability.
There are multiple scenarios where this vulnerability can be chained which can lead to Data Disclosure, Denial of services, Cross-site Scripting, Remote Code Execution, etc.
Use this Template Injection table to get some very specific Universal Error-based polyglot.
3. Identify the template engine used in the targeted application. You can use the Wappalyzer extension or look for application-generated errors through fuzzing. Additionally, analyzing JavaScript files can sometimes reveal the template engine or its configuration, which may be exposed.
Based on template engine you can craft payload or use similar payloads to chain this vulnerability to make it more impactful. Also, You can use a hit-and-trial method.
Here are some common template engines based on popular technologies:
Now that we’ve sufficient information and also we’ve identified SSTI, now we need to escalate it to prove an impact. Just mathematical calculation will not justify the impact.
Most of us, always try directly escalate it to RCE, which is not ideal way because your payload will make lots of noise. So instead of directly jumping to spraying payloads for RCE, we should approach it step by step. Additionally when spraying basic payloads to achieve any impact can work on some sites, but in most of the scenario, your exploitation payload may not work, so its better to be little creative use below bypasses or similar.
In most scenarios where you will able to achieve code execution. If code executions is not possible, still there can be other attacks which can be chained like information disclosure or denial of service.
Lets see whats the ideal way !!
Step 1: Identifying the engine with below techniques.
Syntax Fingerprinting
{{7*'7'}} → Jinja2 returns '7777777' (string), Twig returns 49 (number).
${"a"+"b"} → Mako/Freemarker returns "ab".
<%= 7*7 %> → ERB (Ruby) returns 49. Leak Built-in Objects
{{config}} → Flask config (Python).
{{_self}} → Twig’s template scope (PHP).
<%= ENV.inspect %> → Ruby environment variables. Step 2: Once we identified, we can explore what’s accessible in the engine like built-in objects, variables, configs, request objects and environment variabes, etc. Lets look into some famous template engines.
Jinja2 (Python/Flask):
{{ config }} → Leak Flask/Django configuration (secrets, debug mode).
{{ request }} → Access Flask request object (cookies, headers).
{{ self }} → Template namespace (may expose internal variables).
{{ url_for.__globals__ }} → Access global variables (e.g., `os`, `sys`).Twig (PHP):
{{ _self }} → Template scope (exposes environment and filters).
{{ _self.env }} → Environment variables (e.g., database credentials).
{{ _self.loadTemplate() }} → Load arbitrary templates (file read?).Freemarker (Java):
${.data_model} → Dump the data model (variables passed to the template).
${object?api.class} → Access Java object methods (reflection).Sometimes you’ll find SSTI but won’t be able to get RCE, and that’s totally normal. It can happen for a bunch of reasons :-
But just because RCE isn’t possible doesn’t mean the bug is useless. We can still use SSTI to read sensitive files, perform internal SSRF attacks, or even mess with the application’s business logic. So its better to put some impact by chaining the vulnerability.
Secure Template Engine Usage
Input Validation and Sanitization
Least Privilege and Runtime Hardening
I hope this is informative to you, and if you have any doubts or suggestions, reach out to me over Twitter; I’ll be happy to assist or learn from you.
Happy Hacking !
Twitter handle :- https://twitter.com/Xch_eater