php2shell patches (CVE-2026-109618 and CVE-2026-109619) for EOL PHP are out now. With sales replies slower than usual due to high demand, the fastest route to patched packages is to buy ELS for Runtimes online (opens in new tab). Read the patch FAQ

Go straight to an AI summary of this blog post instead

  • Summarize this post with Perplexity
  • Summarize this post with Gemini

Key Takeaways

  • php2shell is the name for two critical PHP core RCE flaws, CVE-2026-109618 and CVE-2026-109619.
  • PHP fixed them in 8.5.12, 8.4.27, 8.3.36 and 8.2.35, but end-of-life branches get no upstream patch.
  • Attack attempts were reported before the release, so patch now and restart every PHP worker.
  • TuxCare’s Endless Lifecycle Support (ELS) for Runtimes has fixed builds available now for end-of-life PHP versions.

ELS Patches for php2shell Are Available Now

Fixed PHP builds for CVE-2026-109618 and CVE-2026-109619 are available in the ELS for Runtimes repositories for branches php.net no longer patches.

Get ELS for Runtimes →

The Short Version

Jump to the Long Version ↓

What it is: php2shell is the nickname researchers gave CVE-2026-109618 and CVE-2026-109619, two critical remote code execution vulnerabilities in PHP itself, not in any framework or CMS. News of them leaked on October 20, 2026, ahead of the planned disclosure, and the PHP security team pulled its emergency release forward to October 21.

Impact: Both can let an unauthenticated attacker run code as the PHP worker’s user, though how exploitable a given server is depends on how PHP runs there. PHP confirmed full RCE against Apache mod_php with default limits and validated one path against PHP-FPM as well. Jump to this section →

Severity: The PHP security team rates both 9.2 under CVSS v4.0, which is critical. The 10.0 that circulated with the leak was never PHP’s rating. Jump to this section →

Who is affected: PHP’s advisories cover affected releases in all four supported branches, while the leaked exploit tool claims to work as far back as 5.4.0. Upstream only patches 8.2 through 8.5, so PHP 8.1, 8.0, 7.x and 5.x get nothing from php.net, and Mallory counted roughly 884,000 internet-facing hosts advertising one of those versions. Jump to this section →

Status: Cloudflare CTO Dane Knecht reported early attack attempts against PHP sites at 07:43 UTC on October 21, hours before the release, though how many succeeded isn’t clear. Fixed Endless Lifecycle Support (ELS) for Runtimes builds covering both CVEs are available now for every end-of-life branch from PHP 5.2 through 8.1.

Do this now: Find every PHP install you own, including the copies bundled inside vendor software and control panels, and start with anything running Apache mod_php without skipping FPM hosts. Update supported branches from upstream or your distro, and move end-of-life ones onto a fixed ELS build or take them off the public internet. Then restart or replace every PHP worker, because the old code keeps serving requests until that process is gone. To see what’s actually running:

ps aux | grep -E '[p]hp-fpm|[p]hp-cgi|[l]sphp|[h]ttpd|[a]pache2'

While you work through the list, watch for PHP worker processes launching shells or download tools. That’s what exploitation tends to look like.

Jump to this section →

The Long Version

Most PHP emergencies start with an app. A WordPress plugin, a Magento extension, a Laravel package nobody has touched since 2019. You update it, rotate a few secrets and move on. None of that touches the interpreter underneath.

php2shell doesn’t work like that. The vulnerabilities sit in the interpreter, so the affected software is roughly “anything that runs PHP,” and the fix only lands on four maintained branches. Anyone still running PHP 7.4 under a legacy billing app (and there are plenty of them) found out about a critical RCE with no upstream patch coming, ever.

There’s a third wrinkle, too. These bugs went public as rumors before they went public as patches, and attackers started probing before the fixes arrived.

How a Leak Rewrote the Schedule

The current CVSS 4.0 score from NIST is 9.2 (Critical), but there were other unconfirmed numbers floating around. On the morning of October 20, a post on Hacker News claimed two PHP remote code execution flaws, rated 10.0 and reaching back to PHP 5, would be disclosed on Thursday, October 22. The poster offered no evidence beyond “source: trust me, bro.” The post came down, but screenshots were already everywhere.

This rumor didn’t die, because watchTowr posted that the information was real, the bugs were under embargo and everyone was supposed to wait until Thursday. At 19:33 UTC that evening, Jakub Zelenka sent the PHP security team’s advance notice to php.announce. It confirmed two critical vulnerabilities, both 9.2 under CVSS v4.0, with a release set for the next day, October 21, and reserved the right to ship earlier if details went public or exploitation showed up.

The embargo lost a day, and plenty of other people were reading the same screenshots. By 07:43 UTC the next morning, Cloudflare’s CTO said the company was seeing early attacks emerging, and its stopgap WAF rules went out to every plan, Free included.

PHP’s advance notice didn’t name either CVE, by the way. The identifiers first circulated through the leaked material and watchTowr’s reporting, and PHP’s advisories confirmed them when the fixes shipped.

What PHP’s Advisories Actually Say

Before the release, the public picture was thin. watchTowr said it believed it had reproduced CVE-2026-109618, Huntress hadn’t built a proof of concept, and per Sansec the leaked PHP2Shell screenshot showed a banner claiming coverage of every release from 5.4.0 on. PHP’s two advisories on October 21 filled in most of the blanks.

CVE-2026-109618, “RCE via OOM corruption,” comes down to how PHP bails out of out-of-memory errors. It uses longjmp() to abort the request, and if any allocation fails while request memory is only partly initialized, the shutdown code later reads or branches on that broken state. Multipart parsing hands attackers several ways to hit OOM while staying inside apparent limits, such as parts with an unbounded number of headers. On mod_php it all happens during request startup, before any application code runs.

CVE-2026-109619, “RCE via stale global state,” is a use-after-free that spans requests. A fatal error, OOM included, can leave a persistent worker holding pointers into memory that belonged to a request that’s already gone. A later request on the same worker then touches that stale state before normal initialization resets it. It’s fussier to pull off, but PHP still confirmed RCE against mod_php and validated the session path against PHP-FPM.

So how you run PHP matters. mod_php is the worst case for both. FPM kills a child when request startup fails, which blocks the cleanest OOM sequence, but PHP still treats it as susceptible. PHP calls FrankenPHP likely susceptible to application-independent OOM attacks, much like mod_php, and LSAPI behaves more like FPM.

The Upload Progress Connection

That 5.4.0 banner turned out to be a good clue. PHP 5.4 introduced session upload progress, which lets a script poll how far along a multipart upload is. It’s on by default, and PHP’s own advisory describes it as hardly used. It’s also a confirmed consumer in CVE-2026-109619, because it reaches session handling before normal initialization and can pick up state an interrupted request left behind, no session_start() call needed. One researcher’s analysis had pointed at a use-after-free in this code path before the advisories landed, and it was close to the mark.

Switching the feature off still isn’t a fix. Gina Banyard of the PHP core team said early on that patching is the only mitigation, and both advisories back her up, calling an update the only complete or reliable one. Disabling upload progress blocks the demonstrated session path but none of the other lifecycle paths.

If some hosts can’t be patched today, PHP’s stopgap for that path goes in php.ini or a drop-in under conf.d, and Sansec adds strict session mode on top:

session.upload_progress.enabled = 0
session.use_strict_mode = 1

FPM pools can override the main INI settings, so check each pool’s effective configuration. Where a pool needs its own override, use these, then restart its workers:

php_admin_flag[session.upload_progress.enabled] = off
php_admin_flag[session.use_strict_mode] = on

Strict session mode is something PHP’s own session security guidance has pushed for years, so that half costs almost nothing, though Redis or database session handlers may not implement the ID check it relies on. Other stopgaps include fatal_error_backtraces=0 on PHP 8.5 and blocking multipart requests at the frontend if the app never takes uploads. For CVE-2026-109618, PHP’s main configuration stopgap is enforcing memory_limit=-1 administratively and restarting every worker, which removes the predictable per-request limit the confirmed exploit relies on. Handle that one carefully, since it also drops PHP’s guard against runaway scripts, and PHP says it isn’t complete. None of these is a fix. Treat them as speed bumps until every host is patched.

Why End of Life PHP Is the Bigger Problem

PHP’s supported versions page is blunt about this. Branches 8.4 and 8.5 are in active support, 8.2 and 8.3 receive security fixes only, and everything older is end of life. EOL branches get no security releases from php.net, and a critical RCE doesn’t change that.

The numbers on what’s still deployed are not pretty. Mallory counted about 884,000 internet-facing hosts whose HTTP banner reported PHP 8.1 or older on October 20, about 59% of the hosts that reported a version at all. Banners aren’t confirmed exposure, and some of those hosts run backported packages. It’s still a staggering number of internet-facing hosts reporting outdated PHP.

The installs that hurt are the ones nobody thinks of as PHP servers. Vendor appliances. Windows software that ships its own php-cgi.exe (ELS for Runtimes has Windows PHP builds too, for exactly this reason). Control panels. An internal wiki someone stood up in 2017 and never mentioned again.

Picture a 30-person agency hosting a few hundred client WordPress sites on one cPanel server, with half the accounts pinned to PHP 7.4 because a premium theme falls apart on 8.x. Nobody is migrating all of those sites to PHP 8 by Friday. Upgrading PHP is an application project, and that’s exactly why so many PHP 7.x installs are still running.

This is the gap ELS for Runtimes exists to cover. TuxCare backports security fixes into end-of-life branches, so a site pinned to 7.4 gets the php2shell fix without a forced PHP 8 migration during an active RCE. The migration still has to happen. It just gets to happen on a schedule someone chose.

Who Is Affected?

Where the php2shell fix comes from, by branch, with PHP’s own support dates:

PHP branch Upstream status Where the php2shell fix comes from
8.5 Active support until December 31, 2027, security fixes until December 31, 2029 PHP 8.5.12
8.4 Active support until December 31, 2026, security fixes until December 31, 2028 PHP 8.4.27
8.3 Security fixes only until December 31, 2027 PHP 8.3.36
8.2 Security fixes only until December 31, 2026 PHP 8.2.35, then nothing after that date
8.1 and 8.0 End of life No official release. ELS for Runtimes, or distro backports where offered
7.0 through 7.4 End of life No upstream fix. ELS for Runtimes, or distro backports where offered
5.x End of life No upstream fix. The leaked tool claims 5.4.0 and later. ELS for Runtimes covers 5.x builds, with exact branches varying by platform

Both advisories list 8.5.12, 8.4.27, 8.3.36 and 8.2.35 as the patched releases, and PHP’s changelog has the per-branch details. Install yours, then confirm the processes serving requests actually run it.

If PHP came from your Linux distribution’s own repositories, its advisory is the one that counts. Distros often backport fixes without moving the upstream version number, so a version string by itself settles nothing.

Get ELS for Runtimes today →

PHP Has Been Here Before

In May 2012, details of CVE-2012-1823, a remote code execution bug in php-cgi, were accidentally made public before the fix was ready. PHP rushed out 5.3.12 and 5.4.2. Stefan Esser and others quickly showed the fix could be bypassed, and PHP had to ship again days later, as Computerworld reported at the time.

Then there’s CVE-2024-4577, the Windows php-cgi argument injection from June 2024. Akamai saw exploit attempts against its honeypots within 24 hours of disclosure, according to The Hacker News.

Leaked bugs compress everyone’s timeline, including the developers writing the fix, and compressed fixes sometimes need a second pass. Keep watching the advisories for a few days after you patch.

Watch What PHP Spawns

Interpreter-level RCE tends to look the same no matter how the bug works. A PHP worker such as php-fpm, php-cgi, lsphp or an Apache process starts something it never normally starts. Shells, Python or Perl, curl or wget. Command lines containing /dev/tcp/ or mkfifo are classic reverse shell tells, and so is a php -r one-liner calling fsockopen. A burst of whoami, id or uname -a running as the web server user is someone doing recon. None of those is proof on its own, since admin tooling does some of the same things. What matters is an odd process tree that lines up with a request or a freshly written file.

On Linux servers without endpoint detection, Florian Roth’s free process-watcher logs every program a PHP process starts, with its full command line. Roth is upfront that it prevents nothing – it only sees. For a quick point-in-time look in the meantime:

ps -eo pid,ppid,user,args --forest | grep -A3 '[p]hp-fpm'

If something turns up, isolate the server and preserve web server and PHP logs before anyone “cleans up.” A clean access log isn’t an all-clear, since default logs don’t record POST bodies. Look for webshells, new files in the web root, new cron jobs and new accounts. Patching a compromised host closes the door with the intruder still inside. Where patching can’t happen right away, CERT-EU recommends considering taking internet-facing applications off public networks until a fix is in place. A WAF or VPN in front of an old branch buys time, and that’s all it buys.

Patch and Verify Every PHP Install

Inventory comes first, and the list is usually longer than anyone expects. This loop checks the usual Linux locations, ELS builds under /opt/alt/ included, and prints the version each binary reports. It’s a starting point, since custom builds and vendor bundles can live anywhere:

for p in /usr/bin/php /usr/bin/php[0-9]* /opt/alt/php*/usr/bin/php; do [ -x "$p" ] && echo "$p $("$p" -r 'echo PHP_VERSION;')"; done

That shows what’s on disk, not what each site runs, since FPM pools, vhosts and control panels can each point at a different binary. Check those configs too. Supported branches get the upstream or distro update. ELS for Runtimes builds update through the normal package manager, using the commands in TuxCare’s ELS for Runtimes PHP documentation. On RPM-based systems, that’s sudo yum update alt-php*. On Debian and Ubuntu, refresh the package lists and upgrade the alt-php packages:

sudo apt-get update
sudo apt-get upgrade alt-php*

Then the step that gets skipped. Installing the fixed package isn’t enough while workers still run the old code. A running PHP-FPM master, or mod_php loaded inside Apache, keeps the old code in memory until it’s restarted, and a container keeps it until it’s replaced. Shared hosting servers with dozens of FPM pools are the classic case, where one version gets restarted and three don’t. Check that every FPM master started after the update went in:

ps -eo pid,lstart,args | grep '[p]hp-fpm: master'

Last, don’t close the ticket based on a scanner’s upstream version comparison. A backported ELS or distro build may still report the original upstream version number, so scanners that only read version strings can flag hosts that are already patched. Where your tooling supports it, feed it TuxCare’s machine-readable security data, which includes OVAL and CSAF feeds. Record the package installed, the advisory it addresses and the process actually serving requests, because that’s what an auditor will ask for.

Why php2shell Matters

A runtime is a dependency, and leaving it unsupported means every new core vulnerability can stay open indefinitely. Teams track their Composer packages carefully and then run them on an interpreter that stopped getting security fixes years ago.

The leak also showed how little warning anyone gets. The first rumors were confirmed within hours, the fix shipped the next day, and attack attempts showed up before it did. “We’ll upgrade next quarter” doesn’t survive a timeline like that.

A do-it-yourself route has appeared too. PHP committed backports for 7.4, 8.0 and 8.1 to php-src without tagging releases, and community scripts now rebuild source-compiled PHP with them. It can work for a single self-compiled server, but it fixes php2shell and nothing after it, skips distro packages, `mod_php` and `lsphp`, and turns every future PHP CVE back into a build project.

So patch supported branches now, and restart what needs restarting. For PHP that php.net has left behind, move it onto a fixed ELS build or take it off the internet, and then put the real migration on a calendar instead of a wish list.

Get ELS for Runtimes today →

Timeline

Date Event
October 20, 2026 (morning) A Hacker News post claims two PHP RCE flaws will be disclosed on October 22. It’s removed after screenshots spread
October 20, 2026 watchTowr confirms the leaked information is real and the bugs are under embargo
October 20, 2026, 19:33 UTC The PHP security team’s advance notice goes out on php.announce, covering two critical flaws rated CVSS v4.0 9.2
October 21, 2026 Sansec, CERT-EU and others publish preparation guidance
October 21, 2026, 07:43 UTC Cloudflare’s CTO reports early attack attempts against PHP sites as stopgap WAF rules roll out to all plans
October 21, 2026 PHP ships 8.5.12, 8.4.27, 8.3.36 and 8.2.35 and publishes advisories for both CVEs

References

Primary sources for this post are PHP’s security advisories RCE via OOM corruption (CVE-2026-109618) and RCE via stale global state (CVE-2026-109619), the PHP security team’s advance notice on php.announce (October 20, 2026), PHP’s supported versions page and PHP 8 changelog, watchTowr’s php2shell FAQ, Sansec’s php2shell research, Huntress’s rapid response writeup, CERT-EU Security Advisory 2026-018, Mallory’s exposure analysis, Florian Roth’s process-watcher, the NVD entries for CVE-2026-109618 and CVE-2026-109619, coverage from The Hacker News, and TuxCare’s ELS for Runtimes PHP documentation.

Join 4,500+ Linux & Open Source Professionals!

2x a month. No spam.

Table of Contents
Get the open-source security answers you need

Join 4,500+ Linux & Open Source Professionals!

2x a month. No spam.