PHP Security Update: Patch php2shell Now

· PHP, Development

Table of contents

On October 21, 2026, the PHP team shipped an emergency PHP security update: PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. They fix two remote code execution flaws, CVE-2026-109618 and CVE-2026-109619, both rated 9.2 under CVSS v4.0, and the exploit chain already has a name, php2shell. The official advisory is blunt: all PHP users are strongly encouraged to update immediately. I patched my own servers the same day. Here is what I checked, in order, and the one step most people forget.

What the two CVEs are

According to the PHP security advisory:

  • CVE-2026-109618, remote code execution via out-of-memory corruption. Out-of-memory handling can interrupt PHP while internal request data is only partially initialized, and request cleanup may then treat that invalid data as valid.
  • CVE-2026-109619, remote code execution via stale global state. Request-owned internal state can survive after its memory has been freed, and a later request handled by the same worker may use it before normal initialization resets it.

Both sit in the engine, not in your code. That is what makes them nasty: PHP lists every installation serving web requests as at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The risk is highest with mod_php, where a demonstrated exploit fires from an unauthenticated request before any application code runs. Your framework, your WAF rules in the app and your input validation never get a say.

Is it being exploited?

Short answer: assume yes. Tenable's FAQ collects the reports: Amazon Threat Intelligence saw an attempt against its honeypots on October 17, four days before disclosure, and more on October 21; Cloudflare said it was seeing early attacks; watchTowr reports exploitation attempts against both CVEs. As of October 23, Tenable notes the flaws were not yet in CISA's Known Exploited Vulnerabilities catalog. A public exploit and a catchy name are usually all mass scanning needs, so I would not wait for the catalog.

Which version you need

The fixed releases are:

  • PHP 8.5 → 8.5.12
  • PHP 8.4 → 8.4.27
  • PHP 8.3 → 8.3.36
  • PHP 8.2 → 8.2.35 (and 8.2 reaches end of life on December 31, 2026, so plan the next upgrade now)

PHP 8.1 and older get no new release from the project. The PHP team published minimal source backports for 7.4 to 8.1, and distribution maintainers patched their packages. The catch, as the Laravel Forge advisory points out, is that the version number does not change on those backports: php -v shows the same thing before and after. You have to check the package revision instead.

# Supported branches: the version itself tells you
php8.4 -v
php-fpm8.4 -v

# End-of-life branches: check the package revision against your vendor's advisory
dpkg -l 'php8.1*' | awk '/^ii/ {print $2, $3}'

Patching on Ubuntu and Forge

One detail surprised me: on Laravel Forge, automatic security updates do not patch PHP. Forge installs PHP from Ondřej Surý's repositories, and Ubuntu's unattended upgrades only cover Ubuntu's own security repository. The fix is the Update PHP X.X action in the server's PHP tab, or by hand:

sudo apt-get update
apt-cache policy php8.4-fpm   # Candidate must be the fixed version

sudo apt-get install --only-upgrade -y \
  -o Dpkg::Options::="--force-confold" \
  $(dpkg -l 'php8.4*' | awk '/^ii/ {print $2}')

Do it for every PHP version installed on the server (ls /etc/php), including the ones no site uses today. An old 7.4 left behind for a cron script is exactly the kind of thing that gets forgotten.

The step people forget: long-running processes

The package upgrade restarts PHP-FPM, so web requests are on the new binary within seconds. Your CLI processes are not. Queue workers, Horizon, Octane, Reverb, Magento consumers and any daemon started with php keep running the old, vulnerable binary until they restart. On a Laravel app:

php artisan queue:restart
php artisan horizon:terminate
php artisan octane:reload

On Magento, restart your queue:consumers:start processes through whatever supervises them (Supervisor, systemd or cron). Then run the version checks again, including php-fpm8.x -v, to confirm the fix is really live.

If you cannot patch today

PHP lists temporary mitigations, and says clearly that they do not make an installation safe. For CVE-2026-109618: set memory_limit=-1 through the FPM pool or Apache config, reduce post_max_size (8M without uploads, 32M with them) and enforce request size limits at the web server or proxy. For CVE-2026-109619: set session.upload_progress.enabled=0 in php.ini or the pool config (not with ini_set()) and restart every worker; on PHP 8.5, also set fatal_error_backtraces=0. Treat these as hours of breathing room, not as the fix.

Conclusion

This PHP security update is a "drop what you are doing" release: an engine-level RCE, reachable before your code runs, already being probed. Update every installed PHP version, check package revisions on end-of-life branches, restart your workers, and verify. And if this is the incident that reveals you still run PHP 8.1 somewhere, use it as the push to move to PHP 8.5 before the next one.