On 21 October 2026 the PHP project released 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix two critical vulnerabilities: CVE-2026-109618 and CVE-2026-109619. Both can lead to remote code execution (RCE), both are rated 9.2 on CVSS v4.0, and both sit inside the PHP interpreter itself, so no application code of yours has to be buggy for you to be affected.
The bugs were nicknamed PHP2Shell. Details leaked a day before the coordinated disclosure, and attackers started probing right away. This post explains what is known, how to patch, and what the official interim mitigations do and do not cover.
The short version
- Update PHP now. The PHP team says: "There are no complete mitigations, updating is the only safe choice."
- Fixed versions: 8.5.12, 8.4.27, 8.3.36, 8.2.35.
- PHP 8.1 and older get no official fix. The PHP security team published minimal source backports for 7.4, 8.0 and 8.1, and some distributions or LTS vendors ship their own backports.
- Restart everything after updating: PHP-FPM, Apache, and long-running CLI processes such as queue workers.
- Assume probing is already happening. Amazon's honeypot recorded an attempt on 17 October, four days before disclosure.
What the two bugs are
PHP describes them in two GitHub advisories. The public descriptions are short.
| CVE-2026-109618 | CVE-2026-109619 | |
|---|---|---|
| Name | RCE via out-of-memory (OOM) corruption | RCE via stale global state |
| Advisory | GHSA-w8hw-w9fx-j79w | GHSA-6wmf-4rv5-p3rp |
| Severity | Critical, CVSS v4.0 9.2 | Critical, CVSS v4.0 9.2 |
| In plain words | When PHP runs out of memory while request data is only half set up, the cleanup afterwards treats that half-built data as valid. | State owned by one request can outlive the memory behind it. A later request on the same worker can use that dangling state before PHP resets it. |
| Bug class | Memory corruption | Use-after-free |
Researchers also showed that the second bug can be used to read PHP worker memory without running code. Worker memory can hold passwords, session data, tokens and API keys, so a successful attack is a credential leak even before any shell appears.
I am deliberately not describing exact trigger steps. The official advisories do not publish them, they are not fully public, and code claiming to be a proof of concept is circulating. The PHP Foundation says its team inspected the post-leak proofs of concept and found no functional exploits at release time. Treat any random "PHP2Shell exploit" repository as possible malware, not as a test tool.
A correction worth knowing: some early listings showed a CVSS score of 10.0. The PHP Foundation says that was incorrect. The score is 9.2.
Who is affected
According to the PHP advisory, every PHP installation that serves web requests is at risk:
- PHP-FPM (Nginx and Apache with FPM)
- Apache with
mod_php php-cgi- FrankenPHP
- LiteSpeed
PHP rates the risk highest for mod_php, where a demonstrated exploit lets an unauthenticated request trigger the attack before any application code runs. That is why updating WordPress or Laravel does not help: the bug lives below your framework.
Wiz Research reported that every PHP version back to 5.4 has at least one of the two issues, and demonstrated unauthenticated RCE against the latest WordPress, Drupal and Joomla container images. Some reports say that even a minimal "Hello World" app on a vulnerable version can be hit. Those are researcher claims, and the PHP advisory does not list affected app versions.
Fixed versions
| PHP branch | Fixed in | Notes |
|---|---|---|
| 8.5 | 8.5.12 | |
| 8.4 | 8.4.27 | |
| 8.3 | 8.3.36 | |
| 8.2 | 8.2.35 | PHP 8.2 reaches end of life on 31 December 2026. |
| 8.1, 8.0, 7.4 | No official release | Minimal source backports exist for people who build from source. Ask your distro or LTS vendor. |
| 7.3 and older | End of life | No fix from the PHP project. |
If you are on 8.1 or older, this is the moment to schedule an upgrade to a supported branch. Backports are a bridge, not a plan.
How to patch
1. Find out what you really run
php -v
php-fpm8.4 -v # replace 8.4 with your version
The CLI and the web server can run different PHP versions. Check both, and check inside containers.
ls /etc/php # Debian/Ubuntu: every installed PHP version
2. Update the packages
On Debian or Ubuntu with a PHP package repository, upgrade every package for each PHP version you have installed:
sudo apt-get update
apt-cache policy php8.4-fpm # "Candidate" should be the fixed version
sudo apt-get install --only-upgrade -y \
-o Dpkg::Options::="--force-confold" \
$(dpkg -l 'php8.4*' | awk '/^ii/ {print $2}')
Repeat for every installed PHP version, including ones no site currently uses. These commands come from the Laravel Forge advisory. If Candidate is not the fixed version yet, the patched packages have not reached your repository. Wait and retry.
Two things to know:
- Distributions backport. Your package may keep an older version number but still contain the fix. Check your vendor's security advisory or changelog for the CVE IDs instead of only comparing numbers.
- Some repositories lag. The PHP team warns that the volunteer-run Remi repository would not have updates on release day and recommends distribution packages or a source build until it does. Managed platforms may also not patch PHP automatically. Laravel Forge, for example, says its automatic security updates do not touch PHP, so you must update each server yourself.
3. Docker and containers
Rebuild, do not just restart. An old cached layer still contains the old PHP.
docker build --no-cache -t my-app .
Pin your base image to a patched tag, for example a php:8.4.27-fpm style tag once it is available for your variant. Check the tag exists on Docker Hub first.
4. Restart everything that holds PHP
Updating the package restarts PHP-FPM, but long-running processes keep the old interpreter in memory until they are restarted. That includes queue workers, Horizon, Octane, Reverb, Supervisor-managed scripts and cron-launched daemons.
php artisan queue:restart
php artisan horizon:terminate
php artisan octane:reload
5. Verify
php -v
php-fpm8.4 -v
Confirm both report the fixed version, or confirm in your vendor's changelog that the CVEs are listed as fixed.
If you cannot patch today
PHP is clear that these measures block specific paths only and do not make an installation safe. Use them to buy time, not as a substitute.
For CVE-2026-109618 (OOM):
- Set
memory_limit=-1where applications cannot override it, for example withphp_admin_valuein the FPM pool. Make sure no pool, directory or app setting re-applies a finite limit. - Lower
post_max_size(8M if you do not need uploads, 32M if you do) and keepupload_max_filesizebelow it. - Enforce request size and multipart limits at the web server or proxy, including requests without a usable
Content-Length. - If you use a memory cap, use a container or cgroup limit that kills only the affected worker. Do not use
RLIMIT_ASorRLIMIT_DATA: PHP says those can cause the vulnerable allocation failure.
For CVE-2026-109619 (stale state):
- Set
session.upload_progress.enabled=0inphp.ini, the FPM pool or the server config, then restart every worker. Do not rely onini_set()or.user.ini. - On PHP 8.5 only, also set
fatal_error_backtraces=0and restart workers. - Suppressing errors with
error_reporting,display_errorsor@does not help. - Fix fatal errors and execution timeouts that an attacker can reach.
An FPM pool example, adapted from the PHP advisory:
; php.ini or FPM pool configuration
session.upload_progress.enabled = 0
post_max_size = 8M ; or 32M if uploads are required
upload_max_filesize = 8M ; must stay below post_max_size
; FPM pool: applies before request parsing and cannot be overridden per app
php_admin_value[memory_limit] = -1
php_admin_value[session.upload_progress.enabled] = 0
Note the trade-off: an unlimited memory_limit means a runaway script can consume memory, which is why the cgroup limit is part of the advice.
At the edge: Cloudflare says it deployed WAF rules for known vectors on all plans, including free. Cloudflare calls this a stopgap, and it only helps if your origin accepts traffic solely from Cloudflare.
Network: CERT-EU recommends isolating internet-facing PHP applications until the fix is applied and verified.
Signs someone already tried
Wiz suggests these indicators. They are hints, not proof:
- Unusual multipart requests followed by memory-limit errors or repeated HTTP 500 responses.
- Unexpectedly large or malformed
PHPSESSIDcookies. - Unexplained PHP worker restarts.
Amazon recommends looking back at logs from 17 October onward. If you see these signs, update first, then treat the server as possibly compromised and rotate the credentials it held: database passwords, API keys, session secrets and tokens, because memory disclosure is possible.
What is still unknown
Being honest about the gaps matters as much as the fixes:
- Exact trigger conditions and exploit mechanics are not public in the official advisories.
- It is unclear whether any attempt seen on 21 October would succeed against a real system. Early reports describe probing, with no confirmed breach counts.
- Researchers say the flawed code goes back to PHP 5.4, but the PHP project's own table lists only supported branches.
- Numbers for exposed hosts (estimates above 1.3 million) are unverified.
My take
I mostly work with Node.js and NestJS, not PHP, but this is the kind of bug that hurts everyone who runs anything on a shared host or old server: the vulnerable component is the runtime, so your own code review does not help. Update the runtime, restart every long-lived process, and check the version inside each container.
I have not tested the exploits or the mitigations myself. This post summarizes the official PHP advisory and the vendor and researcher write-ups listed below. If anything here differs from php.net, php.net wins.
Sources
Working on something like this?
I take on backend, payments and deployment projects. I reply within one working day.