Skip to content
KitploitKITPLOIT
OutilsBlog
Soumettre
OutilsBlog
Soumettre

Outils de Hacking, PenTest et Cybersécurité pour votre Arsenal de Sécurité !

Kitploit est un répertoire d'outils de hacking, de cybersécurité et de pentesting. Découvrez les dernières mises à jour des projets pour trouver des vulnérabilités, analyser des systèmes, automatiser les tests et renforcer votre sécurité.

··Flux·Contact·Confidentialité·© 2026 Kitploit

Répertoire d'outils

Catégories

Voir toutes les catégories
Loading categories
Magento-CVE-2016-4010 — Magento Exécution de code à distance non autorisée (CVE-2016-4010) | Kitploit
Outils/GitHubGitHub/brianwrf/magento-cve-2016-4010
Analyse des VulnérabilitésAnalyse de CodeExploitationExploitation d'Applications WebTests d'IntrusionApprentissage et ÉducationLabs et Pratique
GitHubbrianwrf/magento-cve-2016-4010

Magento-CVE-2016-4010

Magento Exécution de code à distance non autorisée (CVE-2016-4010)

Voir le dépôt
633il y a 10 ansPas encore vérifié

Populaires

Voir tout →

Découvrez les outils les plus utilisés par notre communauté.

Explorer tous les outils

Parcourez notre collection d'outils

Voir tous les outils →
Partager

Analyse et exploitation de la vulnérabilité d'exécution de code à distance non authentifiée dans Magento (CVE-2016-4010)


0x00 Introduction

Le 17 mai, le chercheur en sécurité Netanel Rubin a divulgué une vulnérabilité d'exécution de code à distance non authentifiée dans Magento (CVE-2016-4010). Cette vulnérabilité regroupe en réalité plusieurs failles mineures et permet à un attaquant d'exécuter du code PHP sans authentification sur un serveur Magento vulnérable. Magento est une plateforme e-commerce très populaire, rachetée par eBay en 2011. Des entreprises connues telles que Samsung, Nikon, Lenovo, ainsi que de nombreuses petites boutiques en ligne l'utilisent. On estime que Magento est utilisé par 250 000 boutiques en ligne, représentant un volume d'affaires annuel de 60 milliards de dollars.

0x01 Analyse

Conditions d'exploitation de la vulnérabilité :

  • Les RPC (REST ou SOAP) sont activés sur Magento, ce qui est le cas par défaut pour la plupart.
  • Les versions CE et EE de Magento sont inférieures à 2.0.6.

L'API web de Magento permet deux types de RPC : REST RPC et SOAP API. Les deux offrent les mêmes fonctionnalités, la seule différence étant que la première utilise JSON et des requêtes HTTP pour transmettre les entrées, tandis que la seconde utilise XML.

Pour n'exposer que certaines parties des modules, Magento offre aux développeurs un moyen pratique : déclarer uniquement les API des modules auxquels ils souhaitent donner accès dans le fichier webapi.xml. Ce fichier contient toutes les classes et méthodes d'API web qui doivent être exposées, et chaque méthode spécifie les droits nécessaires. Ces droits incluent :

  • anonymous – méthodes accessibles à tout le monde.
  • self – uniquement aux utilisateurs enregistrés et aux administrateurs disposant de droits spécifiques, par exemple Magento_Backend::admin est réservé aux administrateurs pouvant modifier la configuration du serveur.
  • Bien sûr, cette méthode qui permet aux développeurs d'utiliser le fichier webapi.xml pour communiquer entre le front-end et le back-end (API web) ouvre en réalité une porte dérobée directement dans le cœur du module.

    De plus, même avec un accès anonymous, nous avons besoin d'un moyen de transmettre dynamiquement des valeurs. Il s'agit d'utiliser différents objets dans le système. Par exemple, la fonction API CustomerRepositoryInterface::save() nous permet d'utiliser un objet CustomerInterface dans la variable $customer. Le prototype du code est le suivant :

    root@kitploit:~
    interface CustomerRepositoryInterface
    {
    /**
     * Créer un client.
     */
    public function save(\Magento\Customer\Api\Data\CustomerInterface $customer);
    }
    

    Comment créer un objet via l'interface RPC ? En réalité, la réponse réside dans la façon dont Magento configure le serveur SOAP.

    Magento utilise le serveur SOAP par défaut fourni avec PHP (SoapServer). Pour une configuration correcte, SoapServer a besoin d'un fichier WSDL qui définit toutes les méthodes, les paramètres et les types personnalisés utilisés dans les requêtes RPC réelles. Magento génère différents fichiers WSDL pour chaque module prenant en charge les fonctionnalités XMLRPC et définit directement les valeurs provenant du fichier webapi.xml du module.

    Lorsqu'une requête RPC est analysée par le serveur, celui-ci utilise les données trouvées dans le fichier WSDL pour déterminer si la requête est valide, en vérifiant la méthode, les paramètres et les types. Si la requête est valide, l'objet de requête analysé est transmis à Magento pour un traitement ultérieur. Un point très important : SoapServer n'interagit en aucune façon avec Magento ; toutes les informations sur les méthodes et paramètres du module proviennent du fichier WSDL. À ce stade, la requête est toujours constituée de tableaux imbriqués ; aucun objet n'est créé lors de la phase d'analyse de SoapServer. Pour créer les objets nécessaires, Magento traite lui-même les entrées.

    Pour extraire les noms de paramètres et leurs types, Magento récupère le prototype de la méthode de la requête (voir le code précédent). Pour certains types de base (chaîne, tableau, booléen, etc.), le système associe l'entrée au type correspondant. Mais pour les types objet, la résolution est plus complexe.

    Si le type de données d'un paramètre est une instance de classe, Magento tente de créer une instance à partir de l'entrée fournie. Rappelons qu'à ce stade, l'entrée n'est qu'un dictionnaire dont les clés sont les noms de propriétés et les valeurs les valeurs des propriétés.

    Tout d'abord, Magento crée une nouvelle instance de la classe requise. Ensuite, il tente de la remplir en utilisant la méthode suivante :

    1. Récupérer le nom de la propriété (à partir de la clé du dictionnaire d'entrée).
    2. Chercher une méthode publique nommée Set[Nom], où [Nom] est le nom de la propriété.
    3. Si une telle méthode existe, l'exécuter en utilisant la valeur de la propriété comme paramètre.
    4. Si aucune méthode de ce type n'existe, ignorer cette propriété et passer à la suivante.

    Magento traite ainsi chaque propriété que l'utilisateur tente de définir. Une fois toutes les propriétés vérifiées, Magento considère l'instance comme prête et traite le paramètre suivant. Lorsque tous les paramètres sont traités, Magento exécute finalement la méthode API.

    En résumé, Magento vous permet de créer un objet, de définir ses propriétés publiques, puis d'exécuter n'importe quelle méthode commençant par Set via son RPC. C'est ce comportement qui entraîne la vulnérabilité.

    Des recherches ont montré que certains appels API permettent de définir des informations spécifiques dans le panier, comme l'adresse de livraison, les articles, voire le mode de paiement.

    Lorsque Magento définit nos informations dans l'instance du panier, il utilise la méthode save de l'instance pour stocker les nouvelles données dans la base de données.

    Voyons comment fonctionne la méthode save :

    root@kitploit:~
    /**
     * Enregistrer les données de l'objet
     */
    public function save(\Magento\Framework\Model\AbstractModel $object)
    {
    ...
    // Si l'objet est valide et peut être enregistré
    if ($object->isSaveAllowed()) {
        // Sérialiser les champs qui doivent l'être
        $this->_serializeFields($object);
        ...
        // Si l'objet existe déjà dans la BD, le mettre à jour
        if ($this->isObjectNotNew($object)) {
            $this->updateObject($object);
        // Sinon, créer un nouvel enregistrement
        } else {
            $this->saveNewObject($object);
        }
         
        // Désérialiser les champs que nous avons sérialisés
        $this->unserializeFields($object);
    }
    ...
    return $this;
    }
    // AbstractDb::save()
    

    Magento s'assure que notre objet est valide, sérialise les parties qui doivent l'être et les stocke dans la base de données, puis désérialise les parties précédemment sérialisées.

    Cela semble simple, n'est-ce pas ? En réalité, ce n'est pas le cas. Continuons à voir comment Magento détermine quelles parties doivent être sérialisées.

    root@kitploit:~
    /**
     * Sérialiser les champs sérialisables de l'objet
     */
    protected function _serializeFields(\Magento\Framework\Model\AbstractModel $object)
    {
    // Parcourt la propriété '_serializableFields'
    // (contenant les champs codés en dur qui doivent être sérialisés)
    foreach ($this->_serializableFields as $field => $parameters) {
        // Récupère la valeur du champ
        $value = $object->getData($field);
         
        // Si c'est un tableau ou un objet, le sérialiser
        if (is_array($value) || is_object($value)) {
            $object->setData($field, serialize($value));
        }
    }
    }
    // AbstractDb::_serializeFields()
    

    Comme nous le voyons, seuls les champs figurant dans le dictionnaire codé en dur _serializableFields peuvent être sérialisés. Le plus important est que cette méthode ne sérialise que si la valeur du champ est un tableau ou un objet.

    Examinons maintenant comment Magento détermine quelles parties doivent être désérialisées.

    root@kitploit:~
    /**
     * Désérialiser les champs sérialisables de l'objet
     */
    public function unserializeFields(\Magento\Framework\Model\AbstractModel $object)
    {
    // Parcourt la propriété '_serializableFields'
    // (contenant les champs codés en dur qui doivent être sérialisés)
    foreach ($this->_serializableFields as $field => $parameters) {
        // Récupère la valeur du champ
        $value = $object->getData($field);
         
        // Si ce n'est pas un tableau ou un objet, le désérialiser
        if (!is_array($value) && !is_object($value)) {
            $object->setData($field, unserialize($value));
        }
    }
    }
    // AbstractDb::unserializeFields()
    

    Très similaire. La seule différence est que cette fois, Magento s'assure que la valeur du champ n'est ni un tableau ni un objet. Grâce à ces deux vérifications, nous devrions pouvoir réaliser une attaque par injection d'objet, en définissant simplement une chaîne de caractères bien formée dans un champ sérialisable. Lorsque nous procédons ainsi, le système ne sérialise pas ce champ avant de stocker l'objet dans la base de données, car ce n'est ni un objet ni un tableau. Mais lorsque le système tentera de le désérialiser, après l'exécution de la requête en base de données, il sera désérialisé parce que ce n'est ni un objet ni un tableau.

    C'est cette condition presque invisible qui crée la vulnérabilité. Il reste à déterminer quels champs sont considérés comme « sérialisables » et comment nous pouvons les définir.

    Bien sûr, la première question est simple : il suffit de chercher quelle classe contient la propriété _serializableFields. Rapidement, une méthode API a été trouvée dans la classe Payment, mais pas en tant que paramètre, nous ne pouvons donc pas créer ni contrôler les propriétés de son instance. Plus important encore, son champ sérialisable additional_information ne peut être défini que comme un tableau, en utilisant la technique `Set[PROPRIÉTÉ] comme mesure de sécurité supplémentaire. Nous ne pouvons donc pas le créer, et même si nous le pouvions, nous ne pourrions pas le définir comme une chaîne.

    Cependant, il est intéressant de noter qu'il peut être défini d'une manière « astucieuse ». Lorsque Magento définit les propriétés d'une instance de paramètre, en réalité il ne définit pas les propriétés, mais les enregistre dans un dictionnaire nommé _data. Ce dictionnaire est utilisé lorsqu'une propriété de l'instance est sollicitée. Pour nous, cela signifie que notre champ sérialisable additional_information est en réalité stocké dans un dictionnaire interne plutôt que dans une propriété normale.

    Ainsi, si nous pouvons contrôler complètement le dictionnaire _data, nous pouvons facilement contourner la restriction du tableau sur le champ additional_information, car nous pouvons le définir manuellement sans appeler Set[PROPRIÉTÉ].

    Mais comment contrôler ce dictionnaire sensible ?

    Avant d'enregistrer notre instance de Payment, Magento doit modifier ses propriétés. Magento considère notre entrée API comme les informations de paiement à stocker dans l'instance Payment, comme suit :

    root@kitploit:~
    /**
     * Ajoute un moyen de paiement spécifié à un panier spécifié.
     */
    public function set($cartId, \Magento\Quote\Api\Data\PaymentInterface $method)
    {
     
    $quote = $this->quoteRepository->get($cartId); // Récupérer l'instance du panier
    $payment = $quote->getPayment(); // Récupérer l'instance de paiement
    // Récupérer les données de l'entrée utilisateur
    $data = $method->getData();
    // Vérifier les données supplémentaires
    if (isset($data['additional_data'])) {
        $data = array_merge($data, (array)$data['additional_data']);
        unset($data['additional_data']);
    }
    // Importer l'entrée utilisateur dans l'instance de paiement
    $payment->importData($data);
     
    ...
    }
    // PaymentMethodManagement::set()
    

    Comme nous le voyons, les données de Payment sont extraites en appelant $method->getData(), qui renvoie la propriété _data du paramètre $method. Rappelez-vous que $method étant un paramètre de la méthode API, nous pouvons le contrôler.

    Lorsque Magento appelle getData() sur notre paramètre $method, la propriété _data du paramètre est renvoyée, contenant toutes les informations de paiement que nous avons insérées. Ensuite, il appelle importData() avec la propriété _data comme entrée, remplaçant la propriété _data de l'instance Payment par la nôtre. Ainsi, nous pouvons désormais utiliser notre propre propriété _data pour remplacer la propriété _data sensible de l'instance Payment, ce qui signifie que nous pouvons maintenant définir le champ additional_information.

    Pour que unserialize() fonctionne, nous avons besoin que le champ puisse être défini comme une chaîne, mais la méthode Set[PROPRIÉTÉ] n'autorise que les tableaux. La solution consiste à insérer deux lignes de code avant d'appeler importData(). Magento permet aux développeurs d'ajouter leurs propres moyens de paiement, en fournissant leurs propres données et informations. Pour ce faire, Magento utilise le champ additional_data. Ce champ est un dictionnaire contenant des données supplémentaires pour le moyen de paiement, entièrement contrôlé par l'utilisateur. Pour que le contenu personnalisé fasse partie des données d'origine, Magento fusionne le dictionnaire additional_data avec le dictionnaire data d'origine, ce qui permet en pratique au dictionnaire additional_data de remplacer toutes les valeurs du dictionnaire data, donc de les écraser complètement. Cela signifie qu'après la fusion des deux dictionnaires, le dictionnaire additional_data contrôlé par l'utilisateur devient maintenant le dictionnaire _data du paramètre, et par importData(), il devient aussi la propriété _data sensible de l'instance Payment. En d'autres termes, nous contrôlons désormais complètement le champ sérialisable additional_information et pouvons réaliser une attaque par injection d'objet.

    Puisque nous pouvons désérialiser n'importe quelle chaîne de notre choix, il est temps de procéder à l'attaque par injection d'objet.

    Tout d'abord, nous avons besoin d'un objet avec une méthode __wakeup() ou __destruct() pour qu'elle soit appelée automatiquement lors de la désérialisation ou de la destruction. En effet, même si nous pouvons contrôler les propriétés de l'objet, nous ne pouvons pas appeler ses méthodes. C'est pourquoi nous devons nous appuyer sur les méthodes magiques de PHP, qui sont appelées automatiquement lorsqu'un événement se produit.

    Le premier objet que nous utiliserons est une instance de la classe Credis_Client, qui contient la méthode suivante :

    root@kitploit:~
    /*
     * Appelée automatiquement lorsque l'objet est détruit.
     */
    public function __destruct()
    {
    if ($this->closeOnDestruct) {
        $this->close();
    }
    }
    /*
     * Ferme le flux Redis.
     */
    public function close()
    {
    if ($this->connected && ! $this->persistent) {
            ...
            $result = $this->redis->close();
    }
    ...
    }
    // Credis_Client::__destruct(), close()
    

    Nous voyons que cette classe a une simple méthode __destruct (appelée automatiquement par PHP lors de la destruction de l'objet) qui appelle la méthode close(). Fait intéressant, si elle trouve une connexion active à un serveur Redis, la méthode close() appelle close() sur la propriété redis pour la fermer.

    Comme unserialize() nous permet de contrôler toutes les propriétés de l'objet, nous pouvons aussi contrôler la propriété redis. Nous pouvons y placer n'importe quel objet de notre choix (pas seulement Redis) et appeler n'importe quelle méthode close() dans n'importe quelle classe du système. Cela élargit considérablement notre surface d'attaque. Dans Magento, plusieurs méthodes close() existent, et comme elles sont souvent utilisées pour terminer des flux, fermer des descripteurs de fichiers ou stocker des données d'objet, nous devrions trouver des appels intéressants.

    Comme prévu, nous avons trouvé la méthode close() suivante dans la classe Transaction :

    root@kitploit:~
    /**
     * Fermer cette transaction
     */
    public function close($shouldSave = true)
    {
    ...
    if ($shouldSave) {
        $this->save();
    }
    ...
    }
    /**
     * Enregistrer les données de l'objet
     */
    public function save()
    {
    $this->_getResource()->save($this);
    return $this;
    }
    // Magento\Sales\Model\Order\Payment\Transaction::__destruct(), close()
    

    Simple : close() appelle save(), qui appelle save() sur la propriété _resource. De la même manière, comme nous contrôlons la propriété _resource, nous pouvons contrôler sa classe et donc appeler la méthode save() de n'importe quelle classe de notre choix.

    Encore un grand pas en avant. Comme nous l'avons deviné, la méthode save() est souvent utilisée pour enregistrer diverses données dans différents supports (système de fichiers, base de données, etc.). Nous devons maintenant trouver une méthode save() qui utilise le système de fichiers comme support de stockage.

    Rapidement, nous en avons trouvé une :

    root@kitploit:~
    /**
     * Essayer d'enregistrer le cache de configuration dans un fichier
     */
    public function save()
    {
    ...
    // enregistrer les statistiques
    file_put_contents($this->getStatFileName(), $this->getComponents());
    ...
    }
    // Magento\Framework\Simplexml\Config\Cache\File::save()
    

    Cette méthode écrit les données du champ components dans un fichier. Comme le chemin du fichier est obtenu à partir du champ stat_file_name et que nous contrôlons ces deux paramètres, nous contrôlons en réalité le chemin et le contenu du fichier, ce qui crée une vulnérabilité d'écriture de fichier arbitraire.

    Il ne nous reste plus qu'à trouver un chemin valide, accessible en écriture et accessible par le serveur web, pour écrire le fichier. Dans toutes les installations de Magento, il existe un répertoire /pub utilisé pour stocker les images ou les fichiers téléchargés par l'administrateur, ce qui constitue un chemin exploitable.

    Finalement, il suffit d'écrire un fichier webshell PHP sur le serveur pour exécuter du code PHP arbitraire sans authentification sur le serveur Magento.

    0x02 Exploitation

    Mise en place de l'environnement de test

    1. Télécharger le package vulnérable (nous utilisons la version 2.0.0) Lien de téléchargement : https://github.com/magento/magento2/archive/2.0.0.zip

    2. Installer Magento Procédure d'installation : https://github.com/magento/magento2/tree/2.0.0

      Remarque : Des problèmes peuvent survenir, voir :

      • http://magento2king.com/magento2-insta-be-downloaded/
      • https://github.com/magento/magento2/issues/2419

    Exploitation de la vulnérabilité

    L'exploit public de l'exploit-db est disponible à : https://www.exploit-db.com/exploits/39838/

    Méthode d'exploitation :

    1. Trouver un site Magento vulnérable Vérification en ligne de la version de Magento : http://magentoversion.com/

    2. Ajouter un produit au panier Description de l'image

    3. Aller dans le panier et cliquer sur « Passer à la caisse » Description de l'image

    4. Remplir l'adresse de livraison et examiner la requête POST /rest/default/V1/guest-carts/[guestCartId]/shipping-information pour obtenir [guestCartID] Description de l'image Description de l'image

    5. Enregistrer l'exploit ci-dessus sous le nom magento_exp.php et exécuter : php magento_exp.php [Magento_URL] [guestCartID] ([chemin d'écriture du webshell]) Description de l'image

    Détection en masse

    Après étude de l'exploit ci-dessus, l'exploitation nécessite les conditions suivantes :

    1. La version de Magento du site cible doit être inférieure à 2.0.6 et l'API REST activée.
    2. La page d'accueil du site cible doit contenir le code JavaScript suivant : Description de l'image

    Par conséquent, un script simple de vérification en masse a été écrit pour être utilisé avec l'exploit ci-dessus :

    root@kitploit:~
    #!/usr/bin/env python
    import urllib
    import sys
    import socket
    timeout = 5
    socket.setdefaulttimeout(timeout)
    
    input = sys.argv[1]  # Fichier contenant les URLs des sites Magento
    output = sys.argv[2] # Fichier de sauvegarde des résultats, par exemple : output.txt
    
    def logFile(str):
        f = open(output,'a')
        f.write(str+"\n")
        f.close()
    
    def checkVul(url):
        try:
            html = urllib.urlopen(url).read()
            if "guest-carts" in html:
                print url,"is vulnerable!"
                logFile(url)
            else:
                print url,"is not vulnerable!"
        except Exception:
            pass
    
    if __name__ == '__main__':
        inp = open(input,'r')
        for i in inp:
            url=i.strip()
            #print url
            checkVul(url)
        print "All Done!"
    

    Résultat de l'exécution : Description de l'image

    0x03 Défense

    Mettre à jour Magento vers la dernière version (2.0.6). Lien de téléchargement : https://www.magentocommerce.com/download

    Références

    • http://netanelrub.in/2016/05/17/magento-unauthenticated-remote-code-execution/

    • https://www.exploit-db.com/exploits/39838/

    Télécharger l’outil