SSTI : vulnerabilite Server Side Template Injection

Les failles server-side template injection (SSTI) restent moins recherchées que d’autres types de vulnérabilités. Pourtant, leur impact est important et mène souvent à de l’exécution de code à distance (RCE). Ce sont donc des failles à ne pas sous-estimer.

Dans quels contextes se produisent les vulnérabilités SSTI ? Comment détecter ces failles et comment s’en protéger ?

C’est ce que nous allons détailler dans cet article.

Qu’est-ce qu’une vulnérabilité server-side template injection (SSTI) ?

Certaines applications web utilisent des moteurs de template pour dissocier la présentation visuelle (HTML, CSS…) de la logique applicative (PHP, Python…). Ces moteurs permettent de créer des fichiers modèles (des templates) dans l’application. Les templates sont un mélange de données fixes (mise en page) et de données dynamiques (variables). Lors de l’utilisation de l’application, le moteur de template va remplacer les variables contenues dans un template par des valeurs et va transformer le template en page web (HTML) puis l’envoyer au client.

Une vulnérabilité SSTI (server-side template injection) se produit lorsque les données utilisateurs sont intégrées directement dans un template puis interprétées par le moteur de template. Cela permet aux attaquants d’injecter des directives arbitraires pour manipuler le moteur de template.

Dans quelles situations apparait cette faille ?

La source du problème est liée au fait que les données transmises par les utilisateurs sont interprétées directement par le moteur de template (en tant que données dynamiques), au lieu d’être intégrées en tant que données fixes.

On retrouve le plus souvent ce type de vulnérabilité sur des sites voulant offrir des fonctionnalités de personnalisations avancées comme les wikis, les blogs, les applications marketing ou les CMS.

Une vulnérabilité SSTI arrive généralement lorsqu’un template est modifié pour obtenir un résultat personnalisé pour un besoin précis et que les utilisateurs ont accès à des fonctions de modification du template.

Un exemple concret pourrait être une faille SSTI dans une application web qui propose aux utilisateurs un moyen de gérer les modèles des mails qui sont envoyés par l’application. Un template de base est défini pour l’email mais l’utilisateur peut modifier certains champs. Le moteur de template génère ensuite l’email final. Si l’utilisateur injecte des expressions dans le template et qu’elles sont évaluées par le moteur de template, la fonctionnalité sera vulnérable. L’utilisateur pourra alors tenter de prendre la main sur le serveur.

À noter qu’une faille SSTI ne donne pas systématiquement lieu à une RCE et une compromission du serveur, cela dépend de la situation (comment l’application utilise le template, l’endroit de l’injection, etc.).

Elle peut aussi permettre d’autres attaques comme la lecture de fichiers sur le serveur, des fuites d’information ou des élévations de privilèges. Dans de rares cas, la vulnérabilité n’est pas exploitable.  

Comment trouver une faille SSTI ?

Les failles SSTI sont peu souvent découvertes, car elles sont moins connues et moins recherchées que des failles Cross Site Scripting (XSS) par exemple. Leur identification se déroule en deux parties :

  • Une première phase de détection de la vulnérabilité.
  • Une seconde phase d’identification du moteur de template utilisé.

1. Détecter la vulnérabilité

La première étape consiste à déterminer si une application est vulnérable.

Une approche efficace consiste à fuzzer la cible dans tous les champs de données avec un playload contenant des caractères spéciaux souvent utilisés par les moteurs de templates.

Par exemple : ${{<%[%'"}}@{%\.#{<%=

Si un message d’erreur est retourné dans les réponses du serveur, cela indique que l’application est potentiellement vulnérable. Analyser le message d’erreur pourrait permettre de déterminer quel moteur est utilisé.

Exemple de message d’erreur avec le moteur de template Tornado en python lors de l’injection du payload «{{» dans un paramètre vulnérable:

Internal Server Error

No handlers could be found for logger "tornado.application" Traceback (most recent call last): File "<string>", line 15, in <module> File "/usr/lib/python2.7/dist-packages/tornado/template.py", line 317, in __init__ "exec", dont_inherit=True) File "<string>.generated.py", line 4 _tt_tmp = user.nickname{{ # <string>:1 ^ SyntaxError: invalid syntax

Les injections de template peuvent survenir dans deux contextes différents :

Contexte Plaintext : Beaucoup de moteurs de templates permettent d’entrer du contenu directement dans des balises HTML ou en utilisant la syntaxe du moteur de template. Ce contenu sera interprété côté serveur avant que le serveur ne renvoie une réponse HTTP. Dans ce cas, il est conseillé d’envoyer des formules mathématiques dans les champs testés (en fonction du moteur de template, la syntaxe peut changer). Voici quelques exemples :

  • {{7*7}}
  • ${7*7}
  • ${{7*7}}
  • <%= 7*7 %>
  • #{7*7}

Si une de ces formules est interprétée, la réponse du serveur contiendra « 49 ». Cela veut dire que l’opération mathématique a été interprétée dans un contexte « Plaintext » et qu’une vulnérabilité SSTI est présente.

Contexte code : C’est lorsque les entrées utilisateurs sont directement insérées dans une expression évaluée par le moteur de template. Dans ce cas-là, l’idée consiste à modifier le paramètre concerné en essayant de générer une erreur.

Pour plus de détails sur les contextes, vous pouvez consulter cet article.

2. Identifier le moteur de template utilisé

Identifier le moteur de template utilisé est une étape clé qui permettra par la suite de réaliser la phase d’exploitation.

De nombreux moteurs de template existent. Parmi les plus populaires, nous pouvons citer Twig pour PHP, Freemarker pour Java, ERB pour ruby, Jinja pour python…

La plupart des moteurs de template utilisent des syntaxes très différentes du code HTML pour éviter les confusions.

La méthode d’identification rejoint ce qui a été évoqué dans la partie « Détecter la vulnérabilité » : réussir à générer un message d’erreur permet souvent d’identifier le moteur de template utilisé.

En revanche, lorsque cette méthode échoue (par exemple si les messages d’erreurs ne sont pas affichés dans les réponses du serveur), il convient de procéder par élimination en testant des payloads simples (opération arithmétique) avec des syntaxes différentes correspondant à différents moteurs de template. L’analyse des réponses du serveur doit permettre de déterminer si l’opération transmise a été interprétée ou non. En fonction de la syntaxe utilisée, cela donnera une indication sur le moteur de template utilisé.

Vous pouvez retrouver ici une liste de payload selon les langages de templating.

Note : Si vous connaissez le langage de programmation utilisé pour développer l’application, cela peut vous orienter sur les moteurs de templates utilisés et vous permettre de cibler les tests pour la phase d’identification.

Une fois que vous savez le moteur de template utilisé, vous pourrez commencer à exploiter la faille afin d’obtenir l’impact le plus élevé possible. La première étape consiste à lire la documentation associée au moteur de template afin de déterminer les actions existantes. Il convient ensuite de déterminer quels sont les objets et fonctions auxquels vous avez accès sur l’application vulnérable. Une fois que vous avez une idée globale de ce qu’il est possible de faire, il ne reste plus qu’à tenter d’exploiter la vulnérabilité en essayant d’obtenir une exécution de commande, lire des fichiers, etc.

Exemple d’un payload permettant d’exécuter du code arbitraire lorsque Freemarker est utilisé :

${"freemarker.template.utility.Execute"?new()("id")}

Comment se protéger des failles SSTI ?

Voyant la gravité potentielle de la faille SSTI, on peut se demander pourquoi utiliser un template engine. Il présente des avantages et notamment, il facilite les modifications. En effet, un changement de design pourra se faire sans toucher au flux du code et de manière autonome par les designers/intégrateurs. Les modifications sont indépendantes du code et des traitements logiques. Il n’y a pas besoin de toucher au fonctionnement de l’application.

De même, les développeurs peuvent faire évoluer le code et des éléments logiques sans que l’affichage des données ne soit affecté ou que l’interface graphique ne soit cassée.

La maintenance du projet en est facilitée. Cependant, une bonne configuration est primordiale.

Le meilleur moyen d’éviter les vulnérabilités SSTI consiste à ne jamais autoriser la modification ou la création de templates aux utilisateurs. Cependant lorsqu’il s’agit d’un besoin métier, des solutions existent :

Faire de la sanitization (Assainissement)

Cela consiste à détecter et retirer le contenu potentiellement malveillant avant de les utiliser dans le template. Il faut donc analyser efficacement les données transmises par les utilisateurs. Pour cela différents moyens sont possibles (utilisation de regex, de liste blanche d’expressions autorisées, etc.).

Cependant cette solution n’est pas fiable à 100%. Une erreur de configuration peut laisser votre environnement vulnérable.

Avoir recours au sandboxing (Bac à sable)

Il s’agit de fournir un environnement fermé, où les modules et fonctionnalités à risques sont désactivés. Si les données fournies par les utilisateurs sont interprétées, cela ne pourra pas donner accès à d’autres fonctionnalités ou d’autres données.

Malheureusement, les environnements sandbox sont difficiles à configurer et peuvent être contournés en cas de mauvaise configuration ou d’oublis. C’est un peu un jeu du chat et de la souris entre les défenseurs et les attaquants.

Utiliser un template logic less

Des templates engine « logic-less » existent, qui séparent au maximum le rendu visuel et l’interprétation de code. Mustache est l’un des plus connus.

Il n’a pas d’énoncés explicites de flux de contrôle, tout le contrôle étant piloté par les données, et il est impossible d’intégrer une logique d’application dans les templates Mustache.

Les attaques les plus dangereuses (RCE) ne sont ainsi plus possibles et cela réduit grandement le risque d’attaques.

Conclusion

La grande majorité des vulnérabilités de type SSTI que nous rencontrons lors des tests d’intrusion sur une plateforme web conduisent à une exécution de code arbitraire et à la compromission du serveur. Il s’agit donc d’une faille à ne pas négliger. Les recommandations peuvent être complexes à mettre en place et des tests sont nécessaires pour confirmer leur efficacité.