php2shell (CVE-2026-109618): how to check if your PHP is vulnerable and update it
Table of Contents
php2shell is the name given to two critical vulnerabilities in the PHP core, CVE-2026-109618 and CVE-2026-109619, disclosed on October 21, 2026. Both score 9.2 on CVSS v4 and allow unauthenticated remote code execution. The fixed releases are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. If your PHP is below the fixed release for its branch, update today: PHP’s own advisory says there are no complete mitigations and updating is the only safe choice.
This guide covers how to find the version that actually serves your traffic (often not the one your shell reports), how to update depending on where PHP lives, the Docker redeploy trap I walked into on my own production server the day the advisory came out, what to do if you can’t update today, and where PHP 8.1 and older stand. There’s a short section at the end for business owners who won’t be opening a terminal.
What php2shell is
Two separate bugs, fixed in the same release:
- CVE-2026-109618 (GHSA-w8hw-w9fx-j79w): if PHP runs out of memory while a request’s internal data is only partly initialized, request cleanup can treat that half-built data as valid.
- CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp): some request-owned state can outlive the memory behind it, and a later request handled by the same worker can use it before normal initialization resets it.
According to php.net, every PHP installation that serves web requests is in scope: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed, with mod_php rated highest risk. You don’t need the technical details to patch; they’re in the two GitHub advisories if you want them.
The name comes from a proof-of-concept tool that surfaced on October 20, before the embargo ended. That leak is why the PHP team moved the release up a day.
Fixed versions
| Branch | Fixed release | Security support ends |
|---|---|---|
| 8.5 | 8.5.12 | Dec 31, 2029 |
| 8.4 | 8.4.27 | Dec 31, 2028 |
| 8.3 | 8.3.36 | Dec 31, 2027 |
| 8.2 | 8.2.35 | Dec 31, 2026 |
| 8.6 (pre-release) | 8.6.0 RC4 | not for production |
| 8.1 and older | no official release | see PHP 8.1 and older |
Sources: the php.net security advisory and supported versions. If you’re on 8.2, today’s release covers php2shell, but the branch stops getting security fixes in about ten weeks. Plan the next hop now; if you’re heading to 8.6 later, here’s what breaks.
How to tell if your PHP is vulnerable
Compare your version against the table. The hard part is knowing which version is yours.
On a server:
php -v # the CLI binary
php-fpm -v # or php-fpm8.4 -v depending on the distro: the one serving the web
Boxes with several PHP versions installed often have a CLI on 8.4 and FPM still on 8.2. What matters is the binary handling requests: PHP-FPM, mod_php, or whatever your web server talks to.
On Docker, check the running container, not your laptop and not the image you think you shipped:
docker exec <container> php -v
docker exec <container> php-fpm -v
From the app, if you have no shell: drop a temporary file containing <?php echo PHP_VERSION; at an unguessable path, load it once, delete it. Never leave a phpinfo() page up.
Distro packages keep the old version number
Debian, Ubuntu, Red Hat and friends usually backport security fixes into their own packages without bumping PHP’s upstream version. A Debian PHP 8.2 can report something below 8.2.35 and still be fixed. The table doesn’t apply there; the package changelog does:
# Debian / Ubuntu
apt changelog php8.2-fpm | grep -E 'CVE-2026-10961[89]'
# RHEL, Alma, Rocky
rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961[89]'
Both CVE IDs listed means the package carries the fix.
How to update, by environment
Docker
Official images are out: php:8.4.27-*, php:8.5.12-*, php:8.3.36-* and php:8.2.35-* on Docker Hub, plus serversideup/php:8.4.27-*. The sequence that works:
- Pin the patch release in
FROMwhere you can (php:8.4.27-fpm). A floating tag likephp:8.4-fpmonly helps if every host and CI runner pulls it again. - Rebuild against the fresh base:
docker build --pull ...forces the new base image down. - Ship a genuinely new image, and make sure your platform actually rebuilds (more on that below).
- Verify inside the container with
php -vandphp-fpm -v. A green deploy tells you nothing about which PHP ended up running.
If you maintain your own base image on top of the official one, that’s two rebuilds: base first, then the app.
Package-managed servers (apt, dnf)
php.net says distributions and LTS vendors got the patches ahead of time, so packages should be there:
# Debian / Ubuntu
sudo apt update && sudo apt install --only-upgrade 'php8.4*'
sudo systemctl restart php8.4-fpm # plus apache2 if you use mod_php
# RHEL and derivatives
sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm
Swap 8.4 for your branch. The restart is part of the fix: workers that were already running keep the old code in memory until they’re replaced.
If you install PHP from the volunteer-run Remi repository (rpms.remirepo.net), the php.net advisory asks you to move to distribution packages or build from source, since that repo wasn’t getting updates on October 21.
Shared hosting (cPanel, Plesk)
On shared hosting, your provider patches PHP, not you. What you can do:
- Check the version under Select PHP Version or MultiPHP Manager in cPanel, or the domain’s PHP settings in Plesk.
- If you’re on an old branch, move to 8.3, 8.4 or 8.5 from the same screen, after confirming your site runs on it.
- Ask support, in writing, whether the fixed release is already on your server.
WordPress
WordPress doesn’t ship PHP; it uses the server’s. Updating WordPress core or plugins won’t fix php2shell. To see the version your site runs, go to Tools → Site Health → Info → Server. If it’s below the fixed release, the fix goes through your host or your own server.
Before jumping branches (8.1 to 8.3, say), check that your theme and plugins support the target. That’s where old sites tend to break.
The redeploy trap: what happened on my server on October 21
My backend runs in Docker on serversideup/php:8.4-fpm-nginx, deployed by Coolify from CI. Before touching anything, production said this:
+ docker ps --filter name=jd0k… --format "{{.Names}} {{.Image}} {{.Status}}"
jd0k…-20261021T001519 jd0k…:f2863d8… Up 18 hours (healthy)
+ docker exec … php -v
PHP 8.4.26 (cli) (built: Oct 6 2026 01:25:30) (NTS)
Copyright (c) The PHP Group
Built by https://github.com/docker-library/php
Zend Engine v4.4.26, Copyright (c) Zend Technologies
with Zend OPcache v8.4.26, Copyright (c), by Zend Technologies
8.4.26, vulnerable. I rebuilt my base image on 8.4.27, published it and pulled it onto the host. The new base had the fixed release:
+ docker pull ghcr.io/ortamarco/ombackend-php-base:8.4
8.4: Pulling from ortamarco/ombackend-php-base
…
Digest: sha256:8a2f70…
Status: Downloaded newer image for ghcr.io/ortamarco/ombackend-php-base:8.4
+ docker run --rm --entrypoint php ghcr.io/ortamarco/ombackend-php-base:8.4 -v
PHP 8.4.27 (cli) (built: Oct 21 2026 15:53:31) (NTS)
Then I re-ran the deploy from CI. Green. New container up and healthy. And then I checked:
# 12:28, after pulling the 8.4.27 base and re-running CI (same commit f2863d8):
+ docker ps --filter name=jd0k… …
jd0k…-20261021T182804 Up About a minute (healthy)
+ docker exec … php -v
PHP 8.4.26 (cli) (built: Oct 6 2026 01:25:30) (NTS)
+ docker exec … php-fpm -v
PHP 8.4.26 (fpm-fcgi) (built: Oct 6 2026 01:25:30) (NTS)
Still 8.4.26. Coolify saw a deploy for the same commit and, because CI requests it with force=false, reused the image it had already built for that commit. The new base was sitting on the host and nothing used it.
A new commit forced the rebuild. After that deploy:
+ docker ps --filter name=jd0k… --format "{{.Names}} {{.Image}} {{.Status}}"
jd0k…-20261021T183708 jd0k…:cac8d90… Up 30 seconds (healthy)
+ docker exec … php -v
PHP 8.4.27 (cli) (built: Oct 21 2026 15:53:31) (NTS)
+ docker exec … php-fpm -v
PHP 8.4.27 (fpm-fcgi) (built: Oct 21 2026 15:53:31) (NTS)
8.4.27 in both CLI and FPM. Later the same day I moved the backend to PHP 8.5.12.
What I took from it, and it applies to anything that deploys images (Coolify, a homegrown pipeline, Kubernetes):
- A floating tag (
:8.4) lives in host and CI caches. Pulling it doesn’t help if the platform decides not to rebuild. - Redeploying the same commit may reuse the previous image. Force a rebuild (a force-rebuild option,
--no-cache,--pull) or push a new commit. - The only evidence that counts is
php -vinside the container serving traffic. I had “deploy succeeded” and “container healthy” on the vulnerable version.
If you can’t update today
The php.net advisory lists measures that reduce exposure to specific attack paths but do not make an installation safe. Use them to buy hours, not as the fix:
session.upload_progress.enabled=0, then restart every worker. Set it inphp.ini, the FPM pool or server config;ini_set()and FPM.user.iniapply too late to help.memory_limit=-1, ideally viaphp_admin_valuein the FPM pool or Apache config so the app can’t override it, and check that nothing else sets a finite limit again. Don’t substituteRLIMIT_ASorRLIMIT_DATA: the advisory warns they can trigger the very allocation failure you’re trying to avoid. Use a container or cgroup memory limit as the availability boundary instead.- Low
post_max_size:8Mif the app takes no uploads,32Mif it does, withupload_max_filesizebelowpost_max_size. - PHP 8.5 only:
fatal_error_backtraces=0, then restart workers.
Hiding diagnostics with error_reporting, display_errors or @ does not help.
CISA adds that a WAF may provide some protection until you patch, and that systems you can’t update, including anything on PHP 8.1 or earlier, should be taken offline until they’re fixed.
PHP 8.1 and older
The PHP project no longer ships releases for 8.1, 8.0 or 7.4, so there’s no official 8.1.x carrying the fix. What does exist:
- Given the severity, the PHP security team published minimal source backports for PHP 7.4, 8.0 and 8.1, for people who build PHP from source.
- Some distributions and extended-support vendors backport the fix into their packages for older branches. Ask yours, or check the package changelog as shown above.
- The real exit is a supported branch. On an old codebase that’s rarely just a version bump: the framework and dependencies come along, which is why it’s not an afternoon job.
For business owners
If your online store, customer portal or internal system runs on PHP (WordPress, Laravel, something custom), send whoever maintains it these three questions today:
- Which PHP version does my system run?
- Has it been updated to the October 21 fixed release? Ask for the
php -voutput as proof. - If it’s PHP 8.1 or older, what’s the plan, and by when?
On shared hosting, the question goes to your host.
If you’d rather hand it off: I do remote legacy PHP and Laravel rescue, starting with a fixed-price 5-day assessment (US$590, credited in full if you go ahead with the stabilization), which is the right starting point for anything on PHP 8.1 or older. For clients in Mexico I also offer a 24-business-hour urgent PHP update (page in Spanish, billed in MXN: $2,900 + IVA per server or site, PHP 8.2 to 8.5): backup, update to the fixed release, testing and php -v as evidence.
Frequently asked questions
What is php2shell?
php2shell is the name for two critical vulnerabilities in the PHP core, CVE-2026-109618 and CVE-2026-109619, disclosed on October 21, 2026. Both are rated 9.2 on CVSS v4 and allow unauthenticated remote code execution on PHP installations that serve web requests, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed.
Which PHP versions fix php2shell?
PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35, plus PHP 8.6.0 RC4 for anyone testing the pre-release. php.net says there are no complete mitigations and updating is the only safe choice.
How do I check whether my PHP is vulnerable?
Run php -v and php-fpm -v on the server, or docker exec <container> php -v on Docker, and compare against the fixed release for your branch. Debian, Ubuntu and Red Hat packages often keep the old version number after backporting, so check the package changelog (apt changelog or rpm -q --changelog) for CVE-2026-109618 and CVE-2026-109619.
Is there a php2shell patch for PHP 8.1, 8.0 or 7.4?
There is no official release, because those branches no longer receive updates from the PHP project. The PHP security team published minimal source backports for 7.4, 8.0 and 8.1 for people who build from source, and some distributions and extended-support vendors backport the fix. The long-term answer is moving to a supported branch.
Is a WAF or a php.ini change enough?
No. The measures in the php.net advisory (disabling session.upload_progress, memory_limit=-1, a low post_max_size and, on PHP 8.5, fatal_error_backtraces=0) reduce exposure to specific attack paths but do not make an installation safe. CISA says a WAF may offer some temporary protection. Only updating fixes the flaws.
Does php2shell affect WordPress?
It affects the PHP that WordPress runs on, not WordPress itself. Updating WordPress or plugins does not fix it; PHP has to be updated on the server or by the host. You can see the version under Tools, Site Health, Info, Server.
Is php2shell being exploited?
When CISA published its alert on October 21, it said it had not observed exploitation. The security firm watchTowr reported exploitation attempts against both flaws. A proof of concept circulated on October 20, before the embargo ended, so do not wait.
I updated my Docker image and redeployed. Am I covered?
Not necessarily. A floating tag like php:8.4 sits in caches, and some platforms reuse the already-built image when the commit has not changed. Run php -v and php-fpm -v inside the container serving traffic; if it still shows the old version, force a rebuild or deploy a new commit.
Sources
- php.net: Security advisory: Critical remote code execution vulnerabilities, update now (October 21, 2026).
- GitHub, php-src: GHSA-w8hw-w9fx-j79w, CVE-2026-109618 and GHSA-6wmf-4rv5-p3rp, CVE-2026-109619.
- CISA: Critical Vulnerabilities in PHP Require Immediate Patching (October 21, 2026).
- watchTowr: PHP2Shell: PHP Remote Code Execution.
- php.net: supported versions.
Found it useful? Get the next one by email
Once a week: what breaks when you upgrade, AI for developers and what I'm building, with sources. No spam.
By subscribing you accept our privacy policy.