← Back to Feed CACHED · 2026-10-19 16:28:30 · CACHE_KEY CVE-2026-93953
CVE-2026-93953 · CWE-89 · Disclosed 2026-10-16

Backuply WordPress plugin second-order SQL injection via Biographical Info field during backup restore

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

A booby-trapped diary entry that only explodes if someone photocopies the whole filing cabinet

CVE-2026-93953 is a second-order SQL injection in the Backuply plugin for WordPress (≤ 1.5.8, fixed in 1.5.9). An attacker with at least subscriber-level access plants a crafted SQL payload in their WordPress profile's *Biographical Info* field. Backuply exports this value verbatim — unescaped — into the wp_usermeta INSERT statement of the SQL dump it generates. During a subsequent backup restore, the plugin's regex-based table-prefix rewriting logic (/CREATE\s+TABLE\s+…options…/is) matches the attacker-controlled text inside the biography, concatenates it directly into an UPDATE query, and the injected SQL executes with full database privileges. The payload typically flips users_can_register to 1 and sets default_role to administrator in wp_options, letting the attacker register a new admin account via the standard WordPress form.

The vendor's HIGH / 8.0 rating is overweight for real-world conditions. CVSS accounts for needing authentication (PR:L) and user interaction (UI:R), but it cannot model the magnitude of the interaction: the 'user interaction' here is not clicking a link — it is an administrator performing a full database restore, an event that occurs once a year on active sites and never on most. The attacker has zero control over whether or when the restore happens. That makes this a time-bomb with no detonator, which fundamentally changes the risk calculus. On an install base of 600,000 sites, only a thin slice will restore a backup during the window between payload insertion and patching. No public PoC exists, EPSS is 0.34%, and there is no observed exploitation. The severity lands closer to MEDIUM.

"Second-order SQLi that only fires when an admin restores a backup. Patch on schedule, don't panic."
02 · The Attack Path

5 steps from start to impact.

STEP 01

Obtain subscriber account

The attacker registers a subscriber-level account on the target WordPress site. On sites with open registration (default in many WordPress installs), this is trivial — no social engineering needed. On closed-registration sites, the attacker needs compromised credentials or an invitation, which raises the bar significantly.
Conditions required:
  • Target site allows public user registration, or attacker already has subscriber credentials
Where this breaks in practice:
  • Many enterprise/business WordPress sites disable public registration
  • Sites behind SSO/IdP may not expose wp-login.php registration at all
Detection/coverage: Login/registration activity is logged by most WordPress security plugins (Wordfence, Sucuri, iThemes). Bulk registrations are easy to flag.
STEP 02

Plant SQL payload in Biographical Info

The attacker edits their WordPress profile and inserts a crafted SQL fragment into the *Biographical Info* textarea. The payload is designed to match Backuply's unanchored prefix-rewriting regex during a future restore. WordPress stores this harmlessly in wp_usermeta — the injection is dormant at this stage. No weaponized tooling is publicly available for this step as of October 2026.
Conditions required:
  • Subscriber can edit their own profile (default WordPress behavior)
Where this breaks in practice:
  • Payload must be precisely crafted to match the regex pattern — requires understanding the plugin's restore code
  • No public PoC or automated tool exists to generate the payload
Detection/coverage: Profile changes are logged by audit plugins (WP Activity Log, Simple History). A regex scan of usermeta description fields for SQL keywords would catch most payloads.
STEP 03

Backuply creates a backup containing the poisoned record

An administrator (or a scheduled cron job) triggers a Backuply database export. The plugin dumps wp_usermeta rows — including the attacker's biography — verbatim into the SQL file. The payload is now embedded in the backup archive. No additional attacker action is required.
Conditions required:
  • Backuply ≤ 1.5.8 is installed and actively used for backups
  • A backup is taken *after* the payload was planted
Where this breaks in practice:
  • If the site uses a different backup plugin (UpdraftPlus, BlogVault, VaultPress, etc.), this path is dead
  • Backup frequency varies wildly — some sites back up daily, others monthly or manually
STEP 04

Administrator restores the poisoned backup

An admin initiates a database restore through Backuply's UI. During restoration, the plugin's unanchored regex scans the SQL dump for table-prefix tokens. It matches the attacker's crafted biography text, extracts it as if it were a table prefix, and concatenates it into a raw UPDATE query. The injected SQL executes with full database privileges, modifying wp_options to enable open registration and set the default role to administrator.
Conditions required:
  • An administrator must voluntarily initiate a full database restore
  • The restore must use the poisoned backup (not an older clean one)
Where this breaks in practice:
  • This is the critical friction point. Database restores are rare events — disaster recovery, site migration, or staging refresh. Most production WordPress sites go months or years without a restore.
  • The attacker has zero control over whether or when this happens
  • If the admin restores from a backup taken *before* the payload was planted, the chain breaks
Detection/coverage: Wordfence Premium/Care/Response users received a virtual patch on September 24, 2026. Free Wordfence users receive coverage on October 24, 2026.
STEP 05

Attacker registers an administrator account

With users_can_register flipped to 1 and default_role set to administrator, the attacker visits /wp-login.php?action=register and creates a new account that is automatically granted full administrator privileges. From here, the attacker has complete control of the WordPress site — plugin/theme editing, file upload, database access, and content manipulation.
Conditions required:
  • The restore in step 4 completed successfully with the poisoned payload
  • The attacker checks back (or monitors) to discover that registration is now open
Where this breaks in practice:
  • The attacker must poll the target to detect when the restore happened — there's no callback mechanism
  • Security plugins may alert admins to the changed settings before the attacker exploits them
Detection/coverage: New admin account registration triggers email notifications to existing admins. Wordfence and similar plugins alert on role changes and option modifications.
03 · Compensating Control

1
MEDIUM 5.5→IGNORE 0.0
SEVERITY REDUCED
Update Backuply to 1.5.9 immediately — The definitive fix. Version 1.5.9 sanitizes biography content during the restore regex processing. Deploy via WP-CLI (wp plugin update backuply) or wp-admin auto-update. Under noisgate's MEDIUM remediation SLA, this should be completed within 365 days — but given the simplicity of a WordPress plugin update, aim for days, not months.
2
MEDIUM 5.5→LOW 2.5
SEVERITY REDUCED
Enable Wordfence WAF with current rulesets — Wordfence Premium/Care/Response customers received a virtual patch on September 24, 2026 that blocks the payload injection at the profile-update stage. Free tier coverage begins October 24, 2026. This breaks the chain at step 2 (payload planting). If you're running Wordfence free, this is active within days. Deploy within the noisgate MEDIUM remediation window.
3
MEDIUM 5.5→LOW 3.0
SEVERITY REDUCED
Disable public user registration if not business-required — Set Settings → General → Anyone can register to unchecked. This eliminates the trivial subscriber-registration path (step 1), requiring the attacker to already have credentials. Dramatically narrows the attacker population.
4
MEDIUM 5.5→LOW 2.0
SEVERITY REDUCED
Audit wp_usermeta for SQL injection artifacts — Run a database query: SELECT user_id, meta_value FROM wp_usermeta WHERE meta_key = 'description' AND (meta_value LIKE '%UPDATE%wp_options%' OR meta_value LIKE '%default_role%' OR meta_value LIKE '%users_can_register%'); Any matches are suspicious and should be sanitized. This detects planted payloads before they can fire during a future restore.
5
MEDIUM 5.5→LOW 2.0
SEVERITY REDUCED
Validate backup integrity before restoring — Before any Backuply restore, grep the SQL dump for anomalous UPDATE statements in INSERT blocks: grep -P 'UPDATE.*wp_options.*default_role' backup.sql. If your restore process includes this check, you catch the payload before it executes, breaking the chain at step 4.
What doesn't work
  • Generic WAF SQL injection rules — The payload is injected via a standard WordPress profile update (POST to /wp-admin/profile.php). The malicious SQL is stored in a free-text biography field that legitimately accepts arbitrary text. Generic SQLi signatures look for SQL in query parameters or headers, not in profile bio fields. Only Wordfence's CVE-specific virtual patch catches this.
  • Rate limiting or brute-force protection — This is a one-shot payload insertion, not a volume-based attack. Rate limiting has no effect.
  • Content Security Policy or security headers — This is entirely a server-side SQL injection triggered during PHP execution of a restore operation. No client-side defenses are relevant.
  • Disabling REST API or XML-RPC — The payload is planted via the standard wp-admin profile editor, not through API endpoints.
04 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNone observed. Not listed in CISA KEV. No campaigns reported by Wordfence, Sucuri, Patchstack, or WPScan as of October 20, 2026.
Proof-of-concept availabilityNo public PoC. Not indexed on pocindex.io. No CVE-specific GitHub repos found. The SecureWithUmer/CVE-2026-PoCs collection does not include this CVE. Exploitation requires understanding Backuply's internal regex and crafting a precise payload — non-trivial without source review.
EPSS score0.0034 (0.34%) — bottom quintile. FIRST's model rates this as very unlikely to be exploited in the next 30 days, consistent with the second-order trigger requirement.
CISA KEV statusNot listed. No federal agency mandate to patch.
CVSS vector interpretationCVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:H — Network-reachable, low complexity, requires low privileges and user interaction, unchanged scope, full CIA impact. The UI:R underweights the actual interaction burden (admin-initiated restore vs. a simple click).
Affected versionsBackuply ≤ 1.5.8 (all versions). Plugin is developed by Softaculous.
Fixed versionBackuply 1.5.9 (released October 1, 2026). No distro backports applicable — WordPress plugins update via wp-admin or WP-CLI.
Installed base / exposure600,000+ active WordPress installations per wordpress.org. Sites are web-facing by nature, but the vulnerability requires authenticated access *and* an admin-triggered restore, so internet exposure alone is not sufficient for exploitation.
Disclosure timelineDiscovered September 17, 2026 by researcher daroo ($1,352 Wordfence bounty). Reported to vendor September 28, 2026. Patch released October 1, 2026. Public disclosure October 15, 2026.
Wordfence virtual patchPremium/Care/Response customers protected since September 24, 2026 (before public disclosure). Free tier coverage begins October 24, 2026.

Sources.

  1. Wordfence Vulnerability Intel — CVE-2026-93953
  2. SecurityOnline — Backuply SQL Injection Advisory
  3. Malware.news — 600,000 WordPress Sites Affected
  4. IT-Connect — Backuply SQL Flaw Analysis
  5. NVD — CVE-2026-93953
  6. VulDB — CVE-2026-93953
  7. PoC Index — CVE Search
05 · The Call

Final Verdict
↓ DOWNGRADED to MEDIUM (5.5/10)

Why this verdict

  • Second-order trigger is a deal-breaker for mass exploitation. The attacker cannot fire the payload on demand. It only executes when an administrator voluntarily restores a database backup — an event that is infrequent by nature (disaster recovery, migration). CVSS's UI:R treats this the same as clicking a link; real-world friction is orders of magnitude higher.
  • Zero attacker control over timing. The payload sits dormant in the database indefinitely. The attacker must poll the target to detect if/when a restore occurred. Most WordPress sites never restore backups during the realistic exploitation window (disclosure → patch adoption).
  • No public PoC and no observed exploitation. EPSS at 0.34% confirms the model's read that this is unlikely to see broad exploitation. Crafting a working payload requires reverse-engineering Backuply's internal regex — non-trivial without a published writeup.
  • Subscriber auth is low-to-moderate friction. On sites with open registration, trivial. On enterprise/gated sites, requires prior credential compromise. This prevents a full downgrade to LOW but is not sufficient to maintain HIGH.
  • Role multiplier: WordPress sites running Backuply occupy the *web application tier* — not identity, hypervisor, CI/CD, backup infrastructure, or network edge in the enterprise sense. (a) *Low-value role:* dev/staging sites — chain succeeds if restore is triggered, blast radius is a throwaway host. (b) *Typical role:* small business / marketing site — chain succeeds, blast radius is a single website (host-level), possible PII exposure if e-commerce. (c) *High-value role:* WooCommerce stores with payment/PII data — chain succeeds, blast radius is still single-site. Even the worst outcome is host-scoped, not domain/fleet/supply-chain. Backuply is not canonically a high-value-role component per the catalog — it's a WordPress plugin, and no WordPress plugin constitutes an identity provider, domain controller, hypervisor, or network edge. The floor rule does not elevate this beyond MEDIUM.
  • 600K installs provides breadth but the second-order trigger throttles actual exploitation. Even if 10% of sites have open registration and the attacker plants payloads, only the fraction that restores a poisoned backup within the exploitation window is at risk — realistically <1% of the install base.

Why not higher?

The vendor's HIGH/8.0 assumes the UI:R interaction is a garden-variety click. In reality, the required interaction is a full database restore — an operational event that is rare, deliberate, and often preceded by integrity checks. The attacker has zero agency in triggering it. With no public PoC, no exploitation, and an EPSS of 0.34%, there is no evidence to support maintaining HIGH. The blast radius is single-site, not infrastructure-tier.

Why not lower?

Subscriber-level auth is genuinely easy to obtain on the large fraction of WordPress sites that allow open registration. The payload, once planted, is persistent and invisible without targeted auditing. If a restore does occur, the impact is full site takeover with admin-level database access — which on e-commerce or membership sites can mean PII and payment data exposure. The 600K install base means the aggregate population at risk is non-trivial even at a low per-site exploitation rate.

06 · Verification

Crowdsourced verification payload.

Run this on the WordPress host as a user with read access to the WordPress installation directory (typically www-data or via sudo). Invoke: bash check_backuply_cve2026_93953.sh /var/www/html (pass the WordPress root path as the first argument). No network access or database credentials required — it checks the plugin version on disk.

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_backuply_cve2026_93953.sh
# Checks whether the installed Backuply plugin is vulnerable to CVE-2026-93953
# Usage: bash check_backuply_cve2026_93953.sh /path/to/wordpress
# Exit codes: 1 = VULNERABLE, 0 = PATCHED, 2 = UNKNOWN

set -euo pipefail

WP_ROOT="${1:?Usage: $0 /path/to/wordpress}"
PLUGIN_FILE="${WP_ROOT}/wp-content/plugins/backuply/backuply.php"

if [ ! -f "$PLUGIN_FILE" ]; then
  echo "UNKNOWN — Backuply plugin not found at ${PLUGIN_FILE}"
  echo "The plugin may not be installed, or the WordPress path is incorrect."
  exit 2
fi

# Extract the Version: header from the plugin's main file
VERSION=$(grep -iP '^\s*\*?\s*Version:\s*' "$PLUGIN_FILE" | head -1 | sed -E 's/^.*Version:\s*([0-9.]+).*/\1/')

if [ -z "$VERSION" ]; then
  echo "UNKNOWN — Could not parse Backuply version from ${PLUGIN_FILE}"
  exit 2
fi

echo "Detected Backuply version: ${VERSION}"

# Compare versions: vulnerable <= 1.5.8, patched >= 1.5.9
# Use sort -V for version comparison
MIN_SAFE="1.5.9"
LOWEST=$(printf '%s\n%s' "$VERSION" "$MIN_SAFE" | sort -V | head -1)

if [ "$VERSION" = "$MIN_SAFE" ] || [ "$LOWEST" = "$MIN_SAFE" ]; then
  echo "PATCHED — Backuply ${VERSION} is >= ${MIN_SAFE}. CVE-2026-93953 is fixed."
  exit 0
else
  echo "VULNERABLE — Backuply ${VERSION} is < ${MIN_SAFE}. Affected by CVE-2026-93953."
  echo "Update to Backuply 1.5.9+ immediately: wp plugin update backuply"
  exit 1
fi