Cybreach Consulting Prendre RDV
Back to articles
Vulnerabilities

SSTI: server-side template injection

Template engines (Jinja2, Twig, Freemarker, Pebble, Velocity) evaluate expressions in special tags. When user input is concatenated directly into the template before evaluation (rather than being passed as a variable), the attacker can inject expressions that the engine will evaluate. Depending on the engine and configuration, this ranges from reading sensitive variables to OS command execution.

Identifying SSTI

The classic detection test consists of submitting a mathematical expression in the engine's syntax and checking whether it's evaluated. If the application displays the result of the expression rather than the literal string, it's vulnerable.

Detection payloads by engine

# Jinja2 (Python/Flask)
{{7*7}}          -> 49 (vulnérable)
{{7*'7'}}        -> 7777777 (confirme Jinja2)

# Twig (PHP)
{{7*7}}          -> 49
${7*7}           -> ${7*7} (n'est pas Twig)

# Freemarker (Java)
${7*7}           -> 49

# Velocity (Java)
#set($x = 7*7)${x} -> 49

Exploitation: from evaluation to command execution

Jinja2 exploitation example (RCE)

# Jinja2 : accès aux classes Python pour exécuter des commandes
{{''.__class__.__mro__[1].__subclasses__()}}

# Une fois la classe subprocess.Popen identifiée (index variable) :
{{''.__class__.__mro__[1].__subclasses__()[xxx](['id'],stdout=-1).communicate()[0]}}

# Résultat : uid=33(www-data) gid=33(www-data) groups=33(www-data)

Prevention

The fundamental rule: never concatenate user data into a template. Always pass it as variables. The difference between the two patterns is critical.

Vulnerable vs. correct pattern (Jinja2)

from jinja2 import Environment

env = Environment()

# Vulnérable : l'entrée utilisateur fait partie du template
template_str = f"Bonjour {user_input}"
env.from_string(template_str).render()

# Correct : l'entrée utilisateur est une variable, pas du template
env.from_string("Bonjour {{ name }}").render(name=user_input)

If the template engine must be dynamic (users can define templates), use a sandbox: Jinja2 offers a SandboxedEnvironment that restricts access to Python objects. Evaluate the need for this feature and consider less risky alternatives (Mustache, Handlebars in logic-less mode).

A question after reading?

Does your application use a template engine with user data? Send a message.

Or book a 30-minute scoping call directly

Book a call