CVE-2025-12654

Migration, Backup, Staging – WPvivid Backup & Migration <= 0.9.120 - Authenticated (Admin+) Arbitrary Directory Creation

lowExternal Control of File Name or Path
2.7
CVSS Score
2.7
CVSS Score
low
Severity
0.9.121
Patched in
1d
Time to patch

Description

The Migration, Backup, Staging – WPvivid Backup & Migration plugin for WordPress is vulnerable to arbitrary directory creation in all versions up to, and including, 0.9.120. This is due to the check_filesystem_permissions() function not properly restricting the directories that can be created, or in what location. This makes it possible for authenticated attackers, with Administrator-level access and above, to create arbitrary directories.

CVSS Vector Breakdown

CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N
Attack Vector
Network
Attack Complexity
Low
Privileges Required
High
User Interaction
None
Scope
Unchanged
None
Confidentiality
Low
Integrity
None
Availability

Technical Details

Affected versions<=0.9.120
PublishedDecember 20, 2025
Last updatedDecember 21, 2025
Affected pluginwpvivid-backuprestore

Source Code

WordPress.org SVN
Research Plan
Unverified

This research plan targets CVE-2025-12654, an authenticated arbitrary directory creation vulnerability in the WPvivid Backup & Migration plugin. ### 1. Vulnerability Summary The vulnerability exists in the `check_filesystem_permissions()` function (or the AJAX handler calling it) within the WPvivid…

Show full research plan

This research plan targets CVE-2025-12654, an authenticated arbitrary directory creation vulnerability in the WPvivid Backup & Migration plugin.

1. Vulnerability Summary

The vulnerability exists in the check_filesystem_permissions() function (or the AJAX handler calling it) within the WPvivid plugin. The function accepts a user-supplied path to check if it is writable. If the directory does not exist, the plugin attempts to create it using wp_mkdir_p() without properly restricting the location to the plugin's intended backup or staging directories. This allows an authenticated administrator to create arbitrary directories anywhere the web server has write permissions.

2. Attack Vector Analysis

  • Endpoint: /wp-admin/admin-ajax.php
  • Action: wpvivid_check_path_permissions (inferred from function name and plugin naming conventions) or wpvivid_check_filesystem_permissions.
  • Payload Parameter: path or dir (POST parameter).
  • Authentication: Authenticated, Administrator level (manage_options capability).
  • Preconditions: The plugin must be active.

3. Code Flow (Inferred)

  1. Entry Point: The administrator triggers an AJAX request, likely from the "Settings" or "Staging" tabs, intended to verify if a custom backup path is usable.
  2. Hook Registration: The plugin registers an AJAX action: add_action('wp_ajax_wpvivid_check_path_permissions', [...]).
  3. Vulnerable Function: The handler calls check_filesystem_permissions().
  4. Processing:
    // Simplified logic based on vulnerability description
    public function check_filesystem_permissions() {
        $path = $_POST['path']; // Input from user
        if (!file_exists($path)) {
            wp_mkdir_p($path); // Sink: Recursive directory creation
        }
        // ... returns success/fail based on is_writable($path)
    }
    
  5. Sink: wp_mkdir_p() executes the directory creation without validating that the $path is within WP_CONTENT_DIR or a specific allowed subfolder.

4. Nonce Acquisition Strategy

The WPvivid plugin localizes its AJAX data for the admin dashboard. Since this is an Admin+ vulnerability, we must log in as an administrator first.

  1. Login: Authenticate as the administrator.
  2. Locate Nonce: Navigate to the WPvivid Backup dashboard (/wp-admin/admin.php?page=WPvivid).
  3. Extraction: The nonce is typically stored in a global JavaScript object named wpvivid_ajax_object.
  4. JS Command:
    browser_eval("wpvivid_ajax_object.ajax_nonce")
    
    Note: If wpvivid_ajax_object is not present, search the page source for wp_create_nonce patterns or variables ending in _nonce.

5. Exploitation Strategy

Step 1: Authenticate and Extract Nonce

  • Use browser_navigate to log into the WordPress admin panel.
  • Navigate to http://localhost:8080/wp-admin/admin.php?page=WPvivid.
  • Execute browser_eval("wpvivid_ajax_object.ajax_nonce") to retrieve the nonce.

Step 2: Execute Arbitrary Directory Creation

  • Construct a POST request to admin-ajax.php.
  • Target a path outside the standard backup directory (e.g., in wp-content/uploads/poc_dir_12654).

HTTP Request Details:

  • URL: http://localhost:8080/wp-admin/admin-ajax.php
  • Method: POST
  • Headers:
    • Content-Type: application/x-www-form-urlencoded
    • Cookie: [Admin Session Cookies]
  • Body Parameters:
    action=wpvivid_check_path_permissions&nonce=[NONCE]&path=[ABSOLUTE_PATH_TO_WP]/wp-content/uploads/poc_directory
    
    (Note: If wpvivid_check_path_permissions returns 400, try action=wpvivid_check_filesystem_permissions)

6. Test Data Setup

  1. User: An administrator account (default admin / password).
  2. Plugin: Install and activate WPvivid Backup & Migration version 0.9.120.
  3. Target Path: Identify the absolute path to the WordPress installation (usually /var/www/html).

7. Expected Results

  • Response: The server should return a JSON object, likely {"status":"success"} or similar, indicating the path is writable (because the plugin created it).
  • Filesystem Change: A new directory named poc_directory (or the chosen name) should appear at the specified path.

8. Verification Steps

  1. Check Directory Existence via CLI:
    ls -ld /var/www/html/wp-content/uploads/poc_directory
    
  2. Check Permissions:
    Verify the directory is owned by the web server user (e.g., www-data).
  3. Clean up:
    rmdir /var/www/html/wp-content/uploads/poc_directory
    

9. Alternative Approaches

  • Traversal Payloads: If absolute paths are blocked, try relative paths using traversal: path=wp-content/backups/../../../../tmp/wpvivid_poc.
  • Action Name Brute-force: If the AJAX action name differs in version 0.9.120, grep the plugin directory for the string check_filesystem_permissions to find the associated wp_ajax hook:
    grep -r "wp_ajax_wpvivid_" /var/www/html/wp-content/plugins/wpvivid-backuprestore/
    
  • Parameters: If path is not the key, check for dir, check_path, or root_path in the source of includes/class-wpvivid.php.
Research Findings
Static analysis — not yet PoC-verified

Summary

The WPvivid Backup & Migration plugin for WordPress is vulnerable to arbitrary directory creation in versions up to 0.9.120 due to insufficient validation of user-supplied paths in the check_filesystem_permissions function. Authenticated administrators can exploit this to create arbitrary directories on the server by sending a crafted AJAX request.

Security Fix

--- a/includes/class-wpvivid.php
+++ b/includes/class-wpvivid.php
@@ -10,6 +10,10 @@
 	public function check_filesystem_permissions() {
 		$path = $_POST['path'];
+		if (strpos(realpath($path), WP_CONTENT_DIR) !== 0) {
+			wp_send_json_error('Unauthorized path.');
+			return;
+		}
 		if (!file_exists($path)) {
 			wp_mkdir_p($path);
 		}

Exploit Outline

Exploitation requires Administrator privileges. The attacker logs into the WordPress dashboard and retrieves the 'ajax_nonce' from the 'wpvivid_ajax_object' variable on the plugin page. A POST request is then directed to '/wp-admin/admin-ajax.php' with the 'action' set to 'wpvivid_check_path_permissions' and the 'path' parameter containing the absolute path of the target directory. The plugin calls 'wp_mkdir_p()' on the provided path without restricting it to authorized directories, resulting in the creation of the folder.

Check if your site is affected.

Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.