PHP2Shell: Check and Patch the PHP RCE Flaws on Your Server

PHP2Shell: Check and Patch the PHP RCE Flaws on Your Server

PHP2Shell: Check and Patch the PHP RCE Flaws on Your Server

On October 21, 2026 the PHP project released 8.5.12, 8.4.27, 8.3.36 and 8.2.35 to fix two flaws in PHP itself, nicknamed PHP2Shell. Both let a request from someone who is not logged in run code as the PHP worker. The bug is in the runtime, not in WordPress, Drupal or Joomla, so updating the CMS or its plugins does not close it. This guide shows who is affected, how to check a server in one command, what to do today if you cannot upgrade yet, and how to upgrade safely.

What the two flaws are

  • CVE-2026-109618, out-of-memory corruption - when a memory allocation fails, PHP can stop a request while its internal data is only half set up, and the cleanup that follows trusts that broken data. A crafted multipart request can trigger it even inside your configured size limits.
  • CVE-2026-109619, stale global state - state that belongs to one request can outlive the memory behind it. The next request served by the same worker can use that freed memory before it is reset (a use-after-free). The confirmed path goes through the session upload progress feature and fatal errors.

Both are scored 9.2 (critical) on CVSS 4.0. The PHP announcement says a working exploit was built during the research, and that the risk is highest for Apache with mod_php, where the attack runs before any application code. Several public proof-of-concept exploits appeared within days; Rapid7 reports each works only for one combination of OS, PHP version and web server, and has seen exploitation attempts. Treat it as an emergency patch.

Who is affected

Every PHP installation that serves web requests: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. That covers almost every WordPress, WooCommerce, Drupal, Joomla, Magento, Laravel and Nextcloud server.

PHP branch Affected First fixed version
8.5 8.5.0 - 8.5.11 8.5.12
8.4 8.4.0 - 8.4.26 8.4.27
8.3 8.3.0 - 8.3.35 8.3.36
8.2 8.2.0 - 8.2.34 8.2.35
7.4, 8.0, 8.1 all no php.net release; source backports, distro or LTS vendor packages
7.3 and older all no fix

Distributions often keep the old version number and backport the fix (for example a Debian or Ubuntu package that still says 8.2.x), so on packaged PHP check your distribution's security tracker for the package, not only the number.

Check a server in one command

Save this as php2shell-check.php and run it with the same PHP that serves your site. It prints the version, whether that version predates the fix, and the two settings the stopgaps below change.

<?php
// PHP2Shell check: is this PHP build older than the October 21, 2026 fixes?
$fixed = ['8.5' => '8.5.12', '8.4' => '8.4.27', '8.3' => '8.3.36', '8.2' => '8.2.35'];
$branch = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
echo "PHP " . PHP_VERSION . " (" . PHP_SAPI . ")\n";
if (!isset($fixed[$branch])) {
    echo "Branch $branch gets no php.net release: check your distro or LTS vendor, or upgrade to 8.4.\n";
} elseif (version_compare(PHP_VERSION, $fixed[$branch], '<')) {
    echo "VULNERABLE: upgrade to {$fixed[$branch]} or later (or a distro build with the backport).\n";
} else {
    echo "Patched upstream version ({$fixed[$branch]}+).\n";
}
echo "session.upload_progress.enabled = " . (ini_get('session.upload_progress.enabled') ? 'On' : 'Off') . "\n";
echo "post_max_size = " . ini_get('post_max_size') . ", memory_limit = " . ini_get('memory_limit') . "\n";
php php2shell-check.php

Output checked on 2026-10-24 in the official php Docker images:

PHP 8.3.30 (cli)
VULNERABLE: upgrade to 8.3.36 or later (or a distro build with the backport).
session.upload_progress.enabled = On
post_max_size = 8M, memory_limit = 128M
PHP 8.4.27 (cli)
Patched upstream version (8.4.27+).
PHP 7.2.34 (cli)
Branch 7.2 gets no php.net release: check your distro or LTS vendor, or upgrade to 8.4.

The command-line php can be a different build from the one behind your site. To check the web side, run php-fpm -v (or php-fpm8.3 -v on Debian and Ubuntu), or put the script in the web root for one request and delete it right after.

If you cannot upgrade today: stopgaps

The PHP team is clear that there is no complete mitigation and updating is the only safe choice. These settings narrow the attack while you schedule the update. Set them where the application cannot override them - php.ini or the FPM pool with php_admin_value - because ini_set() and .user.ini apply too late. Then restart every worker.

; FPM pool, e.g. /etc/php/8.3/fpm/pool.d/zz-php2shell.conf (the [www] section of your pool)
[www]
php_admin_value[session.upload_progress.enabled] = 0
php_admin_value[post_max_size] = 32M
php_admin_value[upload_max_filesize] = 30M
php-fpm8.3 -t && systemctl restart php8.3-fpm

We checked this pool file with php-fpm -t on PHP 8.4 (configuration test successful). What each line does:

  • session.upload_progress.enabled = 0 blocks the confirmed CVE-2026-109619 path, not every variant. Turn it off unless an upload progress bar depends on it.
  • post_max_size of 8M with no uploads, or 32M if the site takes uploads, with upload_max_filesize below it, as the PHP team recommends. Also cap request size at nginx (client_max_body_size 32m;) or Apache (LimitRequestBody).
  • On PHP 8.5 only, add fatal_error_backtraces = 0.
  • PHP also suggests memory_limit = -1 set by the admin, so the memory limit itself cannot be used to trigger CVE-2026-109618. Only do that with a container or cgroup memory limit around PHP, or one bad request can take the whole server's RAM. Do not use RLIMIT_AS or RLIMIT_DATA for this: they can trigger the flaw.

Hiding errors (display_errors, error_reporting, @) does not help, and an empty error log does not prove a server was not attacked.

How to upgrade

  1. Packaged PHP (Debian, Ubuntu, RHEL, AlmaLinux). Install the security update (apt update && apt upgrade, or dnf upgrade 'php*'), then restart PHP-FPM or Apache, because running workers keep the old code. Confirm the package changelog names CVE-2026-109618.
  2. Docker images. Rebuild on a fixed tag (php:8.4-fpm resolves to 8.4.27 now) with docker compose build --pull, then recreate the containers. A running container keeps the old PHP until it is replaced.
  3. PHP 7.4 - 8.1. Use your vendor's backported package, or move to 8.4. PHP 7.3 and older will never get a fix: put the site behind an allow-list or a VPN until it is migrated.
  4. After the upgrade, run the check script again and look through the web server log for unusual multipart POST requests and PHP worker crashes since October 21.

If you need a hand, managed server administration covers patching like this, and our guide to a WordPress cloud server shows a PHP-FPM setup that keeps these settings in the pool. Before you upgrade a production site, take a snapshot: backup and restore for a VM explains how.

What we did

The same week we checked every DCXV service that runs PHP and put the measures from this guide in place. Customer servers are managed by their owners: if you run PHP on a DCXV cloud or dedicated server, run the check above today. A new cloud server gets the current packages on its first apt upgrade.

Sources (checked 2026-10-24): the PHP 8.x security release announcement of October 21, 2026, the PHP advisories GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp, and the analysis by Rapid7.

Starlink Mini 2 Behind CGNAT: Reaching a Site That Moves
networkingcloudinfrastructure

Starlink Mini 2 Behind CGNAT: Reaching a Site That Moves

Starlink Mini 2 is out, but standard plans sit behind CGNAT. We counted 990,208 IPv4 addresses for 12M+ customers and compare four ways to reach a remote site.

Data Center Protests Reach Europe: What Buyers Should Do
clouddedicatedinfrastructure

Data Center Protests Reach Europe: What Buyers Should Do

Hundreds entered Microsoft's Amsterdam data center site on 6 November. The Dutch power numbers behind it, measured latency to EU hubs and what buyers should do.

US Voters Rejected Data Centers 45 to 1: What Buyers Learn
clouddedicatedinfrastructure

US Voters Rejected Data Centers 45 to 1: What Buyers Learn

On 3 November 47 US ballot measures on data centers went to voters and the restrictive side won 45. We recount the raw data: what it means for server buyers.

AWS BYOIP in 2026: $43.80 per IPv4 vs Your Own /24
ipv4pricingcloud

AWS BYOIP in 2026: $43.80 per IPv4 vs Your Own /24

AWS bills $0.005 an hour per public IPv4, $43.80 a year. BYOIP addresses carry no charge: when a bought or leased /24 pays back, with a ROA readiness check.

Leaving VMware After Broadcom: A Checked Migration Guide
clouddedicatedinfrastructure

Leaving VMware After Broadcom: A Checked Migration Guide

Broadcom ended perpetual licences, cut partners and hid VDDK. How EU teams move VMs to KVM, Proxmox or Hyper-V, with dated facts and a tested OVA conversion.