[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$f8VRb3pVEPs_r7v5E8Xf07f5Yv2gX7fQ8axH1vrUxWHU":3},{"id":4,"url_slug":5,"title":6,"description":7,"plugin_slug":8,"theme_slug":9,"affected_versions":10,"patched_in_version":11,"severity":12,"cvss_score":13,"cvss_vector":14,"vuln_type":15,"published_date":16,"updated_date":17,"references":18,"days_to_patch":20,"patch_diff_files":21,"patch_trac_url":9,"research_status":25,"research_verified":26,"research_rounds_completed":27,"research_plan":28,"research_summary":29,"research_vulnerable_code":30,"research_fix_diff":31,"research_exploit_outline":32,"research_model_used":33,"research_started_at":34,"research_completed_at":35,"research_error":9,"poc_status":9,"poc_video_id":9,"poc_summary":9,"poc_steps":9,"poc_tested_at":9,"poc_wp_version":9,"poc_php_version":9,"poc_playwright_script":9,"poc_exploit_code":9,"poc_has_trace":26,"poc_model_used":9,"poc_verification_depth":9,"poc_exploit_code_gated":26,"source_links":36},"CVE-2025-12656","migration-backup-staging-wpvivid-backup-migration-authenticated-admin-arbitrary-directory-deletion","Migration, Backup, Staging – WPvivid Backup & Migration \u003C= 0.9.128 - Authenticated (Admin+) Arbitrary Directory Deletion","The Migration, Backup, Staging – WPvivid Backup & Migration plugin for WordPress is vulnerable to arbitrary directory deletion due to insufficient file path validation in the delete_cancel_staging_site() function in all versions up to, and including, 0.9.128. This makes it possible for authenticated attackers, with Administrator-level access and above, to delete arbitrary folders on the server, which leads to a loss of data.","wpvivid-backuprestore",null,"\u003C=0.9.128","0.9.129","low",3.8,"CVSS:3.1\u002FAV:N\u002FAC:L\u002FPR:H\u002FUI:N\u002FS:U\u002FC:N\u002FI:L\u002FA:L","External Control of File Name or Path","2026-06-05 10:43:12","2026-06-05 23:28:25",[19],"https:\u002F\u002Fwww.wordfence.com\u002Fthreat-intel\u002Fvulnerabilities\u002Fid\u002F2f5962e5-3dc7-4f93-889c-d5e3530c7dba?source=api-prod",1,[22,23,24],"includes\u002Fstaging\u002Fclass-wpvivid-staging.php","readme.txt","wpvivid-backuprestore.php","researched",false,3,"# Exploitation Research Plan: CVE-2025-12656 (Arbitrary Directory Deletion)\n\n## 1. Vulnerability Summary\n**CVE-2025-12656** is an arbitrary directory deletion vulnerability in the **WPvivid Backup & Migration** plugin (\u003C= 0.9.128). The vulnerability exists within the `delete_cancel_staging_site()` function in `includes\u002Fstaging\u002Fclass-wpvivid-staging.php`. Due to a lack of sufficient path validation, an authenticated user with Administrator-level privileges can provide a manipulated file path that causes the plugin to recursively delete an arbitrary folder on the server. Although it requires Admin privileges, it represents a significant data loss risk and can be used to disable security plugins or delete critical system directories.\n\n## 2. Attack Vector Analysis\n*   **Endpoint:** `\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **AJAX Action:** `wpvivid_delete_cancel_staging_site` (inferred based on function name)\n*   **HTTP Method:** POST\n*   **Vulnerable Parameter:** `path` (inferred) or `dir` (inferred)\n*   **Authentication:** Required (Administrator)\n*   **Preconditions:** The plugin must be active, and the attacker must have valid administrator credentials.\n\n## 3. Code Flow\n1.  **Registration:** The `WPvivid_Staging_Free` class constructor (in `includes\u002Fstaging\u002Fclass-wpvivid-staging.php`) calls `$this->load_ajax()`.\n2.  **AJAX Handler:** Inside `load_ajax()`, the plugin registers the callback:\n    `add_action('wp_ajax_wpvivid_delete_cancel_staging_site', array($this, 'delete_cancel_staging_site'));` (inferred registration pattern).\n3.  **Vulnerable Function:** The `delete_cancel_staging_site()` function is triggered.\n4.  **Input Extraction:** The function retrieves a directory path from the user input (e.g., `$_POST['path']`).\n5.  **Insufficient Validation:** The function fails to check if the provided path is contained within the authorized staging directory (e.g., it does not check if the path starts with `WPVIVID_STAGING_DIR` or similar).\n6.  **Sink:** The path is passed to a recursive deletion routine, likely `WPvivid_Staging_Copy_Files_Ex::delete_dir($path)` or a standard PHP `rmdir()` wrapper, which deletes the entire directory tree at that location.\n\n## 4. Nonce Acquisition Strategy\nThe plugin likely protects staging operations with a WordPress nonce.\n\n1.  **Identify Script Localization:** The function `enqueue_scripts` in `class-wpvivid-staging.php` (Line 52) enqueues scripts that handle the staging UI. This is where the nonce is typically localized.\n2.  **Create Trigger Content:** To ensure the staging scripts and nonces are loaded, create a page that includes any WPvivid staging-related shortcode (if applicable) or simply navigate to the plugin's staging page.\n    *   `wp post create --post_type=page --post_status=publish --post_content='[wpvivid_staging_placeholder]'` (inferred shortcode)\n3.  **Navigate and Extract:**\n    *   Login as Administrator using `browser_navigate`.\n    *   Navigate to the WPvivid Staging page: `\u002Fwp-admin\u002Fadmin.php?page=wpvivid-staging`.\n    *   **JS Variable:** Look for the localized object, likely `wpvivid_staging_vars` or `wpvivid_ajax_object`.\n    *   **Execution:** `browser_eval(\"wpvivid_staging_vars.nonce\")` (inferred name).\n\n## 5. Exploitation Strategy\nThe goal is to delete a non-staging directory, such as a dummy folder in `uploads`.\n\n*   **Step 1:** Prepare a target directory.\n*   **Step 2:** Obtain a valid Admin session and Nonce.\n*   **Step 3:** Construct the malicious AJAX request.\n\n**Request Details:**\n*   **URL:** `https:\u002F\u002F\u003Ctarget>\u002Fwp-admin\u002Fadmin-ajax.php`\n*   **Headers:**\n    *   `Content-Type: application\u002Fx-www-form-urlencoded`\n    *   `Cookie: \u003CAdmin Cookies>`\n*   **Payload:**\n    ```text\n    action=wpvivid_delete_cancel_staging_site&path=..\u002F..\u002Fuploads\u002Ftarget_to_delete&_wpnonce=\u003CNONCE>\n    ```\n    *(Note: The `path` may need to be an absolute path if the plugin uses `ABSPATH`, or relative if it uses the current working directory).*\n\n## 6. Test Data Setup\n1.  **Install Plugin:** Ensure WPvivid version 0.9.128 is installed and active.\n2.  **Create Admin User:**\n    `wp user create exploit_admin admin@example.com --role=administrator --user_pass=password123`\n3.  **Create Target Directory:**\n    `mkdir -p \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fuploads\u002Fdelete_me_test\u002F`\n    `touch \u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fuploads\u002Fdelete_me_test\u002Fevidence.txt`\n\n## 7. Expected Results\n*   **HTTP Response:** The server should return a JSON success response (e.g., `{\"result\":\"success\"}`) or a `1` (standard WP AJAX success).\n*   **Filesystem Impact:** The directory `\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fuploads\u002Fdelete_me_test\u002F` and its contents should be completely removed from the server.\n\n## 8. Verification Steps\nAfter sending the HTTP request, verify the deletion using WP-CLI:\n\n```bash\n# Check if the directory still exists\nif [ ! -d \"\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fuploads\u002Fdelete_me_test\u002F\" ]; then\n    echo \"Exploit Successful: Directory deleted.\"\nelse\n    echo \"Exploit Failed: Directory still exists.\"\nfi\n```\n\n## 9. Alternative Approaches\n*   **Absolute Path Injection:** If relative pathing (`..\u002F`) is filtered but the validation is still weak, try providing the full absolute path retrieved via other leaks (e.g., `phpinfo` or error logs): `\u002Fvar\u002Fwww\u002Fhtml\u002Fwp-content\u002Fplugins\u002Fakismet\u002F`.\n*   **Different AJAX Action:** If `wpvivid_delete_cancel_staging_site` is not the correct action name, check `class-wpvivid-staging-sites-list.php` for actions related to \"removing\" or \"cleaning up\" staging sites, such as `wpvivid_delete_staging_site`.\n*   **Bypass via Directory Traversal:** If the plugin prepends a base path, use traversal sequences: `path=\u002Fwp-content\u002Fwpvividbackups\u002Fstaging\u002F..\u002F..\u002F..\u002Fwp-content\u002Fplugins\u002Fsome-plugin`.","The WPvivid Backup & Migration plugin for WordPress is vulnerable to arbitrary directory deletion in versions up to 0.9.128. This occurs because the `delete_cancel_staging_site()` function lacks sufficient validation to ensure that the directory being deleted is actually a staging site, allowing an authenticated administrator to recursively delete any folder on the server.","\u002F* includes\u002Fstaging\u002Fclass-wpvivid-staging.php *\u002F\n\n    public function delete_cancel_staging_site(){\n        global $wpvivid_plugin;\n        check_ajax_referer( 'wpvivid_ajax', 'nonce' );\n\n        \u002F\u002F ... [retrieval of site_path from staging info] ...\n\n                if (!empty($site_path)) {\n                    $home_path = untrailingslashit(ABSPATH);\n                    if ($home_path != $site_path) {\n                        if (file_exists($site_path)) {\n                            if (!class_exists('WP_Filesystem_Base')) include_once(ABSPATH . '\u002Fwp-admin\u002Fincludes\u002Fclass-wp-filesystem-base.php');\n                            if (!class_exists('WP_Filesystem_Direct')) include_once(ABSPATH . '\u002Fwp-admin\u002Fincludes\u002Fclass-wp-filesystem-direct.php');\n\n                            $wp_filesystem = new WP_Filesystem_Direct(array());\n\n                            WPvivid_Staging_Copy_Files_Ex::delete_dir($site_path);\n                        }\n                    }\n                }","diff -ru \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpvivid-backuprestore\u002F0.9.128\u002Fincludes\u002Fstaging\u002Fclass-wpvivid-staging.php \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpvivid-backuprestore\u002F0.9.129\u002Fincludes\u002Fstaging\u002Fclass-wpvivid-staging.php\n--- \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpvivid-backuprestore\u002F0.9.128\u002Fincludes\u002Fstaging\u002Fclass-wpvivid-staging.php\t2025-11-18 00:25:48.000000000 +0000\n+++ \u002Fhome\u002Fdeploy\u002Fwp-safety.org\u002Fdata\u002Fplugin-versions\u002Fwpvivid-backuprestore\u002F0.9.129\u002Fincludes\u002Fstaging\u002Fclass-wpvivid-staging.php\t2026-06-01 00:32:46.000000000 +0000\n@@ -1264,6 +1264,126 @@\n         }\n     }\n \n+    private function wpvivid_normalize_delete_path($path, $use_realpath = true)\n+    {\n+        if (empty($path) || !is_string($path)) {\n+            return false;\n+        }\n+\n+        $path = wp_normalize_path($path);\n+        $path = untrailingslashit($path);\n+        if ($use_realpath) {\n+            $real_path = realpath($path);\n+            if ($real_path !== false) {\n+                $path = wp_normalize_path($real_path);\n+                $path = untrailingslashit($path);\n+            }\n+        }\n+\n+        if ((defined('PHP_OS_FAMILY') && PHP_OS_FAMILY === 'Windows') || DIRECTORY_SEPARATOR === '\\\\') {\n+            $path = strtolower($path);\n+        }\n+\n+        return $path;\n+    }\n+\n+    private function wpvivid_get_cancel_staging_cleanup($site_path)\n+    {\n+        $normalized_path = $this->wpvivid_normalize_delete_path($site_path, false);\n+        if ($normalized_path === false) {\n+            return false;\n+        }\n+\n+        $cleanup = get_transient('wpvivid_cancel_staging_cleanup_' . md5($normalized_path));\n+        if (!is_array($cleanup) || empty($cleanup['staging_path'])) {\n+            return false;\n+        }\n+\n+        $cleanup_path = $this->wpvivid_normalize_delete_path($cleanup['staging_path'], false);\n+        $delete_path = $this->wpvivid_normalize_delete_path($site_path, false);\n+        if ($cleanup_path === false || $delete_path === false || $cleanup_path !== $delete_path) {\n+            return false;\n+        }\n+\n+        return $cleanup;\n+    }\n+\n+    private function wpvivid_is_allowed_staging_delete_path($site_path)\n+    {\n+        if (empty($site_path) || !is_string($site_path)) {\n+            return false;\n+        }\n+\n+        if (!file_exists($site_path) || !is_dir($site_path)) {\n+            return false;\n+        }\n+\n+        $real_site_path = $this->wpvivid_normalize_delete_path($site_path, true);\n+        if ($real_site_path === false) {\n+            return false;\n+        }\n+\n+        $real_site_path = wp_normalize_path(untrailingslashit($real_site_path));\n+\n+        $protected_paths = array(\n+            ABSPATH,\n+            WP_CONTENT_DIR,\n+            WP_PLUGIN_DIR,\n+            get_theme_root(),\n+            ABSPATH . 'wp-admin',\n+            ABSPATH . 'wp-includes'\n+        );\n+\n+        $upload_dir = wp_get_upload_dir();\n+        if (!empty($upload_dir['basedir'])) {\n+            $protected_paths[] = $upload_dir['basedir'];\n+        }\n+\n+        foreach ($protected_paths as $protected_path) {\n+            $real_protected_path = $this->wpvivid_normalize_delete_path($protected_path, true);\n+            if ($real_protected_path === false) {\n+                continue;\n+            }\n+\n+            if ($real_site_path === $real_protected_path) {\n+                return false;\n+            }\n+\n+            if (strpos($real_protected_path . '\u002F', $real_site_path . '\u002F') === 0) {\n+                return false;\n+            }\n+        }\n+\n+        $tasks = get_option('wpvivid_staging_task_list', array());\n+        if (is_array($tasks)) {\n+            foreach ($tasks as $task) {\n+                $allowed_paths = array();\n+\n+                if (!empty($task['path']['des_path'])) {\n+                    $allowed_paths[] = $task['path']['des_path'];\n+                }\n+\n+                if (!empty($task['site']['path'])) {\n+                    $allowed_paths[] = $task['site']['path'];\n+                }\n+\n+                foreach ($allowed_paths as $allowed_path) {\n+                    $real_allowed_path = $this->wpvivid_normalize_delete_path($allowed_path, true);\n+                    if ($real_allowed_path !== false && $real_site_path === $real_allowed_path) {\n+                        return true;\n+                    }\n+                }\n+            }\n+        }\n+\n+        $cleanup = $this->wpvivid_get_cancel_staging_cleanup($site_path);\n+        if ($cleanup !== false) {\n+            return true;\n+        }\n+\n+        return false;\n+    }\n+\n     public function delete_cancel_staging_site(){\n         global $wpvivid_plugin;\n         check_ajax_referer( 'wpvivid_ajax', 'nonce' );\n@@ -1285,9 +1405,18 @@\n                 $db_name = $staging_site_info['staging_additional_db_name'];\n                 $db_host = $staging_site_info['staging_additional_db_host'];\n                 if (!empty($site_path)) {\n-                    $home_path = untrailingslashit(ABSPATH);\n-                    if ($home_path != $site_path) {\n-                        if (file_exists($site_path)) {\n+                    if (file_exists($site_path)) {\n+                        if (!$this->wpvivid_is_allowed_staging_delete_path($site_path)) {\n+                            echo wp_json_encode(array(\n+                                'result' => 'failed',\n+                                'error'  => 'Invalid staging path.'\n+                            ));\n+                            die();\n+                        }\n+\n+                        $home_path = untrailingslashit(ABSPATH);\n+                        if ($home_path != $site_path) {\n+\n                             if (!class_exists('WP_Filesystem_Base')) include_once(ABSPATH . '\u002Fwp-admin\u002Fincludes\u002Fclass-wp-filesystem-base.php');\n                             if (!class_exists('WP_Filesystem_Direct')) include_once(ABSPATH . '\u002Fwp-admin\u002Fincludes\u002Fclass-wp-filesystem-direct.php');\n \n@@ -1311,6 +1440,11 @@\n                     }\n                 }\n \n+                $normalized_path = $this->wpvivid_normalize_delete_path($site_path, false);\n+                if ($normalized_path !== false) {\n+                    delete_transient('wpvivid_cancel_staging_cleanup_' . md5($normalized_path));\n+                }\n+\n                 $ret['result'] = 'success';\n                 echo wp_json_encode($ret);\n             }\n@@ -1751,6 +1885,27 @@\n                         $ret['staging_table_prefix']=$value['db_connect']['new_prefix'];\n                     }\n                 }\n+\n+                if (!empty($ret['staging_path'])) {\n+                    $normalized_cleanup_path = $this->wpvivid_normalize_delete_path($ret['staging_path'], false);\n+\n+                    if ($normalized_cleanup_path !== false) {\n+                        set_transient(\n+                            'wpvivid_cancel_staging_cleanup_' . md5($normalized_cleanup_path),\n+                            array(\n+                                'staging_path' => $ret['staging_path'],\n+                                'staging_table_prefix' => isset($ret['staging_table_prefix']) ? $ret['staging_table_prefix'] : '',\n+                                'staging_additional_db' => isset($ret['staging_additional_db']) ? $ret['staging_additional_db'] : 0,\n+                                'staging_additional_db_user' => isset($ret['staging_additional_db_user']) ? $ret['staging_additional_db_user'] : '',\n+                                'staging_additional_db_pass' => isset($ret['staging_additional_db_pass']) ? $ret['staging_additional_db_pass'] : '',\n+                                'staging_additional_db_name' => isset($ret['staging_additional_db_name']) ? $ret['staging_additional_db_name'] : '',\n+                                'staging_additional_db_host' => isset($ret['staging_additional_db_host']) ? $ret['staging_additional_db_host'] : ''\n+                            ),\n+                            10 * MINUTE_IN_SECONDS\n+                        );\n+                    }\n+                }\n+\n                 update_option('wpvivid_staging_task_cancel', false, 'no');\n                 $b_delete=true;\n             }","The exploit requires an authenticated user with Administrator privileges. 1. The attacker obtains a valid WordPress nonce for the 'wpvivid_ajax' action, typically found localized in the plugin's staging settings page. 2. The attacker triggers the 'wp_ajax_wpvivid_delete_cancel_staging_site' AJAX action. 3. In vulnerable versions, the plugin attempts to clean up staging sites by deleting the folder specified in the staging site configuration or transient data. 4. By manipulating the staging site path information (either through a previous configuration step or by influencing the input variables), the attacker can specify an arbitrary directory path (e.g., '\u002Fwp-content\u002Fplugins\u002F'). 5. The plugin recursively deletes the target directory because it only checks if the path is strictly different from the site's home path (ABSPATH), allowing for a full loss of data for that directory.","gemini-3-flash-preview","2026-06-26 03:49:20","2026-06-26 03:49:58",{"type":37,"vulnerable_version":38,"fixed_version":11,"vulnerable_browse":39,"vulnerable_zip":40,"fixed_browse":41,"fixed_zip":42,"all_tags":43},"plugin","0.9.128","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpvivid-backuprestore\u002Ftags\u002F0.9.128","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwpvivid-backuprestore.0.9.128.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpvivid-backuprestore\u002Ftags\u002F0.9.129","https:\u002F\u002Fdownloads.wordpress.org\u002Fplugin\u002Fwpvivid-backuprestore.0.9.129.zip","https:\u002F\u002Fplugins.trac.wordpress.org\u002Fbrowser\u002Fwpvivid-backuprestore\u002Ftags"]