wp-content » plugins » Backuply SQL Injection (CVE-2026-93953): Update & Check Your Site

Backuply SQL Injection (CVE-2026-93953): Update & Check Your Site

If you use the Backuply plugin (Backup, Restore, Migrate and Clone), update it today. Versions 1.5.8 and earlier have a second-order SQL injection, CVE-2026-93953. A user with only Subscriber access can plant SQL in their profile, and it runs the next time an administrator restores a backup. Wordfence disclosed it on October 15, 2026. The plugin has 600,000+ active installs, so this affects a lot of membership sites, WooCommerce stores, and any other site with open registration.

As of 2026-10-21, the reports I found say there’s no public proof-of-concept and no confirmed exploitation in the wild. That can change quickly, and this bug sits quietly inside your backups until someone hits Restore. Below are the fix, a way to check whether a payload is already planted, and how to make sure an old backup can’t hand someone admin access.

Version 1.5.9 came out on October 1, 2026. Its changelog on WordPress.org lists “Fixed a security issue in the restoration process, reported by daroo (Wordfence)”. You can update from Plugins → Installed Plugins, or with WP-CLI:

# check the installed version
wp plugin get backuply --field=version

# update to the patched release
wp plugin update backuply

# if you manage many sites, check them all
for d in /var/www/*/; do echo "$d: $(wp --path="$d" plugin get backuply --field=version 2>/dev/null)"; done

Anything below 1.5.9 is vulnerable. If you aren’t using Backuply and it’s only sitting there inactive, delete it: wp plugin delete backuply.

How the attack works (and why updating alone isn’t the whole story)

According to the SecurityOnline write-up and the CVE-2026-93953 record, the chain goes like this:

  1. A subscriber puts a crafted payload in the Biographical Info field of their profile. That’s the description key in wp_usermeta.
  2. Backuply writes that value unescaped into the SQL dump the next time the site is backed up.
  3. An administrator restores that backup. A loose regex in the importer (it looks for backuply_version) matches the attacker’s text, and the text is concatenated into an UPDATE query.
  4. The injected SQL sets users_can_register to 1 and default_role to administrator. The attacker then signs up through the normal registration form and gets an admin account.

Notice the timing. The payload goes in while you’re on a vulnerable version, but it only fires on a restore. The fix is in the restoration process, so restoring with 1.5.9 should be safe. Still, any backup archive made before the update can hold a planted payload. That backup could be restored with an old copy of the plugin, or imported somewhere else by hand. You need to check both the live site and those old archives.

If you restored a Backuply backup recently, check these two options first. This is exactly what the payload changes:

wp option get users_can_register
wp option get default_role

On most sites default_role should be subscriber (or customer on WooCommerce). If it says administrator, treat the site as compromised. Back up the current state first so you have evidence, then reset the options:

wp db export before-cleanup-$(date +%F).sql
wp option update default_role subscriber
wp option update users_can_register 0   # only if you don't need open registration
wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

Look for admins created around or after your last restore. If you find one you don’t recognize, don’t just delete it. Follow my complete guide to cleaning up a hacked WordPress site, because an attacker with admin access can upload plugins and drop backdoors. Then reset the passwords of your real admins with wp user reset-password.

Real bios almost never contain SQL keywords or the string backuply_version. This read-only query lists suspicious description values. It uses wp db prefix, so custom table prefixes work too:

P=$(wp db prefix)
wp db query "SELECT um.user_id, u.user_login, LEFT(um.meta_value, 150) AS bio
FROM ${P}usermeta um JOIN ${P}users u ON u.ID = um.user_id
WHERE um.meta_key = 'description'
AND (um.meta_value LIKE '%backuply_version%'
  OR um.meta_value LIKE '%default_role%'
  OR um.meta_value LIKE '%users_can_register%'
  OR um.meta_value REGEXP 'UPDATE|INSERT|DELETE|;');"

The ; pattern will catch some harmless bios, so review the results by hand. If you find a real payload, take a database backup first, then clear that user’s bio and look into the account:

wp user meta update 123 description ""

Treat every Backuply archive made with version 1.5.8 or older as untrusted until you’ve checked it. Extract a copy somewhere outside the web root and search the SQL dump for user bios that contain the trigger string:

grep -n "'description'" backup.sql | grep -i "backuply_version"

No output means that string isn’t in any bio row. If you get a hit, don’t restore that archive with Backuply. Use a different backup, or clean the row first. Also, never leave extracted archives inside the document root. I explained why in Never, EVER leave any backups inside your domain document root!

See also  Purge cache from W3 Total Cache plugin using WP-CLI

The payload works by editing two options in the database. You can make those database values irrelevant with a small must-use plugin. Save this as wp-content/mu-plugins/lock-registration.php:

<?php
/**
 * Plugin Name: Lock registration options
 * Description: Ignores database values for default_role and users_can_register.
 */

// New users always get this role, whatever wp_options says.
add_filter( 'pre_option_default_role', function () {
    return 'subscriber'; // use 'customer' on WooCommerce stores
} );

// Remove this filter if your site needs open registration.
add_filter( 'pre_option_users_can_register', function () {
    return '0';
} );

The pre_option_{$option} filter runs before WordPress reads the option from the database. Even if a restore writes administrator into default_role, get_option() keeps returning your hardcoded value. Because it’s a mu-plugin, nobody can deactivate it from wp-admin. Also make sure your site’s own registration setting matches what you actually need. Most brochure sites don’t need open registration.

A few more habits that help:

  • After any restore, from any plugin, run the two wp option get checks above and list your admins.
  • Enable auto-updates for your backup plugin: wp plugin auto-updates enable backuply. A backup plugin has full database access, so update it as quickly as you’d update a security plugin.

This bug is a good argument for not relying on a plugin as your only backup and restore path. In OpenPanel, the Backups feature dumps MySQL/MariaDB with mysqldump/mariadb-dump and archives website files outside WordPress. You can restore a single database or individual files from a chosen backup, and single databases can be downloaded as .sql files so you can grep them before restoring. The WordPress Manager also has a registration toggle and a vulnerability scan of plugins and themes. That makes it quick to spot an outdated Backuply and confirm registration is off. On cPanel, you’d get the same result with the Backup tool (or whatever backup system your host adds) plus phpMyAdmin. The key point is the same on both: the restore doesn’t run through the vulnerable plugin code.

If you’re not sure whether something already got in, go through my checklist on how to identify if your WordPress website is hacked.

Backuply 1.5.8 and earlier (CVE-2026-93953) lets a subscriber plant SQL in their bio, and it runs when an admin restores a backup. The payload turns on registration and sets the default role to administrator. Update to 1.5.9 with wp plugin update backuply. Check default_role and users_can_register, list your administrators, and search user bios and old backup archives for backuply_version. Add the mu-plugin above so a bad restore can never make new signups admins.

Was this post helpful?

Leave a Comment

I enjoy constructive responses and professional comments to my posts, and invite anyone to comment or link to my site.

Recommended