Database Backup for WordPress <= 2.5.2 - Missing Authorization to Unauthenticated Database Backup Interception
Description
The Database Backup for WordPress plugin for WordPress is vulnerable to authorization bypass in all versions up to, and including, 2.5.2. This is due to the plugin not restricting access to the wp_db_temp_dir parameter, which controls where database backups are written. This makes it possible for unauthenticated attackers to send a request to wp-cron.php with a poisoned wp_db_temp_dir value pointing to a publicly accessible directory (e.g., wp-content/uploads/), and if a scheduled backup is due, intercept the backup file before it is cleaned up. The backup file has a predictable name based on the database name, table prefix, date, and Swatch Internet Time, making interception reliable. Successful exploitation leads to Sensitive Information Exposure including database credentials, user password hashes, and personally identifiable information. This vulnerability requires that the site administrator has configured scheduled backups.
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 SVNThis research plan outlines the steps to verify and exploit **CVE-2026-4031**, a missing authorization vulnerability in the **Database Backup for WordPress** plugin. ### 1. Vulnerability Summary The `wp-db-backup` plugin (up to version 2.5.2) allows unauthenticated users to influence the directory …
Show full research plan
This research plan outlines the steps to verify and exploit CVE-2026-4031, a missing authorization vulnerability in the Database Backup for WordPress plugin.
1. Vulnerability Summary
The wp-db-backup plugin (up to version 2.5.2) allows unauthenticated users to influence the directory where scheduled database backups are temporarily stored. This occurs because the plugin's constructor in wp-db-backup.php reads the wp_db_temp_dir parameter from the global $_GET array without performing any capability or authentication checks. If an attacker provides a path to a publicly accessible directory (like wp-content/uploads/), a scheduled backup triggered via wp-cron.php will be written to that location. Because the backup filename is predictable, the attacker can then download the entire database dump.
2. Attack Vector Analysis
- Target Endpoint:
[WP_URL]/wp-cron.php - Vulnerable Parameter:
wp_db_temp_dir(passed via GET) - Authentication: None (Unauthenticated)
- Preconditions:
- The site administrator must have configured a "Scheduled Backup" (found in Tools > Backup).
- The attacker must know or guess the absolute path to a writable, public directory (e.g.,
/var/www/html/wp-content/uploads/). - A scheduled backup must be due for execution when the attacker triggers
wp-cron.php.
3. Code Flow
- Entry Point: A request is made to any WordPress page (specifically
wp-cron.php) with?wp_db_temp_dir=/path/to/public/dir. - Initialization: The
wpdbBackupclass is instantiated. In the constructor (wp-db-backup.phpline 135):$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 ) ) { // No auth check here! $tmp_dir = $requested_dir; } } $this->backup_dir = trailingslashit( apply_filters( 'wp_db_b_backup_dir', $tmp_dir ) ); - Cron Trigger:
wp-cron.phpchecks for pending tasks. Ifwp_db_backup_cronis due, it fires. - Backup Execution: The
cron_backup()method (hooked at line 115) runs. It uses$this->backup_dirto store the generated SQL file. - Filename Generation: The filename is generated in the constructor (lines 124-125):
$datum = date( 'Ymd_B' ); $this->backup_filename = DB_NAME . "_$table_prefix$datum.sql";DB_NAME: Database name (e.g.,wordpress).$table_prefix: WordPress table prefix (e.g.,wp_).date('Ymd_B'):Ymdis the date,Bis the Swatch Internet Time (000-999).
4. Nonce Acquisition Strategy
This specific vulnerability does not require a nonce.
The poisoned parameter is processed in the class constructor (__construct), which runs on every request including unauthenticated cron triggers. While the "Scheduled Backup" form was patched with a nonce in 2.5.2 to prevent CSRF, the consumption of the wp_db_temp_dir parameter in the constructor remains unauthenticated in affected versions.
5. Exploitation Strategy
Step 1: Reconnaissance
Identify the database name and table prefix.
- Standard defaults are
DB_NAME=wordpressand$table_prefix=wp_. - Determine the current Swatch Internet Time (
date('B')).
Step 2: Poison the Backup Path & Trigger Cron
Send a request to wp-cron.php supplying the poisoned directory and triggering the cron runner.
- Request:
GET /wp-cron.php?doing_wp_cron&wp_db_temp_dir=/var/www/html/wp-content/uploads/ - Note: The absolute path is required. In standard Docker environments, this is usually
/var/www/html/wp-content/uploads/.
Step 3: Predict the Filename
The file will be named: [DB_NAME]_[PREFIX][YYYYMMDD]_[SWATCH_TIME].sql.
Example: wordpress_wp_20231027_123.sql.
- Since Swatch time represents "beats" (1000 per day), you may need to check the current beat and the next 2-3 beats to account for execution time.
Step 4: Intercept the Backup
Immediately attempt to download the file from the public uploads directory.
- Request:
GET /wp-content/uploads/wordpress_wp_20231027_123.sql
6. Test Data Setup
- Plugin Installation: Install and activate
wp-db-backupversion 2.5.2. - Configuration:
- Navigate to Tools > Backup.
- Under "Scheduled Backup", select "Once Daily" and enter an email address.
- Click "Save Settings".
- Cron Sync: Ensure a backup task is actually scheduled:
wp cron event list --name=wp_db_backup_cron - Force Due: To make the exploit reliable for testing, set the next run time to the past:
wp cron event run wp_db_backup_cron --due-now
7. Expected Results
- The request to
wp-cron.phpshould return a200 OK(empty response). - A file should appear in
wp-content/uploads/containing the full MySQL database export. - The content of the SQL file will include the
wp_userstable with admin password hashes and thewp_optionstable.
8. Verification Steps
- Verify File Creation: Use
lsinside the environment to check if the file exists in the uploads directory:ls -la /var/www/html/wp-content/uploads/*.sql - Verify Content: Check the head of the SQL file for database structure:
head /var/www/html/wp-content/uploads/[FILENAME].sql
9. Alternative Approaches
- Swatch Brute-force: If the exact time of the cron execution is slightly off, iterate through 10 beats (approx. 14 minutes) of Swatch time filenames.
- Directory Listing: If the server has directory listing enabled on
wp-content/uploads/, simply navigate to that URL to see the generated file. - Path Discovery: If
/var/www/html/is incorrect, common WordPress paths include/home/user/public_html/wp-content/uploads/or using theis_writeablecheck to confirm paths via error-based side channels ifWP_DEBUGis on.
Summary
The Database Backup for WordPress plugin allows unauthenticated users to specify the directory where scheduled database backups are temporarily stored via the 'wp_db_temp_dir' parameter. By pointing this to a publicly accessible directory, an attacker can intercept and download the resulting database dump, which uses a predictable filename based on the database name, prefix, and current time.
Vulnerable Code
// wp-db-backup.php lines 120-121 $datum = date( 'Ymd_B' ); $this->backup_filename = DB_NAME . "_$table_prefix$datum.sql"; --- // wp-db-backup.php lines 146-153 $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; } }
Security Fix
@@ -146,9 +146,2 @@ $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; - } - } -
Exploit Outline
1. Identify a writeable, publicly accessible directory on the target server (e.g., /var/www/html/wp-content/uploads/). 2. Determine the target's database name and table prefix (defaults: 'wordpress', 'wp_'). 3. Send an unauthenticated GET request to wp-cron.php with the poisoned parameter: ?doing_wp_cron&wp_db_temp_dir=[absolute_path_to_public_dir]. 4. Trigger the WordPress cron system. If a scheduled backup task is due, the plugin will save the SQL dump to the specified public directory. 5. Predict the backup filename (DBNAME_PREFIX_YYYYMMDD_SWATCHTIME.sql) and download the file from the public directory.
Check if your site is affected.
Run a free security audit to detect vulnerable plugins, outdated versions, and misconfigurations.