How To

PHP2Shell on End-of-Life PHP 8.1, 8.0 and 7.4 (WHMCS, ionCube)

You have an application that only runs on PHP 8.1 or older, and a PHP2Shell advisory that says the fix starts at PHP 8.2.35. Those two facts collide on every server that runs WHMCS, older Magento and Drupal sites, or custom code encoded with ionCube for a PHP version that is now end of life. PHP released no new 8.1, 8.0 or 7.4 build for CVE-2026-109618 and CVE-2026-109619. It published a small source patch for those branches instead, and packagers turned it into installable builds within a day.

Original content from computingforgeeks.com - post 176647

This guide fixes PHP2Shell on PHP 8.1, 8.0 and 7.4 by connecting the two sides. It covers what the end-of-life backport actually changes, where each old PHP branch gets it, and how to install it without moving the application to a new PHP version. It also shows how to prove the fix took, since the usual checks give the wrong answer on these builds. We confirmed the setup working on Rocky Linux 9 with Remi’s PHP 8.1 and 8.0 builds and ionCube in October 2026, and on production servers running WHMCS on the same PHP 8.1 build.

Check Which PHP Your Application Allows

Your first job is to find out whether the application can move to a patched branch at all. Moving is the better fix when it’s possible, because a supported branch keeps getting fixes after this one. ionCube-encoded code answers the question in its first bytes, since the encoder writes the PHP versions it targeted into a comment at the top of every file. On our WHMCS 8.10.1 install, the first line of init.php showed the targets:

head -n 1 /var/www/whmcs/init.php

The header lists two targets, 72 and 81, and nothing newer. Read 72 as code encoded for PHP 7.2, which the 7.2 to 7.4 loaders run, and 81 as code encoded for PHP 8.1:

<?php //ICB0 72:0 81:e822

That install cannot run on PHP 8.2 or 8.3 until WHMCS itself is upgraded. That matches WHMCS’s own requirements table: PHP 8.1 is the newest version listed for WHMCS 8.6 to 8.12, The same page notes that PHP 8.2 and higher cannot decode files those releases installed. So the PHP2Shell fix for this server has to come from an 8.1 build. The same reasoning applies to any application whose vendor hasn’t certified a newer PHP: patch the branch it runs on today, and treat the version upgrade as a separate project with its own testing.

Know What the Backport Changes

PHP published “minimal mitigation for EOL version” commits for 7.4, 8.0 and 8.1, on branches named PHP-8.1-CVE-2026-109618-109619 and so on. They carry the core of the fix, “Avoid state corruption via bailout”, across the Zend allocator, request parsing, the session extension and OPcache. The 8.1 patch Ubuntu shipped touches files such as Zend/zend_alloc.c, main/rfc1867.c and ext/session/session.c. Three differences from the full fix matter on a running server:

  • Session upload progress is switched off in the code, whatever php.ini says
  • The new memory_hard_limit_percent setting from the full fix does not exist
  • Every memory_limit overrun ends the process on the spot, so shutdown functions never run

You can easily miss the first one, because the setting still reads On. PHP chose a blunt fix for the old branches. In the 8.1 backport, the session upload callback gains an unconditional early return, so the feature never runs no matter what the configuration says:

-	if (!PS(rfc1867_enabled)) {
+	if (!PS(rfc1867_enabled) || true) {
 		return retval;
 	}

On our Rocky 9 VM, Remi’s patched 8.1 build reported the setting as On in its PHP-FPM configuration while that code path was dead. So if an application shows upload progress bars, they stop updating on these builds. Nothing in the configuration tells you why, which makes it a confusing support ticket unless you know about the patch.

The third difference shows up in your logs and catches people out. Instead of the full fix’s delayed error, the backport prints the memory error and exits immediately with status 1. A script that runs out of memory under PHP 8.1.34-6 from Remi skips its shutdown functions, and the error line loses its usual PHP prefix. Through PHP-FPM, the same overrun kills the worker, and the visitor gets a 502 behind nginx or usually a 503 behind Apache’s mod_proxy_fcgi. The memory error itself never reaches PHP’s error log. The backport writes it straight to the worker’s stderr, which FPM discards unless the pool sets catch_workers_output = yes. In our test the pool’s www-error.log was never even created. We sent one request that ran out of a 10M memory_limit through nginx to the php81 pool on Rocky Linux 9, and the pool’s master log recorded the worker exit:

[22-Oct-2026 09:25:56] WARNING: [pool www] child 20235 exited with code 1 after 1.068191 seconds from start
[22-Oct-2026 09:25:56] NOTICE: [pool www] child 20254 started

Red Hat’s 8.0.30-8.el9_8.1 backport for Rocky Linux 9 behaved the same way in our tests, exiting with status 1 on the same script. Applications that regularly brush against their memory limit, such as large report exports or image processing, therefore turn a logged fatal error into a 502 after this update. Count “exited with code 1” lines in the FPM master log for the first few days, and raise memory_limit for any pool that shows them. For Remi’s php81 collection the log is under /var/opt/remi/php81; for the system PHP it is /var/log/php-fpm/error.log:

sudo grep -c 'exited with code 1' /var/opt/remi/php81/log/php-fpm/error.log

A count that keeps climbing during normal traffic points at a pool whose limit sits too close to what its scripts really use.

Patch PHP2Shell on PHP 8.1, 8.0 and 7.4 by Platform

Every major packager turned the source patch into a build for at least some old branches. Stay with the packager you already use, because switching vendors in the middle of an incident adds a second change you then have to test. These are the builds we installed, or found in the vendor’s package index or advisory on 22 October 2026. A * in the DirectAdmin row is CustomBuild’s own marker for the same PHP version with new patches added:

PlatformPHP 8.1PHP 8.0PHP 7.4
Ubuntu 22.04 (free)8.1.2-1ubuntu2.28not shippednot shipped
Remi, EL 8, 9, 108.1.34-68.0.30-217.4.33-31
cPanel EasyApache 4ea-php81 8.1.34-5ea-php80 8.0.30-12ea-php74 7.4.33-19
DirectAdmin CustomBuild8.1.34*8.0.30*7.4.33*
Ubuntu 20.04 (Ubuntu Pro)not shippednot shipped7.4.3-4ubuntu2.29+esm5
RHEL and Rocky AppStreamEL9 php:8.1 stream retired, no fix8.0.30-8.el9_8.1 (EL9 default php); EL8 php:8.0 stream retired, no fix7.4.33-8 (EL8 php:7.4 stream)
AlmaLinux AppStream8.1.32-1.module_el9.8.0+326+528679fc.alma.1 (EL9 php:8.1 stream)8.0.30-8.el9_8.alma.1 (EL9 default php); 8.0.30-1.module_el8.10.0+4319+26f10322.alma.1 (EL8 php:8.0 stream)7.4.33-8 (EL8 php:7.4 stream)

Red Hat published those two fixes as RHSA-2026:87151 and RHSA-2026:87080 on 21 October, and Rocky Linux synced them overnight. Red Hat retired the RHEL 9 php:8.1 stream in May 2025 and the RHEL 8 php:8.0 stream in November 2024, so neither RHEL nor Rocky will fix those two. AlmaLinux went further. It carries Red Hat’s 7.4.33-8 on AlmaLinux 8 and shipped its own same-version backports for the default 8.0 on AlmaLinux 9 and for both retired streams. Each has a changelog entry naming both CVEs. AlmaLinux’s errata feed had no entry for these CVEs on 22 October, so on AlmaLinux the changelog is the only proof. CloudLinux rebuilt its own ea-php packages and kept their old release numbers (ea-php81 8.1.34-4 with a cloudlinux suffix, for example), so on CloudLinux compare the changelog rather than the cPanel release in the table.

Two more sources cover what the table doesn’t. Ondřej Surý’s Debian and Ubuntu repositories carry the patch for the end-of-life branches they ship, and Zend and TuxCare offer paid long-term support builds. PHP 7.3 and older got nothing from PHP itself. A few packagers patched those branches on their own: DirectAdmin did, Freexian ELTS did for Debian, and Ubuntu Pro shipped fixes for the PHP 7.2, 7.0 and 5 packages of its older releases. On 16.04 and 14.04 those also need the Legacy Support add-on. Remi’s 7.2 and 7.3 builds had no fix.

Install the Patched PHP 8.1 on RHEL-Family Servers

If your old PHP is the distribution’s own package, such as the default 8.0 on Rocky Linux 9 or the php:7.4 stream on Rocky Linux 8, take the vendor update with sudo dnf upgrade --refresh 'php*' and stay on it. RHEL and Rocky servers on the retired php:8.1 or EL8 php:8.0 streams get nothing from the vendor, so they move to Remi’s matching stream with the module commands below. On Rocky Linux, AlmaLinux and RHEL, PHP 8.1 usually comes from Remi in one of two forms, and the form decides the command. The commands below assume the Remi repository is already configured; the RHEL-family patch guide covers adding it. A server that switched its system PHP to Remi’s 8.1 stream updates the normal packages, while a server that runs 8.1 next to a newer system PHP uses Remi’s software collection under /opt/remi/php81. WHMCS servers often use the second form, so the billing system keeps 8.1 while everything else moves on.

For the module stream, enable Remi’s 8.1 stream and sync the installed packages to it. The reset clears whatever stream was enabled before, so the enable step cannot fail on a conflict. On an EL8 server coming from the retired php:8.0 stream, enable php:remi-8.0 instead of php:remi-8.1, or you land on a different minor version:

sudo dnf module reset -y php
sudo dnf module enable -y php:remi-8.1
sudo dnf distro-sync -y 'php*'

For the software collection, upgrade every php81 package together. The glob catches OPcache and the extension packages a WHMCS server usually carries, along with the ionCube loader built for the collection:

sudo dnf upgrade -y 'php81-*'

Remi’s package restarts php81-php-fpm on upgrade, but restart it yourself anyway so no worker keeps the old code, and restart httpd as well if the server loads PHP as an Apache module:

sudo systemctl restart php81-php-fpm

Then check that the interpreter and the loader agree. An application that needs ionCube fails on every request if the loader is missing, so you want to see both on one screen before you send traffic to the pool:

/opt/remi/php81/root/usr/bin/php -v

The Rocky 9 VM showed the patched 8.1 build with the ionCube loader attached:

PHP 8.1.34 (cli) (built: Oct 21 2026 09:22:04) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.1.34, Copyright (c) Zend Technologies
    with the ionCube PHP Loader v15.5.1, Copyright (c) 2002-2026, by ionCube Ltd.

We patched two production WHMCS servers this way. One moved its 8.1 collection from the vulnerable 8.1.34-5 to 8.1.34-6, the other moved an older 8.1.26 on Remi’s 8.1 stream to the same 8.1.34-6 build. In both cases the application, its cron jobs and its ionCube-encoded payment modules kept running on the same pool socket.

Verify the Backport Took

The usual checks give the wrong answer on these builds, which is why this step matters more than on a normal update. The version string stays at 8.1.34, /opt/remi/php81/root/usr/bin/php -r 'var_dump(ini_get("memory_hard_limit_percent"));' (plain php for the module stream) returns false just as it does on a vulnerable build, and the upload progress setting still says On. Only the package changelog proves the fix:

rpm -q --changelog php81-php-fpm | head -3

Remi’s entry names the mitigation and both CVEs:

* Wed Oct 21 2026 Remi Collet <[email protected]> - 8.1.34-6
- Mitigation fix Avoid state corruption via bailout
  CVE-2026-109618, CVE-2026-109619

For the module-stream form, run the same check against php-fpm. On Ubuntu 22.04 the equivalent is zgrep CVE-2026-109618 /usr/share/doc/php8.1-fpm/changelog.Debian.gz, and on cPanel rpm -q --changelog ea-php81-php-cli. The PHP2Shell checker runs the right check for each build automatically, which helps on servers that mix a system PHP and a collection.

Finally, confirm the round trip through the application rather than just the shell, because a pool that starts can still fail on the first encoded file it loads. On the second WHMCS server we compared the HTTP status and response size of ten client-area and admin URLs before and after. We also checked that the store and login pages rendered without ionCube errors, and waited for the next WHMCS cron run to finish cleanly.

Plan the Way Off the Old Branch

The backport closes PHP2Shell, and that’s all it does. Nobody upstream reviews the 8.1, 8.0 or 7.4 branches any more, so the next PHP security release may bring no backport at all. CISA’s alert for this one told organizations still running PHP 8.1 or earlier to take PHP systems offline until they can be patched or updated. The way out is the application upgrade you have been postponing, done in order:

  1. Upgrade the application to a release certified for a supported PHP branch, still on the old PHP.
  2. Install the newer PHP next to the old one, as a second pool or software collection.
  3. Move the application’s pool to the new PHP and test the integrations that run on it, such as payment gateways and provisioning modules.
  4. Remove the old PHP once nothing points at it.

For WHMCS, that means moving to 8.13 or a 9.x release before changing PHP, and checking every third-party module against it first. If you jump straight from 8.12 or earlier to 9.1, remove the legacy PayPal Basic and Checkout modules first, because WHMCS says they otherwise break the Admin Area with an HTTP 500. Until that’s done, keep session upload progress off in the pool and the server behind the PHP2Shell mitigations, because a backported old branch is the first thing to fall behind on the next advisory.

Where Each Old Branch Gets Its Fix

If you look after more than one platform, this is the lookup we use to decide where a given old PHP should get its patched build, and what proves it:

You runPatched build fromProof
Ubuntu 22.04 php8.1Ubuntu security archivechangelog names both CVEs
Remi php81 collection or remi-8.1 streamRemi 8.1.34-6“Mitigation fix” changelog entry
RHEL or Rocky 9 default php 8.0, RHEL or Rocky 8 php:7.4vendor AppStream erratum (RHSA-2026:87151, RHSA-2026:87080, and Rocky’s matching RLSA)dnf updateinfo list --security and the package changelog
RHEL or Rocky 9 php:8.1, RHEL or Rocky 8 php:8.0Remi remi-8.1 or remi-8.0 stream (vendor stream retired)“Mitigation fix” changelog entry
AlmaLinux 8 or 9 AppStream PHPAlmaLinux .alma.1 rebuilds, or Red Hat’s 7.4.33-8 on 8package changelog only
Remi remi-8.0 or remi-7.4 streamRemi 8.0.30-21 or 7.4.33-31“Mitigation fix” changelog entry
cPanel ea-php81, 80, 74EasyApache 4 change log 25.91EA-13611 to EA-13613 changelog lines
DirectAdmin php81 slotCustomBuild * patchpatch file present, no update pending
Debian 11 php7.4Freexian ELTS (paid) or the packages.sury.org php7.4 buildpackage changelog

The RHEL-family patch guide covers the supported branches on the same Remi repository, and the DirectAdmin notes show the CustomBuild side of the same backport running on 78 production sites.

Keep reading

UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 Security UFW Firewall Commands with Examples on Ubuntu 26.04 / 24.04 PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP Php PHP2Shell (CVE-2026-109618, CVE-2026-109619): Check and Patch PHP WireGuard VPN Setup: Ubuntu 26.04/24.04, Debian 13, Rocky 10 Debian WireGuard VPN Setup: Ubuntu 26.04/24.04, Debian 13, Rocky 10 Best Phone for GrapheneOS in 2026 (and Why to Skip the Pixel 11) Security Best Phone for GrapheneOS in 2026 (and Why to Skip the Pixel 11) Best PC for Blue Iris: Quick Sync, AI GPU and Drives by Camera Count Security Best PC for Blue Iris: Quick Sync, AI GPU and Drives by Camera Count How To Install Wazuh Security Platform using Ansible Ansible How To Install Wazuh Security Platform using Ansible

Leave a Comment

Press ESC to close