Security

PHP2Shell (CVE-2026-109618 and CVE-2026-109619): Critical PHP Flaws and How to Patch Your VPS

Ruby 14 min read

Two critical PHP flaws, CVE-2026-109618 and CVE-2026-109619 (PHP2Shell), allow unauthenticated remote code execution. Here is what they are and how to patch your VPS or server.

What is PHP2Shell?

PHP2Shell is the nickname for two separate vulnerabilities in the PHP runtime, published together on 21 October 2026 with a coordinated security advisory from the PHP project. PHP's own advisories refer to them only by CVE number:

  • CVE-2026-109618: remote code execution via out-of-memory corruption.
  • CVE-2026-109619: remote code execution via stale global state.

Both were rated Critical, 9.2 on CVSS v4.0, and PHP confirmed that both can be exploited without logging in. The advisories credit the researchers who reported the flaws, and the fixes were developed by PHP contributors Arnaud Le Blanc, Ilija Tovilo, Jakub Zelenka and Nora Dossche. Wiz Research was among the disclosing parties and said the underlying weaknesses had existed for a long time, but that exploiting them needed an unusually complex chain of steps. In Wiz's account, frontier AI models lowered that barrier considerably.

The disclosure was not a complete surprise. CERT-EU published an advisory naming both CVE IDs on 20 October, and PHP sent an advance notice to the oss-security mailing list the same day. Linux distributions and long-term support vendors received the patches ahead of the public release.

How the two bugs work

You do not need to read PHP's source code to follow the idea. Both bugs come down to the same theme: PHP can be left holding a pointer to memory that has already been thrown away, and later code trusts it.

CVE-2026-109618: out-of-memory corruption

Every PHP request has a memory limit. When a request crosses it, PHP aborts by jumping straight out of whatever it was doing. In a few places, PHP has request data that is only half set up at that moment, and the abrupt exit leaves it in a broken state. The cleanup code that runs at the end of the request then reads that broken data as though it were valid, which corrupts memory.

An attacker can force the out-of-memory condition on purpose with a crafted file-upload (multipart) request, for example one with a huge number of headers or many empty file fields, while staying inside PHP's normal upload limits. PHP demonstrated full remote code execution over HTTP against Apache's built-in PHP module (mod_php) with default limits, before any application code runs at all. The upstream fix stops PHP from aborting mid-operation when memory runs out and ends the request at the next safe point instead.

CVE-2026-109619: stale state between requests

A PHP worker handles many requests one after another and keeps some global state (about errors, sessions and extensions) that points into the current request's memory. That state must be cleared before the memory is freed. If a request is cut short by a fatal error, including running out of memory, some of it is never cleared. On the next request handled by the same worker, a few things run before PHP's normal setup and can use that stale state after its memory has been freed.

PHP confirmed two of these early paths:

  • Warnings raised while applying per-directory or per-request PHP settings, such as .htaccess settings under mod_php, or PHP_VALUE settings that nginx or Apache pass to PHP-FPM. Turning off error display or logging, or using the @ operator, does not prevent it.
  • Session upload progress, a feature that is on by default and works even if the application never uses sessions.

PHP validated remote code execution through the upload-progress path against both mod_php and PHP-FPM. For PHP-FPM, success depends on the application offering an operation that an attacker can make fail.

A note on terminology: different security vendors have described the exact technical chains in slightly different words. We follow PHP's advisories and the AlmaLinux analysis here. If you need the low-level detail, read the two GitHub advisories listed in the sources.

Who is affected

PHP's advisory says every installation that serves web requests is at risk: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. Risk is highest for mod_php, where a single unauthenticated request can trigger the attack before any application code runs.

What that means in practice:

  • The application does not have to be vulnerable. The flaws are in the PHP engine, so WordPress, Magento, Laravel and every other framework are exposed even when their own code is clean. Sansec reports that researchers confirmed unauthenticated code execution against Magento 2.4.9 and ten other PHP applications, including WordPress, Drupal and Joomla.
  • PHP-FPM is not a safe harbour. PHP confirmed remote code execution against PHP-FPM through CVE-2026-109619. For CVE-2026-109618, PHP's analysis says FPM is susceptible, but no complete exploit against it has been shown. Red Hat notes that standard RHEL deployments use PHP-FPM and that exploiting CVE-2026-109618 there is difficult in practice, typically needing non-default behaviour.
  • It reaches back a long way. Sansec says the vulnerable code dates to PHP 5.4.0 (2012), and watchTowr's FAQ says every release from PHP 5 onward is affected, though watchTowr notes that last point has not been confirmed by an official vendor advisory.
  • The exposure is huge. Palo Alto Networks' Unit 42 said its Cortex Xpanse telemetry found more than 22.6 million internet-exposed instances potentially vulnerable.

The code runs as whatever account PHP runs under (for example the web server user, or a hosting account's user). That account's files, databases and credentials are what an attacker gets first.

Severity and exploitation status

  • CVSS v4.0 score: 9.2 (Critical) for each CVE, per the PHP project and as relayed by CERT-EU, Unit 42 and others.
  • Red Hat rates CVE-2026-109618 Important and classifies it as a use-after-free (CWE-416).
  • Working exploits exist at least inside the research community. PHP's advisory describes a demonstrated exploit against Apache with mod_php, and watchTowr says it has reproduced CVE-2026-109618.
  • Reports of real-world attacks conflict. Contabo's customer notice states that active exploitation has been reported, and watchTowr's FAQ lists both CVEs as exploited. SOCRadar, by contrast, wrote on 21 October that PHP had not reported in-the-wild exploitation and that CISA had not added either CVE to its Known Exploited Vulnerabilities catalog. We cannot settle that from here, so treat it as urgent: unpatched, internet-facing PHP should be assumed to be a target.
  • Authorities are alarmed. The UK's National Cyber Security Centre urged organisations to consider taking offline any system that cannot be fixed right away. Cloudflare says it has already deployed initial WAF rules for the known exploit vectors, available to all customers including free plans. That reduces exposure but is not a fix.

What hosting providers are telling customers

Providers are emailing customers about this, and the message is worth understanding because it applies to almost every unmanaged VPS. Contabo's notice is a good example. Its main points:

  • The provider has no access inside your VPS, VDS or dedicated server and cannot apply the update for you. If you manage the operating system, you own the PHP patch.
  • Customers are asked to install the update as soon as patched packages are available for their system.
  • Both flaws are rated 9.2 on CVSS v4.0, and the notice says active exploitation has been reported.
  • Customers are told to take a snapshot or backup first, update through the package manager or control panel, restart PHP-FPM or the web server, and verify the version actually in use.
  • Interim measures from PHP's advisory reduce exposure but do not make an installation safe. Updating is the only complete fix.

If you are a VPS customer, this is the opposite of the kernel-level flaw we covered recently: here your provider cannot fix it and the responsibility is yours. If you use a managed plan, a managed hosting panel or a platform that handles PHP for you, ask your provider when their PHP builds were updated and do not assume it has happened.

Fixed PHP versions and end-of-life releases

PHP branch

Fixed release

Status

8.5

8.5.12

Supported

8.4

8.4.27

Supported

8.3

8.3.36

Supported

8.2

8.2.35

Security fixes only; end of life on 31 December 2026, so plan an upgrade

8.1 and older

No upstream fix

End of life

If you run PHP 8.1 or older, the PHP project is not releasing regular fixes. Your options are to upgrade to a supported branch, or to get backported fixes from your distribution or a long-term support vendor. As an exception, the PHP security team released minimal source-code backports for PHP 7.4, 8.0 and 8.1, but they are meant for people who build PHP from source or have no LTS vendor, and they are not packaged builds. Applications on PHP 5.5, 7.2, 7.3, 7.4 or 8.1 that rely on no backport remain vulnerable.

If you use the Remi repository (rpms.remirepo.net), note that this volunteer-maintained repository was not going to have updates on release day. The PHP project recommends switching to distribution packages or building from source if you need to patch immediately.

How to fix it, panel by panel and distro by distro

Before you start on any server: take a snapshot or backup, then update, then restart (an update does not affect a running worker until it restarts), then verify.

cPanel/WHM and EasyApache 4

Contabo's notice says updates for EasyApache 4 are already available. Run the cPanel update from WHM or on the command line, then restart PHP-FPM or Apache and confirm every PHP version in use:

/scripts/upcp

cPanel's announcements and release notes are at docs.cpanel.net.

Plesk Obsidian

Contabo's notice says updates for Plesk Obsidian are already available. Update through the Plesk panel, then check each PHP handler you have enabled for your subscriptions.

CloudLinux (Alt-PHP)

Follow the CloudLinux status page and the CloudLinux blog for Alt-PHP builds. CloudLinux also offers extended support for PHP versions that are no longer maintained, which is the realistic route if you are stuck on an old branch.

AlmaLinux 8, 9 and 10

AlmaLinux says all three releases ship affected PHP versions. Its team is backporting the fixes ahead of Red Hat and expected patched packages in the testing repository on 21 October, followed by production after community verification. Check the AlmaLinux post for the exact package versions and testing instructions rather than guessing them. The streams being patched are:

  • AlmaLinux 8: php:7.4 and php:8.2
  • AlmaLinux 9: the default PHP 8.0, plus php:8.2 and php:8.3
  • AlmaLinux 10: the default PHP 8.3, plus PHP 8.4 (php8.4 packages)
  • Kitten 10: the fix arrives in the next regular PHP update

Once packages reach your repository:

sudo dnf update 'php*'
sudo systemctl restart php-fpm

The retired streams (php:7.2, php:7.3 and php:8.0 on AlmaLinux 8, and php:8.1 on AlmaLinux 9) are not in the first round. php:7.2 is still the default stream on AlmaLinux 8, so if you installed PHP there without choosing one, that is what you have. Check which stream is enabled (marked [e]) and move to a supported one after testing your applications:

dnf module list php
sudo dnf module reset php
sudo dnf module enable php:8.2
sudo dnf distro-sync 'php*'

AlmaLinux 8 can also run PHP through Apache's mod_php if Apache uses the prefork MPM, which is the highest-risk setup. To check:

httpd -M 2>/dev/null | grep -E 'mpm_|php'

If the output includes mpm_prefork_module and a php_module, you are running mod_php. AlmaLinux 9 and 10 do not ship mod_php, and the AlmaLinux packages use PHP-FPM by default. Note that AlmaLinux's packages only update PHP installed from AlmaLinux's own repositories, not PHP from a control panel or a third-party repository.

RHEL, Rocky Linux, Oracle Linux and other RHEL-family systems

Red Hat's CVE page rates CVE-2026-109618 Important and tracks it in Bugzilla 2553995. At the time AlmaLinux published, Red Hat had not released fixed PHP packages. Rebuilds such as Rocky Linux normally follow Red Hat's release. Check regularly:

sudo dnf check-update 'php*'

When packages appear, update and restart PHP-FPM or httpd.

Ubuntu and Debian

We did not find a published fix status for either at the time of writing. Check Canonical's CVE pages and the Debian Security Tracker for CVE-2026-109618 and CVE-2026-109619 and the fixed package version for your release. When a fix is listed, a normal system update applies it, and you then need to restart the PHP service. On servers with several PHP versions installed, restart each one:

sudo apt update
sudo apt upgrade
sudo systemctl restart php8.3-fpm   # adjust to your PHP version
sudo systemctl restart apache2      # if you use Apache with mod_php

Building from source, containers and everything else

Update to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 from php.net, or to the matching official image if you run containers, then rebuild and redeploy. A running container keeps the old PHP until it is replaced.

8. Mitigations if you cannot update yet

The PHP project's advisory lists configuration measures that reduce exposure to specific attack paths. None of them make an installation safe, and several break features. Treat them as stopgaps for hosts you cannot patch within hours. After changing a PHP setting, restart every PHP worker. Apply these in php.ini, a file in your conf.d directory or a PHP-FPM pool, not with ini_set() or a .user.ini file, which take effect too late.

Measure

Targets

What it costs

session.upload_progress.enabled = 0

CVE-2026-109619 (the confirmed upload-progress path on mod_php and PHP-FPM)

Upload progress bars stop working. PHP suggests keeping it off after patching if you do not need it

Remove invalid or deprecated PHP settings applied per directory or per request that produce warnings (.htaccess, PHP_VALUE) and move them to php.ini or the FPM pool

CVE-2026-109619 (warnings are one of two confirmed triggers)

Configuration work. Hiding warnings with error_reporting, display_errors or @ does not help

Set memory_limit = -1 where applications cannot override it, for example php_admin_value[memory_limit] = -1 in the FPM pool

CVE-2026-109618

PHP stops capping per-request memory, so you need a container or cgroup memory limit as a backstop

Low post_max_size (8M with no uploads, 32M with uploads) and upload_max_filesize below it

CVE-2026-109618

Large forms and uploads fail

pm.max_requests = 1 in the PHP-FPM pool

CVE-2026-109619 (stops one request leaving state for the next on the same worker)

Slower, because every worker is recycled after one request

fatal_error_backtraces = 0 (PHP 8.5 only)

CVE-2026-109619

Less detail in fatal-error output

Fix fatal errors and timeouts an attacker can trigger, including in callbacks, import and cleanup code

CVE-2026-109619 (the confirmed PHP-FPM path starts with a failure in your application)

Developer time

A basic example for session upload progress, from AlmaLinux's guidance (use your distribution's conf.d path):

echo 'session.upload_progress.enabled = 0' | sudo tee /etc/php.d/99-php2shell.ini
sudo systemctl restart php-fpm

Two cautions from the AlmaLinux write-up. First, use a container or cgroup memory limit that kills only the affected worker. Do not use RLIMIT_AS or RLIMIT_DATA instead, as they can trigger the vulnerable path. Second, if you run mod_php, which PHP considers the highest-risk setup, switching to the event MPM with PHP-FPM removes the paths PHP confirmed against mod_php that do not depend on the application.

Edge protection. Cloudflare has deployed WAF rules for the known exploit vectors, including for free plans. Sansec also suggests blocking multipart/form-data requests at the web server if you cannot patch within the hour, which breaks every upload, and in the worst case making the site read-only or taking it offline. Those measures buy time. They do not repair PHP.

9. How to verify the fix and check for compromise

Verify the version.

  1. Restart, then check. An updated package does not affect a running PHP-FPM or Apache worker until it restarts. Without a restart, the old code keeps running.
  2. Check the command-line version with php -v, but remember the command line and your web server can use different PHP builds.
  3. Check what your web server really runs. Load a phpinfo() page through the web server, then delete it. On servers with several PHP versions (common on control panels), check every installed version, not just the default.
  4. Mind backports. Distribution packages often keep an older-looking version number. Check the package changelog instead, for example:
rpm -q --changelog php | grep -i CVE-2026-10961
apt changelog php8.3-fpm | grep -i CVE-2026-10961

Look for signs of compromise, especially if the server ran an unpatched, internet-facing PHP site after the disclosure:

  • Unknown or recently modified PHP files in web directories.
  • New cron jobs for the web user or hosting accounts.
  • Unfamiliar processes, and spikes in CPU load or outgoing traffic.
  • New administrator accounts or unexpected plugins in your CMS.
# PHP files modified in the last 2 days (adjust the path)
find /var/www -name '*.php' -mtime -2 -ls
# cron jobs for the web user (adjust the user)
sudo crontab -l -u www-data

Because the attacker's code runs as the PHP account, rotate database passwords, API keys and any credentials stored in configuration files if you suspect a compromise, and restore from a known-good backup rather than trusting a cleaned-up copy.

Best practices going forward

  • Turn on automatic security updates where possible, and review your control panel's update settings.
  • Remove PHP versions and applications you no longer use. Every extra PHP version on a server is another thing to patch and verify.
  • Plan the move off PHP 8.2 before it reaches end of life on 31 December 2026, and off anything older now.
  • Keep your CMS, plugins and themes up to date. This flaw is in PHP, but the next one may be in a plugin.
  • Put a web application firewall in front of the site and restrict access to administrative interfaces.
  • Take snapshots before every major update, so a bad update is a five-minute rollback.
  • Subscribe to your distribution's and panel's security announcements, so you hear about patches on day one.

Sources

Security advisories change quickly. This post reflects what vendors and researchers had published as of 21 October 2026. If you spot something out of date, tell us and we will update it.

Ruby