A plain dnf upgrade now patches PHP2Shell on most RHEL, Rocky Linux and AlmaLinux servers, but on Rocky 10, Rocky 8 and AlmaLinux the dnf security list will not tell you so. Red Hat shipped errata for CVE-2026-109618 and CVE-2026-109619 on 21 October, Rocky Linux synced them overnight, and AlmaLinux had already pushed its own patched builds hours earlier. The morning after, dnf updateinfo on our Rocky 10 and Rocky 8 VMs still listed no advisory for the fixed packages sitting in AppStream.
This guide follows the vendor update for every stream that got one, then covers the retired streams that never will, where Remi’s repository is the fix. It also shows how to move a server that went to Remi on release day back to AppStream, and how to prove the result from another machine. Everything was run on Rocky Linux 10, 9 and 8 with SELinux enforcing on 21 and 22 October 2026, and the AlmaLinux builds were checked straight from its repositories.
AppStream Now Carries the Fix
Red Hat fixed every PHP stream that is still inside its lifecycle, and Rocky Linux rebuilt the same packages. AlmaLinux went further and patched the retired streams too, using its own .alma.1 builds. Find the PHP you run in the first column:
| Release and PHP | RHEL and Rocky fixed build | Red Hat advisory | AlmaLinux fixed build |
|---|---|---|---|
| 10, php (8.3, default) | 8.3.36-1.el10_2 | RHSA-2026:87070 | 8.3.36-1.el10_2 |
| 10, php8.4 | 8.4.27-1.el10_2 | RHSA-2026:87071 | 8.4.24-1.el10_2.alma.1 |
| 9, php 8.0 (default) | 8.0.30-8.el9_8.1 | RHSA-2026:87151 | 8.0.30-8.el9_8.alma.1 |
| 9, php:8.3 | 8.3.36-1 | RHSA-2026:87079 | 8.3.36-1 |
| 9, php:8.2 | 8.2.35-1 | RHSA-2026:87072 | 8.2.35-1 |
| 9, php:8.1 | retired May 2025, no fix | none | 8.1.32-1 .alma.1 |
| 8, php:8.2 | 8.2.35-1 | RHSA-2026:87073 | 8.2.33-1 .alma.1 |
| 8, php:8.0 | retired Nov 2024, no fix | none | 8.0.30-1 .alma.1 |
| 8, php:7.4 | 7.4.33-8 | RHSA-2026:87080 | 7.4.33-8 |
| 8, php:7.3 and 7.2 (7.2 is the default) | retired, no fix | none | 7.3.20-1 and 7.2.24-1 .alma.1 |
Red Hat rebased its supported streams to the upstream releases, so 8.3.36 and 8.2.35 are the same versions upstream shipped. Only the end-of-life branches it still supports, 8.0 on RHEL 9 and 7.4 on RHEL 8, got a backport. Red Hat scores both CVEs Important (CVSS 3.1 of 8.1) rather than Critical, because RHEL runs PHP-FPM by default, but PHP’s advisory confirms remote code execution against FPM, so treat the update as urgent either way.
Every AlmaLinux .alma.1 build we downloaded carried the same changelog entry, dated 21 October and written by an AlmaLinux maintainer, naming both CVEs:
* Wed Oct 21 2026 Andrew Lukoshko <[email protected]> - 8.0.30-8.alma.1
- Fix state corruption via bailout
CVE-2026-109618 (GHSA-w8hw-w9fx-j79w)
CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp)
Going by those repository changelogs (we read the RPMs rather than installing them on an AlmaLinux host), a plain update on AlmaLinux fixes every PHP stream, retired or not. On RHEL and Rocky Linux, the four retired rows need the Remi section further down.
dnf updateinfo Lags the Repository
You’d normally confirm a security fix with dnf updateinfo, and on 22 October that check gave the wrong answer on two of three Rocky releases. Rocky 9 listed RLSA-2026:87151 for its PHP 8.0 update. Rocky 10 listed nothing for 8.3.36, because its advisory had not reached the metadata yet. Rocky 8 listed nothing for 8.2.35 for a different reason: RLSA-2026:87073, the Rocky twin of the RHEL 8 php:8.2 erratum, did not exist in Rocky’s advisory system at all. The AlmaLinux errata feeds had no entry for either CVE. We infer from those empty lists that security-only updates, dnf upgrade --security or dnf-automatic with upgrade_type = security, skip the fixed PHP on those systems until the advisories appear. So check for the package, not the advisory:
- refresh the metadata and list pending PHP updates
- upgrade only the PHP packages
- confirm the version or the changelog
To reproduce the state of a server that hadn’t patched yet, we put the Rocky 10 VM back on the vulnerable 8.3.33 build from AppStream first. Then we asked dnf what it would upgrade:
sudo dnf check-update --refresh 'php*'
All four installed packages had a newer build waiting in AppStream, even though the security list was empty:
php-cli.x86_64 8.3.36-1.el10_2 appstream
php-common.x86_64 8.3.36-1.el10_2 appstream
php-fpm.x86_64 8.3.36-1.el10_2 appstream
php-opcache.x86_64 8.3.36-1.el10_2 appstream
Upgrade only PHP, so an emergency fix doesn’t drag a new kernel and a reboot window along with it:
sudo dnf upgrade -y 'php*'
The transaction moved the same four packages to the fixed release and installed nothing else:
Upgrading:
php-cli x86_64 8.3.36-1.el10_2 appstream 3.6 M
php-common x86_64 8.3.36-1.el10_2 appstream 710 k
php-fpm x86_64 8.3.36-1.el10_2 appstream 1.9 M
php-opcache x86_64 8.3.36-1.el10_2 appstream 364 k
Complete!
On RHEL itself the same command works once the system is registered, and sudo dnf upgrade --advisory=RHSA-2026:87070 pins it to the advisory if your change process asks for one. Swap the advisory ID for your release from the table.
The Proof Depends on the Build
Three kinds of fixed build exist on these distributions, and each one leaves different evidence. Check the kind you installed, because the wrong check reports a patched server as vulnerable:
- Red Hat rebases (8.3.36, 8.2.35, 8.4.27): the version is the proof
- Red Hat backports (8.0.30-8.el9_8.1, 7.4.33-8): the changelog names both CVEs
- AlmaLinux
.alma.1builds: the changelog names both CVEs
For a rebased build, ask PHP for its version:
php -v | head -1
Rocky 10 reported the fixed release:
PHP 8.3.36 (cli) (built: Oct 19 2026 14:00:34) (NTS gcc x86_64)
Do not grep the changelog of a rebased build for the CVE number. The Red Hat 8.3.36 and 8.2.35 entries only say “rebase to 8.3.36”, and on both VMs the grep came back empty on a fully patched server. A backport is the opposite case, because its version does not move. The Rocky 9 PHP 8.0 build stayed at 8.0.30, so the changelog is where the fix shows up:
rpm -q --changelog php-fpm | grep -B2 -m1 'CVE-2026-109618'
The match sits under the Red Hat 8.0.30-8.1 entry, a few lines below an unrelated CVE from the same update:
CVE-2026-93682
- Avoid state corruption via bailout
CVE-2026-109618, CVE-2026-109619
Any output from that grep means the installed package carries the fix. An empty result on an 8.0.30 or 7.4.33 build means the update has not been applied yet.
Retired Streams Need Remi
RHEL 9’s php:8.1 stream left the Red Hat lifecycle in May 2025, and RHEL 8’s php:8.0 left in November 2024, so Red Hat and Rocky will not ship a fix for either. On RHEL and Rocky Linux, Remi’s repository is the way to patch them without changing the PHP branch. Remi Collet also maintains the Red Hat PHP packages, and his repository had fixed builds on release day:
| You run | Switch to | Fixed build | How it’s fixed |
|---|---|---|---|
| PHP 8.1 (RHEL 9, retired) | php:remi-8.1 | 8.1.34-6 | end-of-life backport |
| PHP 8.0 (RHEL 8, retired) | php:remi-8.0 | 8.0.30-21 | end-of-life backport |
| PHP 8.4 or 8.5 (any release) | php:remi-8.4 or php:remi-8.5 | 8.4.27, 8.5.12 | upstream release |
| PHP 7.2 or 7.3 (RHEL 8, retired) | none | no fix | latest Remi builds date from 7 August |
The 7.2 and 7.3 row is the hard one. As of 22 October, the newest Remi 7.2.34-29 and 7.3.33-22 builds both date from August, so a Rocky 8 server on the default 7.2 stream has no patch from Red Hat, Rocky or Remi. AlmaLinux is the exception, with its 7.2.24 and 7.3.20 .alma.1 builds. Everyone else has to change branch, and the nearest vendor option is the php:7.4 stream, fixed in 7.4.33-8 by RHSA-2026:87080 and still inside its RHEL 8 lifecycle:
sudo dnf module reset -y php
sudo dnf module enable -y php:7.4
sudo dnf distro-sync -y 'php*'
On a Rocky 8 VM running the default 7.2 stream with php-cli, php-fpm, php-opcache and php-mysqlnd (plus php-common and php-pdo as dependencies), the sync upgraded all six installed PHP packages to the fixed release in one transaction, and PHP-FPM came back active. php -v and the php-cli changelog then showed the fixed build:
PHP 7.4.33 (cli) (built: Oct 21 2026 17:33:12) ( NTS )
* Wed Oct 21 2026 Jitka Plesnikova <[email protected]> - 7.4.33-8
- Avoid state corruption via bailout
CVE-2026-109618, CVE-2026-109619
That is a PHP version change, so test the application before it goes near production. Until it is tested, turn off session upload progress, the path the PHP advisory names for remote code execution:
echo 'session.upload_progress.enabled = Off' | sudo tee /etc/php.d/99-php2shell.ini
Test the FPM configuration, then reload so new workers pick the file up:
sudo php-fpm -t
sudo systemctl reload php-fpm
That drop-in closes one attack path, not the bug. The PHP2Shell mitigation guide covers the rest of the interim hardening.
Remi Needs EPEL First
The remi-release package depends on epel-release. On Rocky Linux and AlmaLinux, dnf pulls EPEL in automatically from the extras repository, and our VMs ended up with both from a single command. RHEL has no extras repository, so Remi’s configuration page lists CodeReady Builder and EPEL as prerequisites there. We did not run these two on a registered RHEL host; they are the documented RHEL 10 commands, and the 9 and 8 versions only change the number:
sudo subscription-manager repos --enable codeready-builder-for-rhel-10-$(arch)-rpms
sudo dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-10.noarch.rpm
Next, install the Remi release package for your major version. Run only the line that matches your server:
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-10.rpm
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-8.rpm
Check that both repositories landed, because the modular one is where the fixed PHP streams live:
rpm -q epel-release remi-release
dnf repolist | grep -i remi
On the Rocky 10 VM, both packages and both repositories showed up after the one install:
epel-release-10-7.el10_1.noarch
remi-release-10.2-1.el10.remi.noarch
remi-modular Remi's Modular repository for Enterprise Linux 10 - x86_64
remi-safe Safe Remi's RPM repository for Enterprise Linux 10 - x86_64
With remi-modular enabled, dnf can see the fixed streams in the next step.
Module Streams Decide the Build
Does RHEL 10 even have module streams? Red Hat dropped modular content from RHEL 10, but Remi still publishes his PHP as streams there, so the same three commands work on 8, 9 and 10. They run in a fixed order:
dnf module reset phpclears the stream that was enabled beforednf module enable php:remi-8.1picks the Remi stream for your branchdnf distro-sync 'php*'replaces every installed PHP package with the build from that stream
Use distro-sync rather than upgrade, because it also handles the packages whose version would go down, which upgrade leaves alone. Swap remi-8.1 for your branch from the table:
sudo dnf module reset -y php
sudo dnf module enable -y php:remi-8.1
sudo dnf distro-sync -y 'php*'
On release day, before the AppStream errata existed, we ran this on the Rocky 9 VM with php:remi-8.0. It moved the four installed packages to Remi’s patched 8.0 build and added nothing else:
Upgrading:
php-cli x86_64 8.0.30-21.module_php.8.0.el9.remi remi-modular 4.6 M
php-common x86_64 8.0.30-21.module_php.8.0.el9.remi remi-modular 823 k
php-fpm x86_64 8.0.30-21.module_php.8.0.el9.remi remi-modular 1.6 M
php-opcache x86_64 8.0.30-21.module_php.8.0.el9.remi remi-modular 737 k
Complete!
Remi’s end-of-life builds keep the version and change the release number, so the changelog is the proof again. Its newest entry names the fix and both CVEs:
* Wed Oct 21 2026 Remi Collet <[email protected]> - 8.0.30-21
- Mitigation fix Avoid state corruption via bailout
CVE-2026-109618, CVE-2026-109619
The end-of-life patch also turns session upload progress off in the code itself, so upload progress bars stop working whatever php.ini says. A backport closes PHP2Shell, but nobody upstream reviews these branches for the next bug. Schedule the move to a supported branch once the emergency is over. The end-of-life PHP guide explains what the minimal patch changes.
Software Collections Patch Separately
Remi’s software collection packages (php81-php-fpm, php83-php-fpm and friends) live under /opt/remi/ and have their own service names, so neither AppStream nor the module switch touches them. They come from the remi-safe repository and update with a plain upgrade, one collection at a time:
sudo dnf upgrade 'php81-*'
sudo systemctl restart php81-php-fpm
On the Rocky 9 VM, the 8.1 collection came in at php81-php-fpm-8.1.34-6.el9.remi, with the same “Mitigation fix” changelog entry as the 8.0 build. That’s the package that keeps ionCube-encoded applications such as older WHMCS releases running while they’re stuck on PHP 8.1, and it’s often the only PHP on a RHEL 9 box that needs Remi at all.
Leaving Remi Is a Downgrade
If you switched to Remi on release day, you can now return to the vendor build. Two things surprise people here. First, dnf calls the move a downgrade even at the same version, because rpm sorts the Remi release string above the Red Hat one. Second, dnf module reset changes the module state the moment it runs, and on 8 and 9 it brings back the default stream of the release:
- Rocky 8: reset returns to php:7.2, which Red Hat and Rocky never fixed
- Rocky 9: reset returns to the non-modular 8.0
- Rocky 10: there is no AppStream module, so reset returns to the default php
So on 8 and 9, enable the stream you ran before Remi straight after the reset. On the Rocky 8 VM, which ran php:8.2 before the switch, the sequence was:
sudo dnf module reset -y php
sudo dnf module enable -y php:8.2
sudo dnf distro-sync -y 'php*'
dnf moved all four packages to the fixed Red Hat 8.2.35, listed as a downgrade:
Downgrading:
php-cli x86_64 8.2.35-1.module+el8.10.0+40394+d21b33fb appstream 3.7 M
php-common x86_64 8.2.35-1.module+el8.10.0+40394+d21b33fb appstream 751 k
php-fpm x86_64 8.2.35-1.module+el8.10.0+40394+d21b33fb appstream 1.9 M
php-opcache x86_64 8.2.35-1.module+el8.10.0+40394+d21b33fb appstream 416 k
Complete!
On Rocky 10, and on Rocky 9 when you came from the default 8.0, skip the enable line: reset plus distro-sync landed on 8.3.36-1.el10_2 and 8.0.30-8.el9_8.1. Check the version or changelog afterwards, as in the proof section, before you remove the Remi repository.
php-fpm Restarts During the Update
The php-fpm RPM restarts the service when it is upgraded or downgraded, and on all three VMs the unit’s start time matched the minute of the transaction. Check it rather than assume it:
systemctl show -p ActiveEnterTimestamp php-fpm
A start time from the minute of the update means the service restarted. Then look for any PHP process that still holds a deleted library:
for p in $(pgrep -f 'php-fpm|httpd'); do sudo grep -qE ' /(usr|lib|opt).*\(deleted\)$' /proc/$p/maps && echo "restart needed: $p"; done
No “restart needed” lines means the workers run the patched code. Apache with mod_php is the exception worth checking by hand: if httpd -M lists php_module, run sudo systemctl restart httpd rather than a reload. SELinux needs no changes for any of this, because the packages keep the same paths and file contexts, and getenforce still read Enforcing on every VM afterwards.
Prove It From Outside
The package database tells you what is installed, but a scanner on the other side of the network only sees what the web server runs. RHEL-family PHP leaves expose_php on by default, so the response header gives the running version away. From a second machine, request any PHP page:
curl -sI http://10.0.1.50/index.php | grep -iE '^(HTTP|X-Powered-By)'
From the Rocky 9 VM against the patched Rocky 10 web server, the header showed the fixed build was the one answering:
HTTP/1.1 200 OK
X-Powered-By: PHP/8.3.36
That header only proves a rebased build, since a backport still reports 8.0.30 or 7.4.33. It is also why scanners can list vulnerable servers in minutes. Once you’ve confirmed the fix, set expose_php = Off in /etc/php.ini and restart php-fpm, or in /etc/opt/remi/php81/php.ini followed by a restart of php81-php-fpm for a collection. For an audit across every PHP binary on the host, run the PHP2Shell checker, and the PHP install guide for RHEL 10 covers a fresh setup if you’re building a new server rather than fixing one.