Blog All-in-One WP Migration 7.110 and Gravity Forms 3.0.3 patch RCE paths
All-in-One WP Migration 7.110 and Gravity Forms 3.0.3 patch RCE paths
Two WordPress plugins on roughly six million sites shipped fixes for unauthenticated RCE paths: All-in-One WP Migration via second-order SQL injection, Gravity Forms via a file upload that runs on NGINX.
Two vendors patched unauthenticated remote-code paths on the same day. All-in-One WP Migration and Backup is the bigger of the two. It runs on ~5,000,000 sites. The bug is an SQL injection in the archive restore flow, and the chain ends in code execution on the server. Gravity Forms runs on ~1,000,000 sites, with a file upload that becomes RCE on NGINX. Neither bug is being exploited on real sites that we know of. Both are unauthenticated, and both use features every site owner touches.
One thing worth saying about both: no login is needed to reach them. That is the threat model a scanner and a WAF have to catch, because the attacker never opens an account. Nothing is left in the audit log for an operator to find later.
all-in-one-wp-migration 7.110
The archive restore path in All-in-One WP Migration builds SQL from a value it reads inside the archive. It does not escape that value, and it does not prepare the surrounding query. An unauthenticated attacker crafts a poisoned archive. A site administrator then restores it. That is what fires the injected query. The advisory calls this a second-order SQL injection, and it names the payoff: the injected query reads the ai1wm_secret_key value out of the database. That secret key unlocks the plugin’s own restore endpoints from the outside, and an attacker uses it to write PHP files onto the site.
Why does this reach five million sites so easily? A backup and migration plugin exists to accept archives from other people. Operators move hosts. Consultants hand sites to a client. A staff member restoring what they believe is their own backup is what fires the chain.
- Affected: versions 7.109 and earlier
- CVE: CVE-2026-19949, CVSS 8.8
- Installed on ~5,000,000 sites
- Fix: update to 7.110. The 7.110 changelog credits Jack Taylor for the disclosure.
- After updating, treat any archive from an unknown source as suspect. Rotate
ai1wm_secret_key. Checkwp-contentfor PHP files that arrived recently.
gravityforms 3.0.3
Gravity Forms takes multi-file uploads through its GFAsyncUpload::upload() function. The chunk-continuation logic reuses the same hash the plugin publishes as a public form-state URL. It also accepts the temporary filename the attacker sends with a chunk, before any sanitisation. Both problems together let an unauthenticated visitor write a valid PNG/PDF polyglot to a .php or .html path they choose inside the plugin’s temp directory. The site has to run a public form with a File Upload field, and Multiple Files must be enabled on that field.
The web server decides whether this turns into remote code execution. On install, the plugin drops a .htaccess file into that temp directory. That file blocks PHP execution on Apache. NGINX and other web servers do not read .htaccess at all, so the PHP file the attacker wrote runs. On Apache the same upload lives on as an HTML page under the site’s own origin. The payoff there is stored same-origin XSS instead of code execution — smaller, but still yours to clean up.
- Affected: versions 3.0.2 and earlier
- CVE: CVE-2026-19513, CVSS 8.1
- Installed on ~1,000,000 sites
- Precondition: a public form with a File Upload field and Multiple Files enabled. An NGINX or non-
.htaccessserver to reach RCE; Apache still allows the stored XSS path. - Fix: update to 3.0.3
- Until the update is in, turn off Multiple Files on any public form, or remove the File Upload field. On NGINX, add a location block that denies script execution inside the Gravity Forms temp directory.
Tags: #wordpress , #plugin , #cve