On October 21, 2026, PHP released fixes for two critical bugs in the PHP engine itself: CVE-2026-109618 and CVE-2026-109619, nicknamed php2shell. Both are rated 9.2 on CVSS v4 and allow unauthenticated remote code execution. According to Sansec, they affect every web server running PHP 5.4 or later, and researchers confirmed pre-auth remote code execution against Magento 2.4.9.
This one is different from the Magento vulnerabilities we usually patch. There is no Composer package to update and no patch file to apply to the codebase. The bug lives in the PHP binary, so a store with perfectly clean code is still exposed. Sansec puts it plainly: “You cannot fix it from within your store codebase.”
Sansec reports that Cloudflare saw early attacks hours before the release, and that technical writeups and an exploit tool are now public. The PHP Foundation says it had not seen a functional exploit at the time of release, but a proof-of-concept video that was published by mistake and then deleted had already spread. Either way, treat this as urgent. In this post I go through what the bug is, which Magento stores are affected, how to patch correctly, and what to do if you can’t patch right away.
What happened
The weakness is old. Sansec traces it back to a 2004 finding by Stefan Esser, a 2020 PHP bug report and a 2024 audit, none of which proved it was exploitable. This year, three parties turned it into working exploits.
| Date (UTC) | Event |
|---|---|
| 2026-08-01 | Calif finds multiple RCE paths |
| 2026-08-11 | Calif reports the first bug to PHP; OpenAI reports the same root cause |
| 2026-08-24 | Wiz Research reports the vulnerabilities to PHP |
| 2026-10-20 ~15:00 | The CVE IDs start circulating online with a wrong release time. The PHP Foundation says no technical details leaked. The release moves from October 22 to October 21. |
| 2026-10-21 07:41 | Chainguard publishes fixed images before the release, which the Foundation says broke the embargo |
| 2026-10-21 07:43 | Cloudflare reports early attacks and deploys WAF rules (per Sansec) |
| 2026-10-21 14:00 | PHP releases the fixed versions |
Update, October 21 (afternoon): The PHP Foundation’s post on the release corrects a few early reports. Only the CVE IDs leaked before the release, not technical details. A CVSS score of 10.0 that circulated was wrong; the real score is 9.2. I’ve updated the timeline and the opening.
The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. The php.net announcement says: “All PHP users are strongly encouraged to update immediately.”
OpenAI says Calif found one of the flaws using its GPT-5.6 Sol model, as part of an initiative called Patch the Planet. Sansec notes that all three research teams credit frontier AI models with making the exploits practical.
How the bug works
Here is the simple version. When a request crosses PHP’s memory_limit, PHP aborts it with a fatal out-of-memory error. That abort can interrupt PHP’s internal code before it finishes cleaning up. Pointers to freed memory and half-initialized state stay behind. The next request handled by the same worker then treats that freed memory as live data.
The PHP advisories describe the two CVEs this way:
| CVE | PHP advisory title | What it allows (per Sansec) |
|---|---|---|
| CVE-2026-109618 | RCE via OOM corruption | Remote memory disclosure and RCE |
| CVE-2026-109619 | RCE via stale global state | RCE |
The memory disclosure part deserves attention. According to Sansec, CVE-2026-109618 can send freed worker memory back to the client inside a PHPSESSID response cookie. That memory can hold passwords, session tokens, API keys and TLS private keys from other requests. It works even when the application never calls session_start(), because the upload-progress handler runs before any application code.
Both setups are affected. Under Apache with mod_php, the worker is reused after the failed request. Under PHP-FPM, the path is different, but Sansec says the same dangling state can survive in a worker that keeps running.
Which Magento stores are affected
All of them, until PHP is updated. The real question is whether your PHP branch has a fix at all. PHP only supports 8.2 and later, so servers on PHP 8.1 or older get no official release with the fix.
Cross that with Adobe’s system requirements:
| Magento / Adobe Commerce | Supported PHP | Fixed PHP version available |
|---|---|---|
| 2.4.9 | 8.5 | 8.5.12 |
| 2.4.8 | 8.4, 8.3 | 8.4.27, 8.3.36 |
| 2.4.7 | 8.3, 8.2 | 8.3.36, 8.2.35 |
| 2.4.6 | 8.2, 8.1 | 8.2.35 (8.1: EOL source branch only, see update) |
| 2.4.5 | 8.1 | No release yet (EOL source branch only, see update) |
This is where I would spend my attention first. A store on 2.4.6 running PHP 8.1 has a way out: 2.4.6 supports PHP 8.2, so moving to 8.2.35 is a PHP change, not a Magento upgrade. Test it on staging, but it is a much smaller job than a release-line upgrade.
A store on 2.4.5 has no supported PHP with a released fix. Until it is upgraded, it needs the mitigations below, the EOL source branch from the update below, or it needs to be offline.
Update, October 21: PHP core developers also published source branches with a “minimal mitigation for EOL version” for PHP 8.1, 8.0 and 7.4 in the official php-src repository (for example PHP-8.1-CVE-2026-109618-109619). They aren’t an official release yet: there’s no package, the advisories don’t list them, and you have to build PHP from source. For a 2.4.5 store stuck on PHP 8.1, it’s a way to close the hole while you plan the upgrade. For 2.4.6, moving to PHP 8.2.35 is still the cleaner path. Thanks to Ümit Kayacan and Pradip Shah for pointing it out.
One more date to remember: php.net says PHP 8.2 “reaches end of life on 31 December 2026.” So 8.2.35 buys time, not a long-term home.
Patch it correctly
Updating the package is only half the job. Mark Shust made a good point about this on LinkedIn: the PHP that answers on the command line is not always the PHP that serves your pages.
1. Check what actually serves the store
Check the FPM binary, not only the CLI:
php -v # the CLI binary
php-fpm8.3 -v # Debian/Ubuntu naming; use your version
php-fpm -v # RHEL-based namingA common tip is to check from a real web request, with a temporary file that echoes PHP_VERSION. On a standard Magento setup that file won’t run. Magento’s sample nginx configuration only executes a short list of PHP files in pub/ (index.php, get.php, static.php, health_check.php and the error pages) and denies every other .php file.
So for Magento I check two things: the version of the FPM binary, and that the running service was started after the update:
systemctl status php8.3-fpm | grep Active # the start time must be after the update2. Update PHP
Use your distribution’s packages or your hosting provider’s update:
# Debian/Ubuntu: list the PHP packages you actually have installed
sudo apt-get update && sudo apt-get install --only-upgrade php8.3-fpm php8.3-cli php8.3-common
# RHEL-based
sudo dnf upgrade 'php*'Two warnings from Sansec and Mark Shust:
- Distribution packages often backport security fixes and keep the old version label. Check your vendor’s advisory or the package changelog, not only the number.
- If you install PHP from Remi’s repository, common on RHEL-based servers, the fix was not available there on October 21. Sansec suggests switching to your distribution’s packages or building PHP from source.
If you are on managed hosting or Adobe Commerce on Cloud, ask your provider to confirm the update and the restart.
3. Restart PHP
The old interpreter keeps running in memory until you restart it:
sudo systemctl restart php8.3-fpm # PHP-FPM, with your version
sudo systemctl restart apache2 # mod_php on Debian/Ubuntu (httpd on RHEL)4. Check again
Repeat step 1: the FPM binary must report the fixed version (or your vendor’s patched build), and the service start time must be after the update. If your server does execute arbitrary PHP files, a temporary file with <?php echo PHP_VERSION;, opened in the browser and deleted right after, is the most direct check.
If you can’t patch within the hour
Sansec lists temporary, partial mitigations. In its words: “None of them replaces the patch.”
Block multipart/form-data
Every known trigger arrives as a multipart/form-data request, which PHP parses before Magento runs. For nginx, in every server block that passes requests to PHP:
if ($http_content_type ~* "multipart/form-data") {
return 403;
}For Apache, in every VirtualHost that serves PHP:
RewriteEngine On
RewriteCond %{HTTP:Content-Type} multipart/form-data [NC]
RewriteRule ^ - [F]This breaks every file upload, including product images in the Magento admin. Sansec also warns that some stores submit cart or checkout forms as multipart, so place a test order after adding the rule, and remove it once PHP is updated.
Put the store behind Cloudflare, and lock the origin
Cloudflare deployed WAF rules against the known exploits for all customers, including the free plan. Sansec notes they only help if the origin refuses traffic that doesn’t come from Cloudflare. If an attacker knows your origin IP, they go around the WAF. Set the firewall to accept HTTP and HTTPS only from Cloudflare’s IP ranges.
Go offline or read-only
If you can take the store offline, stop PHP-FPM and serve a static maintenance page from the web server. Magento’s own maintenance mode does not help, because PHP parses the request before Magento checks the flag.
If you can’t go offline, Sansec suggests making the store read-only by allowing only GET and HEAD:
if ($request_method !~ ^(GET|HEAD)$) {
return 503;
}Customers can browse, but they can’t log in or place orders.
PHP settings
For PHP-FPM, Sansec gives these pool settings, for example in /etc/php/8.3/fpm/pool.d/www.conf:
php_admin_flag[session.upload_progress.enabled] = off
php_admin_value[memory_limit] = -1Reload the workers and confirm the values from a web request. Be careful with memory_limit = -1: it closes the memory-exhaustion path, but a single request can then use all of the server’s memory. Treat it as short-term and watch memory use. Sansec also notes that turning off upload progress alone is not enough.
The PHP advisory for CVE-2026-109619 adds two more options: on PHP 8.5, set fatal_error_backtraces=0 and restart every worker; and consider pm.max_requests=1, which makes each FPM worker serve a single request. That second one costs performance, so I would only use it for a short window.
Detecting exploitation
These attacks run below the application layer, so Magento’s own logs may show little. Sansec lists the signals Wiz reported:
- unusual multipart requests followed by memory-limit errors or repeated HTTP 500 responses
- unexpectedly large or malformed
PHPSESSIDresponse cookies - unexplained PHP worker restarts or churn
A spike of PHP fatal errors around memory_limit is worth a look:
grep -i "allowed memory size" /var/log/nginx/error.log /var/log/php*-fpm.log 2>/dev/null | tail -50Adjust the paths to your setup. With PHP-FPM behind nginx, these errors often show up in the nginx error log as “FastCGI sent in stderr”.
My own recommendation, beyond what the sources say: because CVE-2026-109618 can leak memory from other requests, I would treat any server that ran a vulnerable PHP on the public internet after the CVE IDs went public on October 20 as a candidate for secret rotation. That means admin passwords, integration tokens, payment and API keys, and the Magento encryption key if you have reason to believe the server was hit. Run a malware scan too, since an RCE leaves no trace in your code until someone uses it.
What this says about old bugs
The part that stays with me is the timeline. The weakness was known in some form for about twenty years. What changed in 2026 was the cost of weaponizing it. According to Sansec, Wiz reports that a single researcher built the exploit in about a week, using AI models to analyze failures and adapt it across PHP versions and applications.
For those of us who run Magento stores, that changes the patch math. The time between “an old weakness becomes exploitable” and “mass exploitation” is getting shorter. Servers stuck on an unsupported PHP version were always a risk. Now they are a risk that can be turned into a working exploit in days.
A checklist for today
- Check the PHP version that PHP-FPM actually runs, not only
php -v. - Update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or confirm your vendor’s backported fix.
- Restart PHP-FPM or Apache, then check the version and the service start time.
- Stores on 2.4.6 with PHP 8.1: move to PHP 8.2.35.
- Stores on 2.4.5: mitigate, build the EOL source branch, or take offline, and plan the upgrade.
- If you can’t patch now: block multipart, lock the origin behind Cloudflare, or go offline with a static page.
- Watch for memory-limit errors, 500s and odd
PHPSESSIDcookies. - After patching, consider rotating secrets on exposed servers.
How many PHP versions are you running across your Magento servers today, and how fast could you update all of them?
Sources
- PHP2Shell: Critical PHP RCE (CVE-2026-109618, CVE-2026-109619), Sansec Forensics Team, October 21, 2026
- php.net: release announcements for 8.5.12, 8.4.27, 8.3.36 and 8.2.35, October 21, 2026
- PHP security advisories: GHSA-w8hw-w9fx-j79w (CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- Patch the Planet: coordinated disclosure of critical vulnerabilities, OpenAI, October 21, 2026
- Adobe Commerce system requirements, Adobe Experience League
- Today’s PHP security release and how we’re strengthening PHP security, The PHP Foundation, October 21, 2026
- Mark Shust’s LinkedIn post on php2shell, October 21, 2026

Leave a Reply