Detailed Explanation of Server-Side Template Injection (SSTI) Vulnerability
1. Vulnerability Description
Server-Side Template Injection (SSTI) is a security vulnerability that occurs on the server side of web applications. When an application uses a template engine (such as Jinja2, Freemarker, Velocity, etc.) to dynamically generate content like HTML or emails, if user input is not strictly filtered, attackers can inject malicious template code into the templates, which is then executed on the server. This may lead to Remote Code Execution (RCE), sensitive information disclosure, or complete server compromise.
2. Vulnerability Principle
- Role of Template Engines: Template engines combine static template files with dynamic data to generate the final HTML page. For example, a template might contain
{{ username }}, which is replaced with the actual username during rendering. - Formation of Injection Points: When user input (such as URL parameters, form data) is directly concatenated into the template instead of being passed as pure data, attackers can insert template syntax (e.g.,
{{ 7*7 }}). If the server calculates and returns 49, it indicates the presence of SSTI. - Escalation of Harm: Template engines typically support complex logic (such as conditional judgments, loops, object calls), which attackers can exploit to execute system commands or access sensitive objects.
3. Vulnerability Detection Steps
-
Step 1: Identify the Template Engine
Attempt to submit simple template syntax at user input points (e.g., URL parameters):- Jinja2 (Python):
{{ 7*7 }}→ If "49" is returned, it may be Jinja2. - Twig (PHP):
{{ 7*7 }}or{{ 7*'7' }}→ Observe the return value. - Freemarker (Java):
${7*7}→ If "49" is returned, it may be Freemarker.
Note: Syntax varies greatly across engines, requiring multiple attempts.
- Jinja2 (Python):
-
Step 2: Confirm the Injection Point
If you see the number 49 after inputting{{ 7*7 }}, or see the string "abc" after inputting{{ 'abc' }}, it indicates that user input is being executed as template code. -
Step 3: Error Message Analysis
Submit invalid syntax (e.g.,{{ {{ }}) and observe whether error messages from the template engine are returned (e.g., Jinja2's "TemplateSyntaxError") to further confirm the engine type.
4. Detailed Vulnerability Exploitation (Using Jinja2 as an Example)
-
Step 1: Access Python Built-in Classes
In Jinja2 templates, all Python objects can access base classes through class inheritance. For example:
{{ ''.__class__ }}→ Returns the class of the string object (<class 'str'>).
{{ ''.__class__.__mro__ }}→ View the class inheritance chain (including the base classobject). -
Step 2: Obtain Dangerous Subclasses
Through subclasses of the base classobject, locate classes that can execute commands (e.g.,os._wrap_close):
{{ ''.__class__.__mro__[1].__subclasses__() }}→ Lists all subclasses; search for index numbers like<class 'subprocess.Popen'>. -
Step 3: Execute System Commands
Assuming the 100th item in the subclass list isPopen:
{{ ''.__class__.__mro__[1].__subclasses__()[100]('whoami', stdout=-1).communicate() }}→ Executes thewhoamicommand and returns the result.
Note: The actual index needs to be adjusted according to the target environment.
5. Defense Measures
- Input Filtering: Strictly validate user input using a whitelist approach, prohibiting inputs containing template syntax characters (e.g.,
{{ }},${}). - Sandbox Environment: Restrict the access capabilities of the template engine, disabling dangerous functions or classes (e.g., the
osmodule in Jinja2). - Logic Separation: Ensure user input is always passed as data (e.g., template variable
{{ data }}) rather than code (e.g.,{{ user_input }}). - Static Templates: Avoid dynamically concatenating template content; prioritize using predefined template structures.