Atomic Edge Proof of Concept automated generator using AI diff analysis
Published : May 14, 2026

CVE-2026-4031: Database Backup for WordPress <= 2.5.2 – Missing Authorization to Unauthenticated Database Backup Interception (wp-db-backup)

CVE ID CVE-2026-4031
Plugin wp-db-backup
Severity High (CVSS 7.5)
CWE 862
Vulnerable Version 2.5.2
Patched Version 2.5.3
Disclosed May 12, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-4031: This vulnerability affects the Database Backup for WordPress plugin (versions <= 2.5.2) and allows unauthenticated attackers to intercept database backups by manipulating the backup directory path. The vulnerability carries a CVSS score of 7.5 due to its low complexity and high impact on data confidentiality.

The root cause lies in the constructor method of the wpdbBackup class (wp-db-backup.php, lines 122-128 in the vulnerable version). The plugin directly reads the 'wp_db_temp_dir' parameter from the $_GET superglobal without any authorization check. If the parameter is set and the directory is writable, it overwrites the backup directory path. An attacker can set this parameter to point to a publicly accessible directory like wp-content/uploads/. The backup file is generated with a predictable naming convention using DB_NAME, table prefix, date, and Swatch Internet Time, making interception straightforward. Lines 315-316 also expose the backup_dir in the JavaScript fragment URL sent to the backup iframe.

Exploitation begins with an attacker sending a GET request to wp-cron.php with the wp_db_temp_dir parameter set to a publicly writable directory, such as /wp-content/uploads/. The attacker must also trigger the scheduled backup process (or wait for it to execute). The backup file is written to the attacker-controlled directory with a predictable name based on the database name, table prefix, date, and Swatch Internet Time. The attacker then accesses the backup file via its predictable URL before it is cleaned up by the plugin, thereby obtaining the full database dump including credentials, user data, and PII.

The patch, introduced in version 2.5.3, removes the entire block that processes the 'wp_db_temp_dir' parameter from the $_GET (lines 122-128 are deleted). The backup directory is now solely determined by the get_temp_dir() function and the wp_db_b_backup_dir filter, both of which are server-side controlled. Additionally, the patch adds a random 12-character nonce to the backup filename via wp_generate_password, making the filename unpredictable. The patch also changes authorization checks in fragment and backup handlers to early-return on failure (lines 148-161), preventing unauthorized access to backup operations. Several variable scopes are changed from public to private to further restrict access.

Successful exploitation leads to complete disclosure of the WordPress database, including all site content, user credentials (password hashes), email addresses, and any personally identifiable information stored in the database. An attacker could use the exposed credentials for account takeover, privilege escalation, or further compromise of the site and its users. The backup file may also contain sensitive configuration data and secrets.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/wp-db-backup/wp-db-backup.php
+++ b/wp-db-backup/wp-db-backup.php
@@ -5,7 +5,7 @@
 Description: On-demand backup of your WordPress database. Navigate to <a href="edit.php?page=wp-db-backup">Tools → Backup</a> to get started.
 Author: Delicious Brains
 Author URI: https://deliciousbrains.com
-Version: 2.5.2
+Version: 2.5.3
 Domain Path: /languages

 This program is free software; you can redistribute it and/or modify
@@ -43,15 +43,17 @@

 class wpdbBackup {

-	var $backup_complete = false;
-	var $backup_file     = '';
-	var $backup_filename;
-	var $core_table_names = array();
-	var $errors           = array();
-	var $basename;
-	var $page_url;
-	var $referer_check_key;
-	var $version = '2.5.2';
+	private $backup_complete = false;
+	private $backup_file     = '';
+	private $backup_filename;
+	private $core_table_names = array();
+	private $errors           = array();
+	private $backup_dir;
+	private $basename;
+	private $fp;
+	private $page_url;
+	private $referer_check_key;
+	private $version = '2.5.3';

 	function module_check() {
 		$mod_evasive = false;
@@ -87,9 +89,11 @@
 		add_filter( 'cron_schedules', array( &$this, 'add_sched_options' ) );
 		add_filter( 'wp_db_b_schedule_choices', array( &$this, 'schedule_choices' ) );

-		$table_prefix          = ( isset( $table_prefix ) ) ? $table_prefix : $wpdb->prefix;
-		$datum                 = date( 'Ymd_B' );
-		$this->backup_filename = DB_NAME . "_$table_prefix$datum.sql";
+		$table_prefix = ( isset( $table_prefix ) ) ? $table_prefix : $wpdb->prefix;
+		$datum        = date( 'Ymd_B' );
+		$nonce        = wp_generate_password( 12, false );
+
+		$this->backup_filename = sanitize_text_field( DB_NAME . '_' . $table_prefix . $datum . '_' . $nonce . '.sql' );

 		$possible_names = array(
 			'categories',
@@ -118,13 +122,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';

@@ -151,10 +148,14 @@
 					break;
 			}
 		} elseif ( isset( $_GET['fragment'] ) ) {
-			$this->can_user_backup( 'frame' );
+			if ( ! $this->can_user_backup( 'frame' ) ) {
+				return;
+			}
 			add_action( 'init', array( &$this, 'init' ) );
 		} elseif ( isset( $_GET['backup'] ) ) {
-			$this->can_user_backup();
+			if ( ! $this->can_user_backup() ) {
+				return;
+			}
 			add_action( 'init', array( &$this, 'init' ) );
 		} else {
 			add_action( 'admin_menu', array( &$this, 'admin_menu' ) );
@@ -314,7 +315,7 @@

 			function backup(table, segment) {
 				var fram = document.getElementById("backuploader");
-				fram.src = "' . $this->page_url . '&fragment=" + table + ":" + segment + ":' . $this->backup_filename . ':&wp_db_temp_dir=' . $this->backup_dir . '";
+				fram.src = "' . $this->page_url . '&fragment=" + table + ":" + segment + ":' . $this->backup_filename . ':";
 			}

 			var curStep = 0;
@@ -421,7 +422,7 @@
 			}
 		}

-		if ( is_writable( $this->backup_dir ) ) {
+		if ( wp_is_writable( $this->backup_dir ) ) {
 			$this->fp = $this->open( $this->backup_dir . $filename, 'a' );
 			if ( ! $this->fp ) {
 				$this->error( __( 'Could not open the backup file for writing!', 'wp-db-backup' ) );
@@ -691,6 +692,10 @@
 	 * Taken from phpMyAdmin.
 	 */
 	function sql_addslashes( $a_string = '', $is_like = false ) {
+		if ( empty( $a_string ) ) {
+			return $a_string;
+		}
+
 		if ( $is_like ) {
 			$a_string = str_replace( '\', '\\\\', $a_string );
 		} else {
@@ -933,6 +938,8 @@
 								// yet try to avoid quotation marks around integers
 								$value    = ( null === $value || '' === $value ) ? $defs[ strtolower( $key ) ] : $value;
 								$values[] = ( '' === $value ) ? "''" : $value;
+							} elseif ( empty( $value ) ) {
+								$values[] = "'" . $value . "'";
 							} else {
 								$values[] = "'" . str_replace( $search, $replace, $this->sql_addslashes( $value ) ) . "'";
 							}
@@ -957,7 +964,7 @@
 	function db_backup( $core_tables, $other_tables ) {
 		global $table_prefix, $wpdb;

-		if ( is_writable( $this->backup_dir ) ) {
+		if ( wp_is_writable( $this->backup_dir ) ) {
 			$this->fp = $this->open( $this->backup_dir . $this->backup_filename );
 			if ( ! $this->fp ) {
 				$this->error( __( 'Could not open the backup file for writing!', 'wp-db-backup' ) );
@@ -1096,8 +1103,18 @@
 				$recipient = get_option( 'admin_email' );
 			}

-			$message = sprintf( __( "Attached to this email isn   %1$1sn   Size:%2$2s kilobytesn", 'wp-db-backup' ), $filename, round( filesize( $file_to_deliver ) / 1024 ) );
-			$success = $this->send_mail( $recipient, get_bloginfo( 'name' ) . ' ' . __( 'Database Backup', 'wp-db-backup' ), $message, $file_to_deliver );
+			$message   = sprintf(
+				__( "Attached to this email isn   %1$1sn   Size:%2$2s kilobytesn", 'wp-db-backup' ),
+				$filename,
+				round( filesize( $file_to_deliver ) / 1024 )
+			);
+			$blog_name = sanitize_text_field( html_entity_decode( get_bloginfo( 'name' ) ) );
+			$success   = $this->send_mail(
+				$recipient,
+				$blog_name . ' ' . __( 'Database Backup', 'wp-db-backup' ),
+				$message,
+				$file_to_deliver
+			);

 			if ( false === $success ) {
 				$msg = __( 'The following errors were reported:', 'wp-db-backup' ) . "n ";
@@ -1219,7 +1236,7 @@
 			<?php
 			// not writable due to write permissions
 			$whoops = true;
-		} elseif ( ! is_writable( $this->backup_dir ) && ! @chmod( $this->backup_dir, $dir_perms ) ) {
+		} elseif ( ! wp_is_writable( $this->backup_dir ) && ! @chmod( $this->backup_dir, $dir_perms ) ) {
 			?>
 			<div class="wp-db-backup-updated error inline">
 				<p><?php _e( 'WARNING: Your backup directory is <strong>NOT</strong> writable! We cannot create the backup files.', 'wp-db-backup' ); ?></p>
@@ -1630,6 +1647,16 @@

 		// make sure WPMU users are site admins, not ordinary admins
 		if ( function_exists( 'is_site_admin' ) && ! is_site_admin() ) {
+			$this->error(
+				array(
+					'loc'  => $loc,
+					'kind' => 'fatal',
+					'msg'  => __(
+						'You are not allowed to perform backups.',
+						'wp-db-backup'
+					),
+				)
+			);
 			return false;
 		}

ModSecurity Protection Against This CVE

Here you will find our ModSecurity compatible rule to protect against this particular CVE.

ModSecurity
# Atomic Edge WAF Rule - CVE-2026-4031
SecRule REQUEST_URI "@streq /wp-cron.php" 
    "id:20261994,phase:2,deny,status:403,chain,msg:'CVE-2026-4031 - Database Backup for WordPress - Unauthenticated Backup Directory Manipulation',severity:'CRITICAL',tag:'CVE-2026-4031'"
    SecRule ARGS_GET:wp_db_temp_dir "@rx .+" "t:none,chain"
        SecRule REQUEST_METHOD "@streq GET" "t:none"

Frequently Asked Questions

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Blac&kMcDonaldCovenant House TorontoAlzheimer Society CanadaUniversity of TorontoHarvard Medical School