PHP2Shell: two critical PHP holes hit every Magento store. What to do today
On 21 October 2026 the PHP project shipped emergency fixes for two critical bugs, CVE-2026-109618 and CVE-2026-109619, known together as PHP2Shell. Both allow code execution without a login. Both sit in PHP itself, not in Magento, and the attack arrives before a single line of Magento code runs. No Magento patch fixes this. Here is what it means for your store, how to find out whether you are protected, and what to do if you still run PHP 8.1.
What happened, in plain terms
PHP is the language Magento is written in, and on your server it is a separate piece of software with its own updates. Two bugs were found in its core: one in how PHP handles running out of memory (CVE-2026-109618), one in error handling that leaves stale data behind for the next request on the same worker (CVE-2026-109619). Both are rated 9.2 out of 10. The trigger is an ordinary multipart/form-data request, the kind a browser sends when it uploads a file, and PHP parses that body before Magento starts. That is why nothing inside Magento helps: not maintenance mode, not the October security patch from Adobe, not an extension.
Three facts make this one different from the usual monthly bulletin:
- It is confirmed on Magento. Sansec, the company behind the Magento malware scanner, demonstrated unauthenticated remote code execution against a fully patched Magento 2.4.9.
- The exploit got out early. The CVE numbers leaked a day before the fix, a video of a working proof-of-concept circulated, and Cloudflare reported attack attempts on the morning of 21 October, hours before the patched PHP was available. Every public Magento store should expect to be scanned.
- Nobody at Adobe can fix it for you. The fix is a new PHP package on your server. Whoever administers that server, you, your developer, your hosting company or Adobe on Commerce Cloud, has to install it and restart PHP.
Is your store affected?
Yes, unless the PHP that serves your pages already contains the fix. The vulnerable code has existed since PHP 5.4, so every Magento version on every PHP version is in scope. The fixed PHP releases are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Which one you need depends on your Magento release line:
| Magento / Adobe Commerce | PHP it supports | Patched PHP you need |
|---|---|---|
| 2.4.9 | 8.5 (8.4 on older builds) | 8.5.12 or 8.4.27 |
| 2.4.8 | 8.3, 8.4 | 8.3.36 or 8.4.27 |
| 2.4.7 | 8.2, 8.3 | 8.2.35 or 8.3.36 |
| 2.4.6 | 8.1, 8.2 | 8.2.35. On 8.1, see below |
| 2.4.4, 2.4.5 | 8.1 | No official PHP release. See below |
| 2.3.x, 2.4.0 to 2.4.3 | 7.x | No fix from the PHP project |
One trap: Debian, Ubuntu and Red Hat backport fixes without changing the version number. On Ubuntu 24.04, php -v says 8.3.6 before and after the fix. What changes is the package version, 8.3.6-0ubuntu0.24.04.12 before, 8.3.6-0ubuntu0.24.04.13 after. So check the package, not the banner.
How to check, in five minutes
On the server, or sent to whoever runs it:
# Debian / Ubuntu: what is installed, and what is waiting to be installed
dpkg -l 'php*-fpm' | grep ^ii
sudo apt-get update && apt list --upgradable 2>/dev/null | grep php
# Ubuntu names the CVEs in the package changelog once the fix is in
zgrep -E 'CVE-2026-10961[89]' /usr/share/doc/php8.3-fpm/changelog.Debian.gz
# RHEL, Alma, Rocky with the Remi repository
rpm -qa 'php*fpm*'
rpm -q --changelog php-fpm | head -5
If the installed version is older than the fixed one for your distribution, you are exposed. If it is the fixed one, make sure PHP was actually restarted after the install (below), because a running worker keeps the old code in memory until it is.
In Docker and Kubernetes, the question is the base image: the official php images for 8.2 to 8.5 were rebuilt with the fix on 21 October, the RC tags were not, and you have to rebuild and redeploy your own image for it to matter.
Who has to do what
| Where the store runs | Who installs the fix | What you do |
|---|---|---|
| Self-managed VPS or dedicated server | You or your developer | Install today. Ubuntu, Debian, the Sury PPA and Remi all had packages within hours of the release. |
| Managed Magento hosting (Hypernode, Nexcess, Cloudways, MGT, JetRails, Sonassi and the like) | The host | Open a priority ticket and ask for written confirmation with package version and timestamp, for production and staging. Most hosts patched within a day; some do it in batches over several days. |
| Adobe Commerce on Cloud | Adobe (it owns the PHP image) | Open an Adobe Support ticket for every environment that is reachable from the internet, Production and Staging first, and check php -v over SSH once they confirm. |
| Docker / Kubernetes | Whoever builds the image | Bump the base image to a fixed tag, rebuild, redeploy, and check that no old pod is left running. |
A ticket that gets a useful answer looks like this:
Subject: PHP2Shell (CVE-2026-109618 / CVE-2026-109619): patch status for <store domain>
1. Which PHP package version is installed on production and on staging right now?
2. When was it installed, and was PHP-FPM (or Apache) restarted afterwards?
3. If the server runs PHP 8.1 or older: which backport did you apply, or which
mitigations are in place until one is available?
Please reply in writing with versions and timestamps.
If you are on PHP 8.1 or older
PHP 8.1 stopped receiving security fixes from the PHP project on 31 December 2025, so there is no official 8.1 release with this fix, only a minimal source patch for people who compile PHP themselves. That sounds worse than it is, because your 8.1 almost certainly comes from a distribution, and some of those did patch it:
- Ubuntu 22.04: fixed in
php8.1 8.1.2-1ubuntu2.28(USN-8948-1), installable with a normalapt-getupdate. - Ondřej Surý’s PPA and packages.sury.org: he patches every branch he ships, including end-of-life ones. Check for a new 8.1 package; the 8.2 to 8.5 builds were out on day one.
- Remi (RHEL, Alma, Rocky): still patches 7.4, 8.0 and 8.1, with no promise on timing.
- Debian, the official Docker
php:8.1image, Plesk’s 8.1 builds: no fixed 8.1 package at the time of writing. Plesk listed its 8.1 fix as in development.
Then it depends on your Magento line. 2.4.6 supports PHP 8.2, so the clean answer is a move to 8.2.35: a day of testing on staging for most stores, and something you had to do before PHP 8.2 itself goes end-of-life on 31 December 2026 anyway. 2.4.4 and 2.4.5 cannot run anything newer than 8.1. There, you take the distribution backport if you have one, put the stopgaps below in place if you do not, and treat this as the week you finally schedule the upgrade to a supported release. Those lines are out of Adobe’s support too; I explain the dates in Is Magento end of life?
Stopgaps until the patch is on the server, and what they break in Magento
The PHP team and the people who found the bugs published a list of mitigations. None of them is complete; the patch is. But if you are waiting for a host, a change window or an 8.1 backport, these buy time. In order of how little they cost a Magento store:
- Turn off session upload progress. Magento does not use this feature, so this is free. In the FPM pool, where application code cannot switch it back on:
php_admin_flag[session.upload_progress.enabled] = off, then restart PHP-FPM. This closes the confirmed path into CVE-2026-109619, not every path. - Move per-request PHP settings out of nginx. PHP’s guidance lists settings passed per request as a trigger for CVE-2026-109619. Magento’s own
nginx.conf.sampledoes exactly that, with two lines almost every store has copied:
Delete them and put the same values in the FPM pool instead (fastcgi_param PHP_FLAG "session.auto_start=off \n suhosin.session.cryptua=off"; fastcgi_param PHP_VALUE "memory_limit=756M \n max_execution_time=18000";php_admin_flag[session.auto_start] = off,php_admin_value[memory_limit] = 756M,php_admin_value[max_execution_time] = 18000). Thesuhosinsetting refers to an extension that has not existed for a decade; drop it. This is a good change to keep after patching. - Recycle workers after every request.
pm.max_requests = 1in the FPM pool stops a worker carrying stale state into the next request. It costs performance, acceptable for a day on a small store, painful on a busy one. - Remove the memory limit.
memory_limit = -1is PHP’s primary mitigation for CVE-2026-109618, and the most important one on Apache withmod_php. On Magento I would not do it blindly: one runaway import or a misbehaving extension can then eat the whole server. If you do it, cap the pool with a cgroup, and restore a real limit after patching. On PHP-FPM this bug is harder to exploit, because FPM kills a worker whose request startup fails. One more reason Magento belongs on FPM, notmod_php. - Block multipart requests at nginx. Effective, and it breaks product image uploads in the admin, imports, and on some stores a step of checkout. Only as a last resort, only with an exception for your office IP on
/admin, and place a test order after enabling it. - A WAF or Cloudflare rule. Catches the exploit shapes that are known today, nothing else, and only if attackers cannot reach the server directly, bypassing the proxy.
Three things that do nothing: Magento’s maintenance mode (PHP parses the request first), display_errors or error suppression in code, and removing the PHP handler from nginx to take the site down, which makes the web server send your env.php with the database password as plain text.
After the patch: restart, verify, and look around
Install the package, then restart, not reload, if you run Apache with mod_php; a reload keeps the old library loaded. PHP-FPM picks up the new binary on reload or restart. LiteSpeed needs its lsphp processes killed separately. Then confirm nothing is still running old code:
sudo apt-get install --only-upgrade 'php8.3*'
sudo systemctl restart php8.3-fpm
# empty output means no process still maps the old, deleted PHP files
sudo grep -lE ' /(usr|lib|opt).*\(deleted\)$' /proc/[0-9]*/maps
If you set memory_limit = -1 as a stopgap, put the real limit back; the patched PHP adds a hard internal limit, and workers that hit it exit with code 137, which you will see in the FPM log.
Then ask whether anyone got in during the window between the leak and your patch. There is no reliable signature yet, but these checks take ten minutes and catch the usual aftermath:
# PHP where it must never exist
find pub/media pub/static var -type f \( -name '*.php' -o -name '*.phtml' -o -name '*.phar' \)
# PHP files changed in the last week outside the places deployments touch
find . -name '*.php' -mtime -7 -not -path './vendor/*' -not -path './generated/*' -not -path './var/*'
# admin accounts, integrations, injected scripts
mysql> SELECT username, email, created, modified, is_active FROM admin_user;
mysql> SELECT name, status, created_at FROM integration;
mysql> SELECT path, value FROM core_config_data WHERE path IN ('design/head/includes', 'design/footer/absolute_footer', 'design/footer/miscellaneous');
Also read the PHP-FPM error log. Attempts on CVE-2026-109618 deliberately push a request over the memory limit, so a burst of Allowed memory size exhausted errors from one IP on URLs that never caused them before is worth a look. And because CVE-2026-109619 can disclose server memory, a store that was reachable unpatched for a day or more should rotate what is cheap to rotate: admin passwords, API and integration tokens, and payment gateway credentials if they live in core_config_data.
Sources
- The PHP Foundation: Today’s PHP security release, 21 October 2026
- PHP advisory for CVE-2026-109618 and for CVE-2026-109619, with the official mitigations
- Sansec: PHP2Shell, confirmation on Magento 2.4.9
- Mattias Geniar: early mitigation steps and how to apply the patches, with the package status per distribution
- Ubuntu security tracker: CVE-2026-109618, fixed package versions for 22.04 and 24.04
- Adobe Commerce system requirements, PHP versions per release line
- Magento
nginx.conf.sample, thePHP_VALUElines