On August 14th, 2026, we received a submission for an Unauthenticated Second-Order SQL Injection vulnerability in All-in-One WP Migration and Backup, a WordPress plugin with more than 5 million active installations. This vulnerability makes it possible for unauthenticated attackers to inject SQL that is later executed when a site administrator performs an archive restore, which can be used to leak the plugin’s secret key and ultimately achieve remote code execution, leading to complete site takeover.
Props to Jack Taylor who discovered and responsibly reported this vulnerability through the Wordfence Bug Bounty Program. This researcher earned a bounty of $6,400.00 for this discovery. Our mission is to secure WordPress through defense in depth, which is why we are investing in quality vulnerability research and collaborating with researchers of this caliber through our Bug Bounty Program. We are committed to making the WordPress ecosystem more secure through the detection and prevention of vulnerabilities, which is a critical element to the multi-layered approach to security.
Wordfence Premium, Wordfence Care, and Wordfence Response users received a firewall rule to protect against known exploits targeting this vulnerability in All-in-One WP Migration and Backup, hosted on WordPress.org, on August 16, 2026. Sites using the free version of Wordfence will receive the same protection 30 days later on September 15, 2026.
We provided full disclosure details to the ServMask team through our Wordfence Vulnerability Management Portal on August 15, 2026. The developer acknowledged the report on August 17, 2026, and released the fully patched version on August 20, 2026. We would like to commend the ServMask team for their prompt response and timely patch.
We urge users to update their sites to the latest patched version of All-in-One WP Migration and Backup, version 7.110 at the time of this publication, as soon as possible.
Vulnerability Summary from Wordfence Intelligence
Technical Analysis
All-in-One WP Migration and Backup is a popular WordPress plugin for creating backups and migrating sites. It packages a site into a proprietary .wpress archive containing the site’s files and a database dump, and restores that archive on a destination server. During import, the plugin rewrites source URLs and database table prefixes inside each SQL statement before executing them. The plugin’s unauthenticated import action is protected by a secret key stored in the ai1wm_secret_key option, and the plugin deliberately saves and restores this option around each database-restore pass so that it is not overwritten by the imported data.
This vulnerability is a second-order SQL injection, meaning the malicious input is stored during one action and only becomes dangerous when it is processed later during a different action.
The malicious input is planted through WordPress core’s trackback functionality, which does not require authentication. An unauthenticated attacker submits two trackbacks to a public post that accepts pings. In each trackback, the blog name ends with a trailing backslash and the URL carries a crafted payload. WordPress core stores these values in the comments table as the comment author and comment author URL without stripping the backslash or rejecting the URL.
The data only becomes dangerous once a site administrator exports and then imports the site, which is a normal operation for this backup and migration plugin. During import, the plugin rewrites URLs and table prefixes in the stored SQL using the replace_table_values() function in the Ai1wm_Database class, which relies on a regular expression to identify quoted string literals:
// Replace serialized values
foreach ( $this->get_old_replace_values() as $old_value ) {
if ( strpos( $input, $this->escape( $old_value ) ) !== false ) {
$input = preg_replace_callback( "/'(.*?)(?<!\\\\)'/S", array( $this, 'replace_table_values_callback' ), $input );
break;
}
}
The critical flaw is in the regular expression used to match string literals. The negative lookbehind only checks the single byte immediately before a closing quote, rather than counting the full run of backslashes preceding it. A closing quote preceded by two backslashes represents an even-length run, which is the true end of the string, but the regex treats it as an escaped quote and continues matching into the following literal. This is why the planted trailing backslash matters: after the exporter has correctly doubled it, the regex misinterprets the string boundary.
The matched value is then passed to the replace_table_values_callback() function, which unescapes the over-captured match, performs the URL replacement, and re-escapes the result:
protected function replace_table_values_callback( $matches ) {
// Unescape MySQL special characters
$matches[1] = Ai1wm_Database_Utility::unescape_mysql( $matches[1] );
// Replace serialized values
if ( strlen( $matches[1] ) >= $this->get_old_replace_values_min_length() ) {
$matches[1] = Ai1wm_Database_Utility::replace_serialized_values( $matches[1], $this->get_old_replace_values(), $this->get_new_replace_values() );
}
// Escape MySQL special characters
$matches[1] = Ai1wm_Database_Utility::escape_mysql( $matches[1] );
return "'" . $matches[1] . "'";
}
Because the value was over-captured due to the boundary confusion, this unescape-replace-re-escape cycle produces an unbalanced sequence of backslashes that flips the MySQL string boundary in the resulting SQL statement. The attacker-controlled portion of the stored comment is thereby promoted from data inside a string literal to executable SQL, which is run against the database during the restore.
The two planted rows work together. The first is designed to exceed the import pipeline’s time budget, which causes the restore to pause and commit the current transaction while recording its position in the file so that processing continues on a subsequent pass. Because the plugin restores the destination site’s ai1wm_secret_key into the options table at each pass boundary, the real secret key is present by the time the second row is processed. The second row’s payload reads the ai1wm_secret_key value out of the options table and writes it into the comment, marking the comment as approved so that it becomes publicly visible.
At this point, the attacker can simply read the leaked secret key from the site’s public comments REST API endpoint, with no authentication required.
Finally, the attacker leverages the leaked secret key to drive the plugin’s import action directly. The import controller is registered for unauthenticated access, and the only gate protecting it is a secret-key comparison:
public static function import( $params = array() ) {
global $ai1wm_params;
// Set params
if ( empty( $params ) ) {
$params = stripslashes_deep( array_merge( $_GET, $_POST ) );
}
// Set priority
if ( ! isset( $params['priority'] ) ) {
$params['priority'] = 10;
}
$ai1wm_params = $params;
// Set job ID for per-job status tracking
if ( isset( $params['storage'] ) ) {
Ai1wm_Status::$job_id = $params['storage'];
}
// Set secret key
$secret_key = null;
if ( isset( $params['secret_key'] ) ) {
$secret_key = trim( $params['secret_key'] );
}
ai1wm_setup_environment();
try {
// Ensure that unauthorized people cannot access import action
ai1wm_verify_secret_key( $secret_key );
Having obtained the leaked secret key, the attacker satisfies this check and drives the import pipeline with a crafted .wpress archive that contains a malicious must-use plugin. The plugin is extracted to the must-use plugins directory and executed on the next page load, resulting in remote code execution as the web server user.
As with all remote code execution vulnerabilities, this can lead to complete site compromise through the use of webshells and other techniques.
Important Note
We would like to draw attention to the fact that, while the attacker is unauthenticated throughout, this vulnerability requires a site administrator to perform an export followed by an import of the site after the malicious trackbacks have been planted. This export and import cycle is the action that causes the stored payload to be executed as SQL. Since backup and restore is the core purpose of this plugin, this is a routine action, but the injected SQL will not execute until it takes place.
Wordfence Firewall
The following graphic demonstrates the steps to exploitation an attacker might take and at which point the Wordfence firewall would block an attacker from successfully exploiting the vulnerability.
Disclosure Timeline
Vendor / external action
Conclusion
In this blog post, we detailed an Unauthenticated Second-Order SQL Injection vulnerability within the All-in-One WP Migration and Backup plugin affecting all versions up to, and including, 7.109. This vulnerability allows unauthenticated threat actors to plant a SQL injection payload that executes when an administrator restores an archive, which can be used to leak the plugin’s secret key and ultimately achieve remote code execution, leading to complete site compromise.
We encourage WordPress users to verify that their sites are updated to the latest patched version of All-in-One WP Migration and Backup as soon as possible considering the critical nature of this vulnerability.
Wordfence Premium, Wordfence Care, and Wordfence Response users received a firewall rule to protect against known exploits targeting this vulnerability in All-in-One WP Migration and Backup, hosted on WordPress.org, on August 16, 2026. Sites using the free version of Wordfence will receive the same protection 30 days later on September 15, 2026.
If you know someone who uses this plugin on their site, we recommend sharing this advisory with them to ensure their site remains secure, as this vulnerability poses a significant risk.
The post 5 Million WordPress Sites Affected by SQL Injection Vulnerability in All-in-One WP Migration and Backup WordPress Plugin appeared first on Wordfence.
