On 20 October 2026 at 19:33 UTC the PHP security team posted a notice with almost nothing in it: two critical fixes would land the next day at 14:00 UTC, no details, prepare now. Within hours the CVE identifiers were on a public forum. Cloudflare reported attacks at 07:43 UTC on 21 October — more than six hours before the patch existed. Amazon's threat intelligence team had already logged an attempt against a honeypot on 17 October, four days before any of it was public.
So the usual order was reversed. The exploit arrived before the fix. If you run PHP behind a web server, you were exposed by default and you still are until the version your web server actually loads has changed.
This is a do-this-now checklist, not a research writeup. The technical research is Calif.io's; every figure and setting below is linked to its source at the end.
Contents
- What php2shell is
- Are you affected?
- What to do today, in order
- If you cannot patch within the hour
- If you are on PHP 8.1 or older
- Patching is not the whole job
- The checklist
What php2shell is
Two separate memory-safety bugs in PHP itself, chained together by researchers into unauthenticated remote code execution. Both are rated CVSS v4.0 9.2 by the PHP security team, on the vector AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N.
- CVE-2026-109618 — corruption during out-of-memory handling. When a request runs out of memory while data is only partly initialised, the cleanup that follows treats that data as valid.
- CVE-2026-109619 — stale global state, a use-after-free. State left behind by one request points at freed memory, and a later request landing on the same worker can reach it. PHP lists memory disclosure as a confirmed impact as well as code execution.
Two details decide how worried to be. The code runs before your application does, so no login, no route and no framework check is in the way — your Laravel middleware never sees the request. And the second bug's disclosure path can hand freed worker memory back to the client, which is why this is not only a code-execution problem: that memory can contain session tokens, API keys and TLS private keys.
The PHP security team said it had identified no functional public exploit at release. Rapid7 reported multiple independent reproductions the same day and expected a working public exploit shortly. Treat the gap between those two statements as hours, not weeks.
Are you affected?
Assume yes. Default configurations are affected, the vulnerable code path dates back to PHP 5.4.0, and CISA's alert simply says the flaws affect all PHP installations serving web requests.
- mod_php is the worst case. The vendor rates it highest risk: exploitation can succeed before any application code runs, over plain HTTP, with default limits.
- PHP-FPM is affected too, by a different path. For the stale-state bug it needs an operation that leaves state behind, a fatal error, and a follow-up request reaching the same worker.
- php-cgi, FrankenPHP and LiteSpeed are also listed.
- PHP you forgot you were running counts. PHP bundled inside an appliance, a monitoring tool, a legacy subdomain, a staging box with a public IP, an old WordPress install behind a redirect. CISA's first instruction is to inventory, including PHP shipped inside other software.
What to do today, in order
- Find out what your web server actually loads, not what your shell says.
php -vreports the CLI binary, which on most servers is a different build from the one serving requests. Check the running service —php-fpm8.3 -v,systemctl status php*-fpm,apachectl -M | grep php— and then confirm over HTTP, because that is the only answer that matters. - Update to a fixed release. One of these four, by branch:
Branch Fixed in PHP 8.5.x 8.5.12 PHP 8.4.x 8.4.27 PHP 8.3.x 8.3.36 PHP 8.2.x 8.2.35 - Do not compare upstream version numbers to your distribution's. On Debian, Ubuntu, RHEL and friends the fix arrives as a backport, so your package may still report
8.3.6with the patch applied. Read your vendor's advisory and compare the package version, not the PHP one. Distributions and LTS vendors were given the patches in advance. One exception worth knowing: the Remi repository's RPMs were not expected to be updated on release day, so check before you trust adnf updatethere. - Restart every worker, then verify through the web server. A package upgrade does not swap the code inside a running FPM worker. Restart or reload the service, restart Apache if you use mod_php, and then confirm with a request, not a shell: a temporary file printing
phpversion(), your existing health endpoint, or theX-Powered-Byheader if you have not turned it off. Delete the temporary file afterwards. - Do the internet-facing hosts first, then everything else. CISA's prioritisation, and the right one: the bug needs nothing but a reachable request.
- Check your CI images and base images, not just your servers. A pinned
php:8.3-fpmtag in a Dockerfile will keep rebuilding the vulnerable version until you move the tag and redeploy. Anything built from a cached layer is still vulnerable after you patched the host. - If you are on managed hosting, verify rather than assume. Forge, Ploi, Plesk, cPanel and the cloud platform PHP runtimes each have their own timeline. Trigger the update yourself where you can, and check the version over HTTP per site — a server with four sites can easily run three PHP versions.
If you cannot patch within the hour
These reduce exposure. None of them is a fix, and each covers only the paths that were confirmed, so treat them as something you do on the way to patching.
- Set
session.upload_progress.enabled=0administratively — in php.ini, an FPM pool, or server config that applies before request parsing. Restart all workers. The trap:ini_set()in your application and.user.iniboth run too late to help. - On PHP 8.5, also set
fatal_error_backtraces=0administratively, and restart workers. - For the out-of-memory bug, PHP's own guidance is
memory_limit=-1, set so applications cannot override it, with workers restarted and no remaining setting that restores a finite limit. The trap: you have just removed PHP's memory cap, so put bounds back somewhere else —post_max_size=8Mif you do not need uploads (32Mif you do, withupload_max_filesizebelow it), and limits on request size, input and multipart counts and nesting depth at the front end. An allocation failure can still reach the vulnerable path. - Do not reach for
RLIMIT_ASorRLIMIT_DATA. They can trigger the vulnerable path rather than contain it. Container and cgroup memory limits help a little and are explicitly incomplete. - Block
multipart/form-dataat the edge only if your application does not use uploads. Every known trigger is a multipart request, which PHP parses before your code runs. It will break file uploads and some checkout flows, so know what you are switching off. - A WAF is a stopgap, not a patch. Cloudflare shipped rules for known vectors on all plans including the free one, and says so itself — and it only sees traffic routed through it.
- If none of that is possible, take it offline. That is CISA's advice for anything you cannot patch, and for anything on 8.1 or earlier. Note that a framework's maintenance mode does not help: the request never reaches the framework. Stop PHP-FPM and serve a static page, or restrict to GET and HEAD.
And one honest warning from the vendor guidance: hiding diagnostics with error_reporting, display_errors, log_errors or @ does nothing to prevent stale-state access. Quiet logs do not mean a safe server.
If you are on PHP 8.1 or older
There is no official release for you. The PHP security team published minimal source backports covering 7.4 through 8.1 for people who build from source; 7.3 and earlier get nothing from the project. Your distribution may still ship a fix for an older branch under its own support terms, so check its advisory first.
If you cannot build from source and your vendor has nothing, the realistic options are: isolate the host from the internet until the branch upgrade is done, or take it offline. A PHP 8.1 application is a branch upgrade you were going to have to do anyway — this is the week it stopped being optional.
Patching is not the whole job
Exploitation reports came in before the patch did, so patching tells you nothing about what happened last week. The reports also disagree, which is worth holding both halves of: CISA had not observed exploitation as of its 21 October alert, while Amazon reported attempts from 17 October and said on release day that php2shell was being broadly exploited, and watchTowr reported attempts against both CVEs.
- Look for PHP workers doing things PHP workers do not do — spawning a shell, running
curlorwget, making unexpected outbound connections. That is CISA's suggested signature. - Diff your web roots. New or modified PHP files, especially in writable upload and cache directories.
- Rotate anything the worker's memory held if you have reason to think you were hit: session secrets, API keys, database credentials, and TLS private keys if the key is loaded in the same process. The disclosure path is what makes this different from an ordinary RCE.
- Write down the time you patched each host. It is the one thing you will need later and the easiest thing to lose.
The checklist
- Inventory every host, container, image and appliance running PHP, including PHP you did not install yourself.
- Identify the version the web server loads, per site, over HTTP.
- Patch to 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35, or your vendor's equivalent package.
- Internet-facing first.
- Restart every worker. Verify over HTTP, not from the shell.
- Rebuild and redeploy container images; move pinned tags.
- On 8.1 or older: isolate or take offline, then plan the branch upgrade.
- Interim only:
session.upload_progress.enabled=0,fatal_error_backtraces=0on 8.5,memory_limit=-1with request-size bounds moved to the front end. - Hunt for compromise, diff the web root, rotate exposed secrets.
- Record the patch time per host.
FAQ
Is PHP 8.1 getting a fix?
Not an official release. The PHP security team published minimal source backports for 7.4 through 8.1, which assume you build from source. Your Linux distribution may ship a patched package for an older branch under its own support policy — check its advisory rather than php.net.
I run PHP-FPM, not mod_php. Am I safe?
No. mod_php is rated the highest risk, but FPM is affected by a different path, and php-cgi, FrankenPHP and LiteSpeed are listed too. The difference changes your patching order, not whether you patch.
Does my framework's authentication protect me?
No. The vulnerable code runs while PHP is parsing the request, before your application boots, so routing, middleware and authentication never see it.
Is disabling upload progress enough?
No. It covers the confirmed session paths of one of the two bugs. Updating is the only complete fix — that is the vendor's position, not a cautious reading of it.
My distribution's package still shows an old version number. Is it patched?
Possibly. Distributions backport the fix without changing the PHP version string, so compare the package version against your vendor's advisory instead of against the upstream release numbers.
Where do these facts come from?
The PHP security team's pre-release notice and advisory, CISA's alert of 21 October 2026, and the write-ups from Rapid7, Tenable and Sansec. All are linked below. Everything here is a snapshot of 22 October 2026 — on a story moving this fast, check the current advisory before acting on a detail.
Sources
- PHP: upcoming critical security release notice (20 Oct 2026)
- CISA: critical vulnerabilities in PHP require immediate patching (21 Oct 2026)
- Rapid7: CVE-2026-109618 and CVE-2026-109619
- Tenable: php2shell FAQ
- Sansec: php2shell research and mitigation steps
Working on something similar?
If you are patching a fleet this week and the inventory is the hard part, I am happy to look at it with you. Thirty minutes, no deck — bring the estate you cannot see and we will work out how to make it list itself. Get in touch.