How to Patch PHP2Shell (CVE-2026-109618, CVE-2026-109619) on cPanel and CloudLinux

How to Patch PHP2Shell (CVE-2026-109618, CVE-2026-109619) on cPanel and CloudLinux

Patching PHP2Shell on CloudLinux and cPanel: A Complete Step-by-Step Guide

When the critical PHP vulnerabilities CVE-2026-109618 and CVE-2026-109619 (nicknamed "PHP2Shell") were disclosed on 21 October 2026, the advice everywhere was simple: "just update PHP." These are a pair of PHP remote code execution (RCE) flaws rated 9.2/10 for severity, and PHP shipped the fixes in versions 8.2.35, 8.3.36, 8.4.27 and 8.5.12. On a plain server, that is roughly true. On a CloudLinux + cPanel stack running multiple PHP versions behind LiteSpeed or Apache, a single yum update is not enough — and several things can quietly leave you still exposed after you think you are done.

This is the exact process we used to roll the fix across a fleet of managed servers within hours of release, including the pitfalls that cost real time. If you run your own CloudLinux/cPanel box, this guide will save you from the traps.

New to this issue? For a plain-language overview of what PHP2Shell is and why it matters for any website, read our companion article: PHP2Shell Explained: The Critical PHP Vulnerability.

Why "Just Update PHP" Isn't Enough on CloudLinux/cPanel

A typical cPanel server has two parallel PHP systems, each with many versions installed:

  • EasyApache PHP (ea-php) — e.g. ea-php82, ea-php83 — what most sites run on.
  • CloudLinux alt-php — alt-php74 through alt-php85 — used by the PHP Selector.

A security flaw in the PHP engine affects every one of these. Patching only the default version leaves the others vulnerable. On top of that, CloudLinux delivers updates through a staged "gradual rollout", mirrors can lag, and running PHP worker processes keep the old code in memory until they are restarted. Each of these can trip you up.

Step 1: Check if Your PHP Is Vulnerable

Do not trust the version number alone — CloudLinux backports fixes while keeping the same version string. The reliable check is the package build date and changelog. For any installed engine:

rpm -q --changelog ea-php82-php-cli | grep -iE "109618|109619"
rpm -q --qf '%{VERSION}-%{RELEASE} built=%{BUILDTIME:date}\n' ea-php82-php-cli

If the CVE IDs appear in the changelog and the build date is on or after 21 October 2026, that engine is fixed. If not, it still needs updating. Repeat for each ea-php and alt-php version in use.

Step 2: Put a Shield Up Immediately (Virtual Patch)

Installing packages takes time, and attackers do not wait. If you run Imunify360, make sure the WAF is enabled with the full ruleset — its rules were updated to block the public PHP2Shell exploit almost immediately, giving you an instant protective layer before the permanent fix is in. As an interim engine-level mitigation, PHP also recommends disabling the affected feature in php.ini:

session.upload_progress.enabled = 0

Treat these as a bridge, not a fix — the real solution is updated packages.

Step 3: The Gotcha That Wastes the Most Time — a Stale Mirror Cache

The single most common reason a PHP update "does nothing" is a corrupt or stale repository cache. You may see errors like:

Failed to download metadata for repo 'cloudlinux-x86_64-server-9':
repodata/...-primary.xml.gz - Downloading successful, but checksum doesn't match

By default, yum/dnf aborts the entire transaction when any repo fails to refresh — so your whole update silently bails out and nothing gets installed. The fix is to clear the cache and rebuild it before updating:

yum clean all
rm -rf /var/cache/dnf/*
yum makecache

On a freshly cleared cache the correct, current metadata is pulled and the real fixed packages become visible.

Step 4: Understand Gradual Rollout and Epoch Pinning

Two CloudLinux-specific behaviours surprise people:

  • Gradual rollout. CloudLinux ships updates to servers in waves (numbered "rollout slots"), so a package can be "released" but not yet reachable on your box. Each slot has a paired -bypass repository to pull it early, e.g. yum update 'ea-php*' --enablerepo=cloudlinux-ea4-rollout-<N>-bypass — but only once the build is actually staged there.
  • Epoch pinning. On a CloudLinux box, cPanel's own EA4 PHP packages (epoch 0) are overridden by CloudLinux's cl-ea4 packages (epoch 1). Because epoch wins before version in RPM, yum will ignore a newer-looking cPanel build and stay on the CloudLinux branch. Do not try to force the cPanel build by disabling cl-ea4 — the next update will "downgrade" you back and can silently revert the fix. Wait for, or bypass to, the CloudLinux cl-ea4 build instead.

Step 5: Update Every PHP Version

Once the metadata is healthy, update both PHP systems in one pass, then let it pull all versions:

yum clean all
yum update 'ea-php*' 'alt-php*' -y

This updates every ea-php and alt-php engine the mirror has fixed builds for. If your alt-php mirror is still a release behind, it may update ea-php now and need a second pass for alt-php once the mirror catches up — which is why Step 1's verification matters.

End-of-life PHP versions: PHP 8.1 and older receive no fix from the PHP project itself. On CloudLinux, older ea-php and alt-php builds — for example PHP 7.4, 8.0 and 8.1 — are covered by CloudLinux and TuxCare Extended Lifecycle Support (ELS) backports, so an ELS-enabled server can still be patched for these legacy versions. A plain cPanel server without CloudLinux or ELS will not get a fix for end-of-life PHP, so those sites should be moved to a supported version.

Step 6: Restart Services — and Clear "Stale" Workers

Installing the new package is only half the job. Long-running PHP worker processes keep the old binary loaded in memory until they are recycled. Restart the web stack:

# LiteSpeed
/usr/local/lsws/bin/lswsctrl restart
# Apache (cPanel) PHP-FPM
/usr/local/cpanel/scripts/restartsrv_apache_php_fpm

Here is the subtle part: a graceful restart can leave a few detached workers alive, still running the replaced binary. You can spot them because their executable shows as (deleted):

for pid in $(pgrep -x lsphp; pgrep -x php-fpm); do
  readlink /proc/$pid/exe | grep -q '(deleted)' && echo "stale: $pid"
done

If any show up, recycle them (kill the stale PIDs, or restart php-fpm fully) so every live worker runs the fixed engine. Only then is the fix truly serving traffic.

Step 7: Verify, Don't Assume

Finish by confirming, per engine, that the installed package carries the CVE fix and that no stale worker remains:

rpm -q --changelog ea-php82-php-cli | grep -iE "109618|109619"
# expect the CVE IDs; repeat for ea-php83, alt-php74..85, etc.

Quick Checklist

  • Enable the WAF virtual patch and the interim session.upload_progress.enabled=0 as a bridge.
  • Run yum clean all first — a stale mirror cache silently aborts updates.
  • Update both ea-php* and alt-php*, every version.
  • Mind gradual rollout (use -bypass repos) and epoch pinning (don't force cPanel EA4 over cl-ea4).
  • Restart LiteSpeed/PHP-FPM and clear any (deleted) workers.
  • Verify the changelog on every installed engine before calling it done.

When Time Is the Risk, Expertise Is the Edge

None of these steps are exotic, but together they are the difference between a server that is genuinely patched and one that merely looks patched — and they have to be done quickly, across every version and every server, while the exploit is live. That is precisely the work a managed hosting team handles on your behalf. If you would rather not navigate rollout slots and stale workers yourself, our managed, secure hosting covers exactly this, or you can talk to the Cyberframe team about hardening your current setup.

PHP2Shell Explained: The Critical PHP Vulnerabilit...