← Back to Feed CACHED · 2026-10-08 01:58:21 · CACHE_KEY CVE-2026-19949
CVE-2026-19949 · CWE-89 · Disclosed 2026-08-25

All-in-One WP Migration and Backup Unauthenticated Second-Order SQL Injection via Archive Restore

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

A ticking bomb hidden in a trackback comment that only detonates when the site admin clicks 'Restore'

CVE-2026-19949 is a second-order SQL injection in the All-in-One WP Migration and Backup plugin for WordPress (all versions through 7.109, fixed in 7.110). The flaw lives in the plugin's replace_table_values() function, which rewrites URLs and table prefixes during an export/import cycle. A regex flaw in how backslashes and quoted strings are parsed lets attacker-controlled content — planted unauthenticated via WordPress trackbacks — escape its SQL string literal and execute as raw SQL. The injected query extracts the plugin's ai1wm_secret_key and writes it into a publicly visible comment, where the attacker retrieves it via the REST API. Armed with that key, the attacker imports a crafted .wpress archive containing a malicious must-use (mu) plugin, achieving full remote code execution. The plugin has 5 million+ active installs; as of early September 2026, only ~35% had updated, leaving an estimated 3.25 million sites on vulnerable versions.

The vendor rates this HIGH (8.8) with CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. That vector is misleading in two places. First, UI:N implies no user interaction is needed — but the entire chain is inert until a site administrator *actively performs an export-then-import*, a human action the attacker cannot force or schedule. Second, AC:L ignores the multi-phase, time-delayed nature of the attack: the attacker plants, waits (hours to months), and only gets a shot when the admin happens to restore. Wordfence themselves acknowledge this is a 'routine action' for the plugin's users, which keeps severity meaningful, but it is decidedly not zero-click. A more accurate vector would use AC:H/UI:R, which calculates to 7.5. The real-world severity is HIGH but closer to 7.2, not 8.8.

"Second-order SQLi in WP Migration chains to RCE but only fires when an admin restores a backup"
02 · The Attack Path

5 steps from start to impact.

STEP 01

Plant malicious trackback

The attacker sends a crafted XML-RPC trackback to any public post on the target WordPress site. The trackback's blog_name or URL field contains a SQL injection payload — specifically, a carefully constructed string with mismatched backslashes designed to break out of a quoted literal during the plugin's find-and-replace pass. WordPress stores this trackback data in the wp_comments table as a comment of type trackback. No authentication is required; trackbacks are enabled by default in WordPress.
Conditions required:
  • Target WordPress site has at least one published post
  • Trackbacks/pingbacks are enabled (WordPress default)
  • XML-RPC endpoint (/xmlrpc.php) is reachable
Where this breaks in practice:
  • Many hardened WordPress deployments disable XML-RPC entirely (iThemes, Wordfence, Sucuri all recommend this)
  • Some WAFs block or rate-limit XML-RPC requests
  • Security plugins like Disable XML-RPC are installed on millions of sites
Detection/coverage: Wordfence WAF rule deployed Aug 16 for premium subscribers; free-tier rule delayed until Sep 15. Standard WAFs struggle to detect dormant second-order payloads at planting time.
STEP 02

Wait for admin export + import

The payload sits dormant in the wp_comments table as ordinary data. The attacker has no control over when the next step fires — it requires a site administrator to perform an export followed by an import using the All-in-One WP Migration plugin. This is the plugin's core function (backup/restore, staging migration, host migration), so it does happen routinely, but timing is unpredictable. During import, the plugin calls Ai1wm_Database::replace_table_values(), which uses a regex to find and rewrite serialized URLs and table prefixes. The regex mishandles backslash-escaped quotes in the attacker's planted data, allowing the payload to escape the string boundary.
Conditions required:
  • Administrator must actively trigger an export+import cycle
  • All-in-One WP Migration plugin version ≤ 7.109 is in use
Where this breaks in practice:
  • Timing is entirely admin-dependent — could be hours, weeks, or never
  • Sites that only use the plugin for one-time migration (not recurring backups) may never re-trigger
  • Automated backup schedules typically use different plugins (UpdraftPlus, BlogVault) that don't trigger this code path
Detection/coverage: No detection at this stage — the operation looks like a normal plugin restore in WordPress admin logs.
STEP 03

SQL injection extracts ai1wm_secret_key

When the regex flaw allows the planted payload to escape its SQL string literal, the injected SQL executes as part of the import's INSERT or UPDATE statements. The payload is crafted to read the ai1wm_secret_key value from the wp_options table and write it into a new approved comment on the site. This key is a secret token that protects the plugin's unauthenticated import endpoint — leaking it is the critical pivot from data extraction to RCE.
Conditions required:
  • The SQL injection must execute successfully during the import process
  • The injected SQL must be able to read wp_options and write to wp_comments
Where this breaks in practice:
  • Database-level permissions are typically sufficient (WordPress DB user has full access to its own schema)
  • No additional friction at this step — once step 2 fires, this is automatic
Detection/coverage: Database audit logging (if enabled) would show anomalous queries during the import. The leaked key appearing in a public comment is a detectable IOC — monitor for ai1wm_secret_key in comment content.
STEP 04

Retrieve secret key via REST API

The attacker polls the WordPress REST API (/wp-json/wp/v2/comments) to retrieve the comment containing the leaked ai1wm_secret_key. Since the injected SQL created a standard approved comment, it is publicly visible through the unauthenticated REST API. This step requires no privileges and can be automated with a simple polling script.
Conditions required:
  • WordPress REST API is publicly accessible (default)
  • The comment containing the key has been approved or was inserted as approved
Where this breaks in practice:
  • Some sites disable or restrict the REST API (though this is uncommon for the comments endpoint)
  • The attacker must be monitoring the target to catch the key after it appears
Detection/coverage: Unusual REST API polling patterns on the comments endpoint. The comment itself containing what looks like a hex token is a strong IOC.
STEP 05

Import malicious .wpress archive for RCE

With the ai1wm_secret_key in hand, the attacker accesses the plugin's unauthenticated import endpoint and uploads a crafted .wpress archive. This archive contains a malicious PHP file placed in the mu-plugins directory (must-use plugins). WordPress automatically loads all PHP files in mu-plugins on every page load — no activation required. The mu-plugin can be a webshell, a reverse shell, or any arbitrary PHP code, giving the attacker full control over the web server. The PoC by 686f6c61 demonstrates this with a benign marker mu-plugin for safe testing.
Conditions required:
  • Valid ai1wm_secret_key obtained from step 4
  • The plugin's import endpoint is reachable
  • Web server has write permissions to wp-content/mu-plugins (standard WordPress)
Where this breaks in practice:
  • File integrity monitoring (FIM) tools would detect new files in mu-plugins
  • Some hosting providers restrict mu-plugins directory writes
  • EDR/HIDS on the web server may flag new PHP file creation
Detection/coverage: FIM alerts on new files in wp-content/mu-plugins/. Web server access logs showing POST to the plugin's import endpoint with the secret key parameter. New PHP files appearing outside normal deployment patterns.
03 · Compensating Control

1
HIGH 7.2→IGNORE 0.0
SEVERITY REDUCED
Update to All-in-One WP Migration 7.110 immediately — The definitive fix. Version 7.110 corrects the regex handling in replace_table_values() so planted payloads cannot escape string literals. Deploy within 30 days per the noisgate remediation SLA for HIGH. For internet-facing production or e-commerce WordPress sites, treat this as urgent and push within 7 days given the public PoC.
2
HIGH 7.2→LOW 2.5
SEVERITY REDUCED
Disable XML-RPC on all WordPress instances — Blocks the trackback planting vector entirely. Add <Files xmlrpc.php> Require all denied </Files> to your Apache config or equivalent nginx location block, or use the Disable XML-RPC plugin. This should be a standing hardening measure — deploy within 30 days per the noisgate mitigation SLA for HIGH. Breaks the chain at step 1.
3
HIGH 7.2→LOW 2.5
SEVERITY REDUCED
Disable trackbacks and pingbacks globally — In WordPress admin: Settings → Discussion → uncheck 'Allow link notifications from other blogs (pingbacks and trackbacks).' Then run wp post list --format=ids | xargs wp post update --ping_status=closed via WP-CLI to retroactively close trackbacks on existing posts. This blocks the specific planting vector even if XML-RPC remains open. Deploy within 30 days.
4
HIGH 7.2→MEDIUM 5.5
SEVERITY REDUCED
Deploy file integrity monitoring on mu-plugins directory — Monitor wp-content/mu-plugins/ for any new or modified PHP files. The final RCE payload must write a file here. FIM tools (OSSEC, Wazuh, Tripwire, or host-based EDR) will catch the exploitation's final step and enable rapid response. Does not prevent exploitation but ensures detection. Deploy within 30 days.
5
HIGH 7.2→MEDIUM 4.5
SEVERITY REDUCED
Restrict the plugin's import endpoint via WAF or IP allowlisting — Block unauthenticated access to the All-in-One WP Migration import endpoint at the reverse proxy or WAF layer. Wordfence premium WAF rule (deployed Aug 16) covers this. Alternatively, restrict access to the plugin's AJAX import handler to known admin IPs only. Breaks the chain at step 5.
6
HIGH 7.2→HIGH 7.2
Audit comments table for IOCs — Search wp_comments for trackback entries containing unusual backslash sequences, hex-encoded strings, or SQL keywords (UNION, SELECT, FROM wp_options). Also search for any comment containing the string ai1wm_secret_key. If found, the planting phase has already occurred — rotate the secret key immediately by deleting and regenerating the ai1wm_secret_key option in wp_options, and do NOT perform any import until patched.
What doesn't work
  • Standard WAF SQL injection rules — second-order SQLi plants data that *looks* like normal text at ingestion time. The malicious SQL only assembles during the plugin's internal find-and-replace, which happens server-side after the WAF has already passed the request. Generic SQLi signatures will miss this at the trackback stage.
  • WordPress auto-updates alone — while WordPress supports auto-updates for plugins, many enterprise deployments pin versions or disable auto-updates for change-control reasons. Don't assume your fleet has silently updated.
  • Database-level query logging without baseline — the injected SQL executes during a legitimate import operation that already generates thousands of INSERT/UPDATE statements. Without a known-good baseline of import queries, anomaly detection on raw query logs will produce excessive false positives.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild StatusNo confirmed active exploitation as of 2026-10-08. SOCRadar notes 'exploitation is reported but no independent confirmation is available.' Mass automated exploitation is expected to develop gradually due to the admin-action dependency.
Proof-of-ConceptWeaponized public PoC available: 686f6c61/POC-AIOWPM-CVE-2026-19949 — full Docker lab with trackback planting, secret key leak, and RCE via mu-plugin. Also indexed on Vulners and Sploitus. Listed in multiple PoC aggregator repos (asvorg/CVE-2026-PoCs, SecureWithUmer/CVE-2026-PoCs, XZ1r0/cve-2026-poc-collection).
EPSS0.01429 (≈ top 15th percentile). Moderate probability of exploitation in the next 30 days — reflects the admin-action dependency dampening automated exploitation likelihood.
CISA KEVNot listed as of 2026-10-08. No known addition date.
CVSS VectorCVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H — vendor-assigned 8.8 HIGH. noisgate assessment: AC:L and UI:N are generous; the chain requires admin-initiated backup restore (should be UI:R) and is multi-phase/time-delayed (argues for AC:H). Corrected vector would yield ~7.5.
Affected VersionsAll-in-One WP Migration and Backup ≤ 7.109 (all versions). Plugin by ServMask. 5 million+ active installations per wordpress.org.
Fixed Version7.110 (released 2026-08-20). Silently labeled as a 'text fix' in the changelog — no security language in the update notice. No distro backports apply (WordPress plugin, not OS-packaged).
Exposure DataWordPress telemetry shows ~35% adoption of 7.110 as of Sep 2, leaving an estimated 3.25 million sites on vulnerable versions. WordPress sites are internet-facing by design; the plugin's import endpoint and XML-RPC are reachable externally by default.
Disclosure TimelineAug 14: Reported by Jack Taylor to Wordfence Bug Bounty ($5,761 bounty). Aug 15: Disclosed to ServMask. Aug 16: Wordfence premium WAF rule deployed. Aug 20: Patch released (v7.110). Aug 25: CVE published. Sep 2: Full public disclosure. Sep 15: Wordfence free-tier WAF rule.
ReporterJack Taylor, via the Wordfence Bug Bounty Program.

Sources.

  1. BleepingComputer — WordPress backup plugin flaw exposes millions of sites
  2. CyberSecurityNews — WordPress Plugin Flaw Exposes 5M Sites to SQL Injection
  3. ToolsLib Blog — CVE-2026-19949 Technical Advisory
  4. TechTimes — 3.25 Million Sites Exposed, Exploit Code Active
  5. 686f6c61/POC-AIOWPM-CVE-2026-19949 — Weaponized PoC on GitHub
  6. DEV Community — From Second-Order SQL Injection on Restore to Site Takeover
  7. eSecurity Planet — CVE-2026-19949 Leaves Millions Running Vulnerable Versions
  8. SOCRadar — CVE-2026-19949 CVE Radar
05 · The Call

Final Verdict
= UNCHANGED to HIGH (7.2/10)

Why this verdict

  • Admin-action gate breaks zero-click: The entire chain is inert until a site administrator actively performs an export+import cycle. The attacker cannot trigger, schedule, or accelerate this step. The vendor's UI:N is incorrect — this is at minimum UI:R, which alone drops the CVSS from 8.8 to 8.0.
  • Second-order delay adds attack complexity: The payload must survive dormancy in the database, then execute correctly during a regex-driven find-and-replace pass — a multi-phase, timing-dependent chain. The vendor's AC:L understates this; AC:H is more accurate, yielding a further drop to ~7.5.
  • Trackback hardening reduces reachable population: While trackbacks are enabled by default, a significant fraction of production WordPress sites disable XML-RPC entirely (recommended by all major WordPress security plugins). This narrows the population that can even receive the initial payload.
  • No confirmed in-the-wild exploitation: Despite the public weaponized PoC being available since September 2026, no independent source has confirmed active exploitation. The EPSS of 0.01429 reflects moderate — not urgent — exploitation probability.
  • Role multiplier: WordPress is a web-tier application, not a canonical high-value infrastructure component (not an IdP, hypervisor, DC, CA, or PAM). Blast radius on compromise is host-level (webshell on one server) extending to data-breach-level for WooCommerce/e-commerce deployments holding PII or payment data. However, it does not reach domain/fleet/supply-chain scope. The ~3.25M exposed sites are internet-facing by design, which keeps severity meaningful — a compromised WordPress server is a foothold for lateral movement and a platform for malware distribution. This supports HIGH but does not warrant CRITICAL. No high-value-role floor override applies.

Why not higher?

A CRITICAL rating would require either a zero-click/wormable chain or a canonical high-value infrastructure target. This CVE has neither: the chain depends on an unpredictable admin action (export+import), and WordPress — while massively deployed — is a web application tier, not an identity system, hypervisor, or domain controller. The host-level blast radius, while serious for individual sites, does not produce fleet-scale or domain-scale impact. The EPSS of 0.01429 and absence from CISA KEV further argue against CRITICAL.

Why not lower?

Despite the friction, the fundamentals are dangerous: the initial vector is unauthenticated and remote, the end result is full RCE, the attack surface is 3.25 million internet-facing sites, and a weaponized public PoC exists with Docker reproduction steps. Wordfence correctly notes that backup/restore is the *core purpose* of this plugin — admins WILL trigger it eventually. The payload is patient; it doesn't expire. A MEDIUM rating would understate the risk for any organization running this plugin on a production site.

06 · Verification

Crowdsourced verification payload.

Run this script on each WordPress host (or from a management/jump box with filesystem access to WordPress installations). It checks the installed version of All-in-One WP Migration against the patched version 7.110. Requires read access to the WordPress plugin directory. Example: sudo bash check_cve_2026_19949.sh /var/www/html

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_19949.sh — Detect CVE-2026-19949 in All-in-One WP Migration
# Usage: bash check_cve_2026_19949.sh /path/to/wordpress
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

WP_ROOT="${1:-/var/www/html}"
PLUGIN_FILE="${WP_ROOT}/wp-content/plugins/all-in-one-wp-migration/all-in-one-wp-migration.php"
SAFE_VERSION="7.110"

if [[ ! -f "$PLUGIN_FILE" ]]; then
  echo "UNKNOWN — All-in-One WP Migration plugin not found at ${PLUGIN_FILE}"
  # Also check mu-plugins for IOCs while we're here
  MU_DIR="${WP_ROOT}/wp-content/mu-plugins"
  if [[ -d "$MU_DIR" ]] && find "$MU_DIR" -name '*.php' -newer "$WP_ROOT/wp-config.php" -print -quit 2>/dev/null | grep -q .; then
    echo "WARNING — Suspicious recent PHP files found in mu-plugins directory"
  fi
  exit 2
fi

# Extract version from plugin header
INSTALLED_VERSION=$(grep -i '^[[:space:]]*\*[[:space:]]*Version:' "$PLUGIN_FILE" | head -1 | sed 's/.*Version:[[:space:]]*//' | tr -d '[:space:]')

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

# Version comparison function
version_gte() {
  printf '%s\n%s' "$1" "$2" | sort -V | head -1 | grep -qx "$2"
}

echo "Installed version: ${INSTALLED_VERSION}"
echo "Safe version: >= ${SAFE_VERSION}"

if version_gte "$INSTALLED_VERSION" "$SAFE_VERSION"; then
  echo "PATCHED — All-in-One WP Migration ${INSTALLED_VERSION} is >= ${SAFE_VERSION}"
  exit 0
else
  echo "VULNERABLE — All-in-One WP Migration ${INSTALLED_VERSION} is affected by CVE-2026-19949"
  # Check for IOCs: trackbacks with suspicious content
  WP_CONFIG="${WP_ROOT}/wp-config.php"
  if [[ -f "$WP_CONFIG" ]]; then
    DB_NAME=$(grep "DB_NAME" "$WP_CONFIG" | cut -d "'" -f4 2>/dev/null || true)
    DB_PREFIX=$(grep "table_prefix" "$WP_CONFIG" | cut -d "'" -f2 2>/dev/null || true)
    if [[ -n "$DB_NAME" && -n "$DB_PREFIX" ]]; then
      echo "TIP: Check for planted payloads with:"
      echo "  mysql -e \"SELECT comment_ID, comment_content FROM ${DB_NAME}.${DB_PREFIX}comments WHERE comment_type='trackback' AND (comment_content LIKE '%ai1wm_secret_key%' OR comment_content LIKE '%UNION%SELECT%' OR comment_author LIKE '%\\\\\\\\%');\" "
    fi
  fi
  # Check for post-exploitation IOCs in mu-plugins
  MU_DIR="${WP_ROOT}/wp-content/mu-plugins"
  if [[ -d "$MU_DIR" ]]; then
    SUSPICIOUS=$(find "$MU_DIR" -name '*.php' -newer "$PLUGIN_FILE" 2>/dev/null | head -5)
    if [[ -n "$SUSPICIOUS" ]]; then
      echo "WARNING — Suspicious PHP files in mu-plugins (possible post-exploitation):"
      echo "$SUSPICIOUS"
    fi
  fi
  exit 1
fi