Adnan Canturk.
Security

PHP2Shell patch day: a runbook for PHP, Node.js, CloudLinux and cPanel servers

9 min readTürkçe oku

Last updated:

PHP2Shell patch day runbook for PHP, Node.js, CloudLinux and cPanel servers

21 October 2026 was a patch day for nearly every server that runs PHP. The PHP project shipped emergency fixes for two critical flaws now known as PHP2Shell, Node.js published its own security release a few hours apart, and EasyApache 4 packages with backported fixes reached cPanel and CloudLinux servers the same afternoon. This is the runbook we used, written for anyone who operates the stack rather than writes the application.

TL;DR

  • PHP2Shell is CVE-2026-109618 and CVE-2026-109619: two pre-authentication bugs rated 9.2 that act before application code runs.
  • Fixed upstream in PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12; cPanel EasyApache 4 shipped backports for ea-php72 to ea-php85 with unchanged version numbers.
  • Node.js 22.23.4, 24.22.1 and 26.12.1 shipped the same day, fixing ten CVEs including a Brotli decoder bug that permits remote code execution.
  • Verify by package changelog, restart every process that loaded PHP, and check that no process maps deleted PHP libraries.
  • session.upload_progress.enabled = 0 and WAF rules buy time; only the update fixes it.

Why PHP2Shell is different

CVE-2026-109618 and CVE-2026-109619 are both rated 9.2 on CVSS v4.0, and the details explain the score. The first is an out-of-memory bug: PHP can be interrupted while request data is only half initialized, and the cleanup that follows treats the broken data as valid. The second is stale global state, where request-owned data survives after its memory is freed and the next request on the same worker uses it before initialization resets it.

Three properties make this a hosting problem rather than an application problem. No login is needed. The attack runs during request parsing, before any application code executes, so a hardened WordPress or a strict framework does not help. And every web SAPI is affected: PHP-FPM, Apache with mod_php, php-cgi, LiteSpeed and FrankenPHP, with mod_php rated the highest risk. Details leaked before the planned disclosure date, a working exploit has been demonstrated, and Cloudflare deployed WAF rules for the known vectors on all plans. The UK NCSC advises taking any system that cannot be patched offline. Treat it as actively exploited.

The PHP2Shell runbookBEFORE, DURING AND AFTER THE 21 OCTOBER UPDATES01 BEFOREInventory every PHP binaryupload_progress.enabled = 0No memory_limit = -1WAF: Imunify360 8.83, CloudflareBaseline files and crashes02 PATCHPHP 8.2.35 · 8.3.36PHP 8.4.27 · 8.5.12cPanel ea-php72 to 85 backportsCloudLinux alt-php per OSNode 22.23.4 · 24.22.1 · 26.12.103 AFTERVerify by changelog, not php -vRestart FPM, Apache, lsphp, workersNo '(deleted)' PHP mapsHunt for web shells and cronRestore temporary settingsSOURCES: PHP.NET, NODEJS.ORG, CPANEL EA4 CHANGELOG
PHP2Shell runbook: before, during and after the 21 October 2026 updates

What was patched on 21 October

Component Fixed versions Notes
PHP (php.net) 8.2.35, 8.3.36, 8.4.27, 8.5.12 PHP2Shell; minimal source backports published for 7.4, 8.0 and 8.1
cPanel EasyApache 4 on CloudLinux ea-php72 to ea-php85, new release suffix Backported fix, upstream version number unchanged (for example 8.4.26-2.cloudlinux.1)
CloudLinux alt-php Rolling out per OS Track the CloudLinux status page; Imunify360 rules as interim protection
Node.js 22.23.4, 24.22.1, 26.12.1 Ten CVEs, four high, including a Brotli decoder out-of-bounds write that permits remote code execution

The Node.js release is smaller in reach but not in severity. CVE-2026-111497 is an out-of-bounds write in the Brotli decoder on all three lines; CVE-2026-106069 and CVE-2026-105483 bypass TLS hostname checks through loosely parsed or fullwidth IP addresses; and CVE-2026-105482 truncates names at a NUL character in dns.resolve. Bundled c-ares, OpenSSL and Undici were updated, and the CA store dropped an eMudhra intermediate after a compromise.

Check a build against the fixed releases:

Which PHP setups are most exposed?

Every way of serving PHP over the web is affected, but the advisory names mod_php as the highest risk, and the way each setup is restarted after the update decides how long the old code keeps running. The table is the version we used to plan the restarts.

How PHP is served Exposure What makes the fix take effect
Apache with mod_php Highest, per the PHP advisory A full Apache restart; a graceful reload is not enough
PHP-FPM (cPanel, plain Apache or nginx) Affected Restart or reload the FPM service so every pool forks new workers
CGI handler (suPHP-style or /cgi-sys/) Affected Each request starts a new process, so the new binary is used at once
LiteSpeed with lsphp Affected Upgrade the lsphp packages, then stop the running lsphp processes
FrankenPHP, Octane, Swoole, RoadRunner Affected Replace the binary or image and restart the long-running server

A shared server often mixes several of these. On a mixed server, the CGI-served sites pick up the patched binary on their next request while the FPM pools keep the old one until they restart; that difference is why the stale-process check below matters.

A field note from a cPanel and CloudLinux server

On one of our CloudLinux 8 servers running cPanel 11.138, the EasyApache 4 updates installed automatically at 16:16 server time on 21 October, covering every ea-php version from 7.2 to 8.5. Two details are worth knowing. First, php -v still reported 8.4.26, because the fix is a backport; the proof is in the package changelog, where each build lists CVE-2026-109618 and CVE-2026-109619. Second, the update is only half the job until every PHP process has restarted, and that is something you should check rather than assume.

Before the patch: buy time safely

  1. Inventory every PHP binary. List ea-php, alt-php, distribution PHP, LiteSpeed lsphp, Docker images and anything bundled with an application. Each one is a separate patch.
  2. Disable upload progress where it is safe. Set session.upload_progress.enabled = 0 in php.ini or with php_admin_value in the FPM pool, then restart the workers. It blocks the confirmed session path of CVE-2026-109619 only; ini_set() and .user.ini apply too late.
  3. Remove memory_limit = -1. An unlimited limit makes the out-of-memory path of CVE-2026-109618 easier to reach.
  4. Turn on WAF coverage. Imunify360 ruleset 8.83 blocks the public exploit shape on Apache, and Cloudflare has managed rules. Both are temporary, and on LiteSpeed the Imunify360 rule only logs.
  5. Take a baseline. Record recently modified PHP files and current crash counts so you can compare after the patch.

After the patch: prove it

  1. Verify by changelog, not by version. On cPanel, rpm -q --changelog ea-php84-php-cli | head should mention both CVEs. On php.net builds, check for 8.2.35, 8.3.36, 8.4.27 or 8.5.12.
  2. Restart everything that loaded PHP. Restart PHP-FPM; do a full Apache restart for mod_php, since a graceful reload is not enough; kill lsphp on LiteSpeed; restart queue workers and long-running daemons.
  3. Look for stale processes. grep -lE '/opt/cpanel/ea-php.*\(deleted\)$' /proc/[0-9]*/maps should return nothing. Any match is a process still running the vulnerable code.
  4. Update Node.js the same day. Move every service, container and CI runner to 22.23.4, 24.22.1 or 26.12.1, and retire Node.js 20, which no longer receives fixes.
  5. Hunt, then relax mitigations. Compare new or changed PHP files, web shells and unexpected cron entries against your baseline before you remove temporary WAF rules and restore your previous settings.
  6. Plan the version moves. PHP 8.1 and older depend on vendor backports, and PHP 8.2 reaches end of life on 31 December 2026. Target 8.4 or 8.5.

How do you roll the update across a fleet?

On one server the update is a package install and a restart. Across a fleet the risk moves from the bug to the rollout: a configuration that worked on one box can break a site on another, and a restart at the wrong moment drops requests. We use the same order every time.

  1. Pick a canary. Patch one server that carries a representative mix of sites, restart, and compare the control panel and a sample of sites before and after.
  2. Record the before state. For each server, the installed package versions, the handler each sample site uses and its HTTP status. Without that, a regression cannot be told apart from an old fault.
  3. Restart in a quiet hour. An FPM restart is short, but a full Apache restart for mod_php drops connections in flight.
  4. Verify, then move on. Changelog check, stale-process check, sample sites, then the next server. A failure stops the rollout until it is understood.

On cPanel servers with automatic EasyApache 4 updates the install step happens by itself, as it did on ours, which leaves the restart and verification as the work. Servers without automatic updates need the install step scheduled explicitly, and those are the ones most likely to be forgotten.

What signs should you look for after patching?

Patching closes the hole but does not tell you whether anyone used it first. Because details leaked before the release, it is worth a short review of the days before your update. Look for PHP files created or changed in web roots since mid-October that no deployment explains, PHP-FPM log lines about workers that exited on a signal, unexplained bursts of 500 or 502 responses, new cron entries for web users, and outbound connections from PHP processes to unfamiliar hosts.

None of these proves an intrusion on its own, and a clean result is not a guarantee. Treat a hit as the start of an investigation, compare it with the baseline you took before patching, and keep the temporary WAF rules in place until the review is done.

For the background on the earlier 2026 releases see the PHP and Node.js release overview; the BIND and Java updates of the same week are in Node.js, BIND and Java in one week, and the kernel side in five kernel settings for a shared Linux server.

FAQ: PHP2Shell and the 21 October 2026 updates

What is PHP2Shell, and why was it patched as an emergency in October 2026?

PHP2Shell is the name given to CVE-2026-109618 and CVE-2026-109619, two memory-safety bugs in PHP rated 9.2 on CVSS v4.0. They need no login and act before application code runs. Details leaked before the planned date, so PHP released the fixes on 21 October 2026.

Which PHP versions fix PHP2Shell, and what about PHP 8.1 and older?

The fixed upstream releases are 8.2.35, 8.3.36, 8.4.27 and 8.5.12. PHP 8.1 and older get no upstream release; the PHP team published minimal source backports for 7.4, 8.0 and 8.1, and some vendors ship their own patched packages.

Why does php -v still show the old version after cPanel installed the PHP2Shell fix?

Because EasyApache 4 shipped the fix as a backport with a new package release, not a new PHP version. The changelog of each ea-php package lists both CVEs, and that changelog, not php -v, is the proof.

mod_php vs PHP-FPM: which is more exposed to PHP2Shell?

The PHP advisory rates mod_php as the highest risk, with a demonstrated exploit against it. PHP-FPM is also affected. Whatever you run, the fix only applies after the right restart: a full Apache restart for mod_php, an FPM restart for pools.

Is disabling session.upload_progress enough to mitigate PHP2Shell without patching?

No. It blocks only the confirmed session path of CVE-2026-109619 and does nothing for CVE-2026-109618. Set it in php.ini or the FPM pool, not with ini_set(), and treat it as a stopgap until the update is installed.

Does the Imunify360 rule protect LiteSpeed servers from PHP2Shell in 2026?

Only partly. Imunify360 ruleset 8.83 blocks the public exploit shape on Apache, but on LiteSpeed it logs the pattern rather than blocking it. On either server it is interim coverage, not a replacement for the patched PHP.

How do you confirm that no process is still running unpatched PHP code after the update?

Search the memory maps for deleted PHP libraries: grep -lE '/opt/cpanel/ea-php.*\(deleted\)$' /proc/[0-9]*/maps. An empty result means every running process loaded the new files; any match is a process that still needs a restart.

Are CloudLinux alt-php packages patched for PHP2Shell, or only EasyApache ea-php?

The ea-php backports reached CloudLinux 8 servers on 21 October. CloudLinux announced that alt-php packages would roll out per operating system and listed their status on its status page, so check that page for the alt-php versions your accounts use.

Patching PHP vs Node.js first: should the 21 October 2026 Node.js release wait for PHP2Shell?

No, run them in parallel. The Node.js release fixed four high-severity issues, including a Brotli decoder bug that permits remote code execution. Updating Node.js does not touch PHP, so there is no reason to queue one behind the other.

How can you tell whether a server was compromised before the PHP2Shell patch?

There is no single test. Review web roots for unexplained new PHP files, FPM logs for crashing workers, unusual 500 or 502 bursts, new cron entries and outbound connections from PHP, and compare them with a baseline. A hit starts an investigation; a clean result is reassurance, not proof.

Sources