Tutorial

Security & Monitoring

PHP2Shell: patch CVE-2026-109618 and CVE-2026-109619

On 21 October 2026, the PHP security team issued emergency releases PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. These versions fix two flaws nicknamed “PHP2Shell”: CVE-2026-109618 and CVE-2026-109619, both rated 9.2 out of 10 under CVSS 4.0. They target the interpreter itself, not an application: a single anonymous HTTP request is enough, before your code even starts to run. Exploitation attempts have been observed since the very day of publication. Update of 27 October: CISA added both flaws to its Known Exploited Vulnerabilities catalog on 26 October. Here is what these two flaws do, how to find out whether your servers are exposed, and how to fix them on Debian, Ubuntu, RHEL, cPanel and Docker.

16 min read16 steps
Contents· Update of 27 October: CISA lists PHP2Shell among exploited flaws1/24
  1. 01Update of 27 October: CISA lists PHP2Shell among exploited flaws
  2. 02What the KEV catalog entry says
  3. 03What this changes for you
  4. 04PHP2Shell at a glance
  5. 05Key takeaways
  6. 06Why these two flaws are so dangerous
  7. 07Which version to install for your branch
  8. 08Check whether your servers are exposed
  9. 09Take an inventory of the PHP versions being served
  10. 10Apply the fix
  11. 11Update PHP for your environment
  12. 12cPanel and EasyApache 4: an unchanged version number does not mean “not fixed”
  13. 13EasyApache 4 25.91: what cPanel published, package by package
  14. 14Check that a backported package really is fixed
  15. 15CloudLinux: the same fix, one to two days later depending on the product
  16. 16cPanel/WHM's internal PHP and Roundcube: another PHP to update
  17. 17After patching, still turn off upload progress tracking
  18. 18If you cannot update right away
  19. 19Add a barrier in front of PHP
  20. 20Look for traces of exploitation
  21. 21Checks to run after applying the fix
  22. 22More signs to look for, according to primary sources
  23. 23On shared hosting
  24. 24The official documentation

Update of 27 October: CISA lists PHP2Shell among exploited flaws

On 26 October 2026, CISA, the US cybersecurity agency, added both flaws (CVE-2026-109618 and CVE-2026-109619) to its Known Exploited Vulnerabilities (KEV) catalog. An entry appears there only once real-world exploitation is established: until now, that finding rested on watchTowr's network traps; it now comes from a public authority.

CISA set the due date at 29 October 2026, three days after the addition. That date binds only US federal agencies, but it gives the order of magnitude of the urgency a public body attaches to these flaws: a few days, not the next monthly maintenance window.

What the KEV catalog entry says

  • Date added: 26 October 2026, for CVE-2026-109618 (“PHP Memory Corruption Vulnerability”) as well as for CVE-2026-109619 (“PHP Stale Internal Request-State Vulnerability”).
  • Due date: 29 October 2026 for both.
  • Required action: apply the vendor's mitigations in line with directive BOD 26-04 (Prioritizing Security Updates Based on Risk), or stop using the product if no mitigation is available.
  • Forensic triage: the entry points to CISA's “Forensics Triage Requirements”. Patching is not enough: you also need to look for a compromise that happened before the fix.
  • Ransomware: use in a ransomware campaign is listed as “unknown” so far.

What this changes for you

Nothing in the procedure: the fixed versions and the commands remain those of this article. What changes is the priority. Before 26 October you could still weigh “right now” against “at the next maintenance window”; that is no longer defensible. Internet-facing servers come first. And the question is no longer only “am I protected?” but “was I hit between 21 October and the moment I patched?”: the checklist further down has been extended accordingly.

PHP2Shell at a glance

PHP2Shell is not a flaw in WordPress, Laravel or a plugin: the PHP engine itself is at fault. Every PHP application that answers web requests is therefore affected, whatever its code looks like. The security advisory names PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed.

Both flaws share the same underlying mechanism. When PHP abruptly aborts a request (exhausted memory, fatal error), it can leave behind an internal state that is only half initialised or that has already been freed. A subsequent request, handled by the same PHP process, then reuses that state: this is what opens the door to remote code execution.

Key takeaways

  • CVE-2026-109618 — memory corruption when memory_limit is exceeded: request data that was only partially initialised is later treated as valid during the cleanup phase.
  • CVE-2026-109619 — stale global state: state that belongs to one request survives after its memory has been freed and can be read by the next request on the same process.
  • Score: 9.2 out of 10 under CVSS 4.0 for each one, with no authentication and no user interaction required.
  • Patched versions: PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. The project no longer publishes releases for 8.1 and the older branches.
  • Exploitation: watchTowr reports exploitation attempts caught in its network honeypots as early as 21 October 2026.
  • Scope: according to the NVD, CVE-2026-109619 affects every version since PHP 5.4.0, and CVE-2026-109618 every version since PHP 7.3.0.

Why these two flaws are so dangerous

Most web flaws require a specific form, a user account or a vulnerable plugin. PHP2Shell needs none of that: the most common trigger is the processing of a multipart/form-data upload with session upload progress tracking (session.upload_progress), a mechanism that PHP enables by default and that runs before the first byte of your application. A web application firewall living inside your code, a WordPress security plugin or a Laravel middleware therefore all come into play too late.

The second factor is the execution model itself. PHP-FPM and Apache reuse the same process for hundreds of successive requests: this is precisely what allows a booby-trapped request to contaminate the next one. According to Imperva, the published techniques range from crashing processes to reading arbitrary files through the OPcache cache, including leaking around 67 KB of process memory inside an oversized PHPSESSID cookie. That memory can contain database passwords or API keys.

Which version to install for your branch

Scroll the table

PHP branchPatched versionWhat to do
8.58.5.12Update, then restart PHP
8.48.4.27Update, then restart PHP
8.38.3.36Update, then restart PHP
8.28.2.35Update, then plan the migration to 8.3 or 8.4
8.1, 8.0, 7.4No official releaseYour distribution's patched package, a source backport, or else migration
7.3 and earlier, 5.xNo published fixMigrate to a maintained branch

Check whether your servers are exposed

Watch out for a classic trap: the version shown by php -v on the command line is not necessarily the one that serves your websites. A server can have several versions of PHP-FPM installed side by side, and each site can point to a different one. You therefore need to check the version that is actually served over the web, one site at a time.

Take an inventory of the PHP versions being served

  1. List the installed versions

    On Debian or Ubuntu:

    dpkg-query -W -f='${db:Status-Abbrev} ${Package} ${Version}\n' 'php*' | awk '$1=="ii"' | grep -E 'php[0-9.]+-(fpm|cli|common)|libapache2-mod-php'

    On RHEL, AlmaLinux or Rocky Linux:

    rpm -qa 'php*' 'ea-php*' 'alt-php*' | sort
  2. Find the PHP processes that are running

    systemctl list-units --type=service --state=running | grep -E 'php|apache2|httpd|lsws'

    Every active phpX.Y-fpm service is a version you need to check and to patch.

  3. Read the version served by a site

    Drop a temporary file at the root of the site, query it, then delete it immediately afterwards:

    echo '<?php echo PHP_VERSION, " ", PHP_SAPI;' > /var/www/monsite/v-check.php
    curl -s https://monsite.example/v-check.php
    rm /var/www/monsite/v-check.php

    A result such as 8.3.36 fpm-fcgi indicates a patched version being served by PHP-FPM.

  4. Check your Docker containers

    docker ps --format '{{.Names}}' | while read c; do echo "$c: $(docker exec "$c" php -r 'echo PHP_VERSION;' 2>/dev/null)"; done

    A container without PHP shows nothing at all after its name.

Apply the fix

Linux distributions and extended support vendors received the fixes ahead of the public release. The patched package from your distribution therefore does not necessarily carry the number 8.3.36: Ubuntu 24.04, for example, patches its own version 8.3.6 in the package 8.3.6-0ubuntu0.24.04.13. Rely on your distribution's security advisory rather than on the PHP version number alone. In every case, a restart is essential: as long as the old processes keep running, it is the old code that answers your requests.

Update PHP for your environment

  1. Debian and Ubuntu

    sudo apt update
    sudo apt install --only-upgrade $(dpkg-query -W -f='${db:Status-Abbrev} ${Package}\n' 'php*' | awk '$1=="ii"{print $2}')
    sudo systemctl restart 'php*-fpm'
    sudo systemctl restart apache2   # if you use mod_php

    If your versions come from a third-party repository (for example the ondrej/php PPA), check that the version installed after the update really is one of the patched versions.

  2. RHEL, AlmaLinux, Rocky Linux

    sudo dnf upgrade 'php*'
    sudo systemctl restart php-fpm
    sudo systemctl restart httpd

    AlmaLinux published its patched packages on 21 October 2026. The community Remi repository, for its part, published versions 8.2.35, 8.3.36, 8.4.27 and 8.5.12 on 22 October 2026: if you depend on it, check the installed version afterwards.

  3. cPanel server (EasyApache 4 or CloudLinux)

    sudo dnf upgrade 'ea-php*' 'alt-php*'
    /scripts/restartsrv_apache_php_fpm
    /scripts/restartsrv_httpd

    On an older server, replace dnf with yum. Then check each version in WHM › MultiPHP Manager: any account set to 7.3 or older, with no fix from cPanel, must be switched over to a maintained version; 7.4, 8.0 and 8.1 are fixed by backport but end-of-life (see below).

  4. Docker containers

    docker compose pull
    docker compose up -d --force-recreate
    docker compose exec app php -r 'echo PHP_VERSION;'

    If your Dockerfile starts from a php:8.3-fpm image, rebuild the image with docker compose build --pull: without --pull, Docker reuses the old base image that it has in its cache.

  5. Check the result

    Run the check from the previous step again on every site. The fix is only in place if the version served over the web is patched, not merely the version installed on disk.

cPanel and EasyApache 4: an unchanged version number does not mean “not fixed”

The EasyApache 4 (EA4) changelog of 21 October 2026, version 25.91, covers seven PHP branches. For the maintained branches the number goes up: ea-php82 moves from 8.2.34 to 8.2.35, ea-php83 from 8.3.35 to 8.3.36, ea-php84 from 8.4.26 to 8.4.27 and ea-php85 from 8.5.11 to 8.5.12. For ea-php74, ea-php80 and ea-php81, which cPanel has nonetheless declared end-of-life, the fix is backported: the PHP version number stays put and only the package revision changes.

That is the trap: php -v then shows an “old” version even though the code is fixed, and an old version whose changelog does not cite the two CVEs remains vulnerable. Only reading the package changelog settles it. A version 25.92, released the same day, also rebuilt the meta-packages so that their changelog mentions the backport too.

EasyApache 4 25.91: what cPanel published, package by package

Scroll the table

PackageWhat changedHow to check
ea-php858.5.11 to 8.5.12Package version
ea-php848.4.26 to 8.4.27Package version
ea-php838.3.35 to 8.3.36Package version
ea-php828.2.34 to 8.2.35Package version
ea-php81, ea-php80, ea-php74Fix backported, PHP number unchangedRPM changelog: look for both CVEs
ea-php73 and olderNo entry in the 25.91 changelogMigrate, or see CloudLinux below

Check that a backported package really is fixed

  1. Read the EasyApache package changelog

    rpm -qa 'ea-php*-php-cli'
    rpm -q --changelog ea-php81-php-cli | grep -E 'CVE-2026-(109618|109619)'

    The first command lists the installed branches; replay the second for each one. A line citing the CVEs confirms the fix. No line at all: the package is not fixed, whatever its version number.

  2. Do the same for alt-php packages

    On a CloudLinux server, the versions chosen in Select PHP Version are alt-php packages:

    rpm -q --changelog alt-php85-common | grep -E 'CVE-2026-(109618|109619)'
  3. Go through the accounts one by one

    In WHM › MultiPHP Manager, every domain must point to a branch whose package passed this check. The 7.4, 8.0 and 8.1 branches fixed by backport remain end-of-life: they are a holding position, not a destination.

CloudLinux: the same fix, one to two days later depending on the product

CloudLinux publishes its own packages, and they do not arrive at the same time as cPanel's. According to its bulletin, the ea-php packages (5.2 to 8.5) reached stable on 21 October, the alt-php ones on 22 October for CloudLinux 7, 8 and 9, the system's native PHP late on 22 October (UTC), and the packages numbered like PHP.net on 23 October.

These too are backports: ea-php84 there stays, for instance, at 8.4.26 with a CloudLinux-specific revision (8.4.26-2) while being fixed. Two points of caution: on 21 October, alt-php packages announced as released were in fact in the TuxCare ELS repository and not in CloudLinux's own; and the advice to disable the php-els repository, given on 22 October, was corrected on the evening of the 23rd: leave it enabled. Finally, Imunify360 published its WAF rules on 21 October (ruleset 8.83), and version 8.88 has been stable since the 23rd: they block known vectors and do not replace the update.

cPanel/WHM's internal PHP and Roundcube: another PHP to update

MultiPHP and EasyApache govern the PHP of your sites. The cPanel and WHM interfaces and the Roundcube webmail run instead on an internal PHP shipped with cPanel itself, which choosing a version in MultiPHP Manager does not touch. On 21 October 2026, cPanel published a security update for this internal PHP, stating that it “affects PHP in cPanel interfaces, including Roundcube webmail”.

It delivers PHP 8.4.27 in cPanel & WHM 138.0.18, 136.0.50 and 134.0.66, and backported fixes for PHP 8.1.34 in version 110.0.153. These interfaces are open to your customers' logins, so often reachable from the internet: they are prime targets. Read your version with /usr/local/cpanel/cpanel -V (or in the WHM banner) and compare it with these numbers. If your release series is not listed, do not assume it is fixed: check cPanel's release notes and move to a series that is.

After patching, still turn off upload progress tracking

On 23 October 2026, PHP published a supplementary informational advisory: it recommends keeping session.upload_progress.enabled=0 until the next regular bugfix release, to guard against a code-execution vector it considers unlikely. It targets applications that set session.serialize_handler to php_binary or php_serialize, then switch it back to php at runtime with ini_set() before session_start(). PHP announces a full advisory for CVE-2026-115560 with the next release. The setting only concerns the file upload progress bar.

If you cannot update right away

The official PHP advisory suggests settings that reduce exposure without making the installation safe. They are there to buy you a few hours, not to replace the fix. They must be set in php.ini, in the PHP-FPM pool or in the web server configuration, never through ini_set() or a .user.ini file: those are read too late, after the request has already been parsed.

Against CVE-2026-109619, disable upload progress tracking. On PHP 8.5, also disable fatal error backtraces:

; in the FPM pool, for example /etc/php/8.3/fpm/pool.d/www.conf
php_admin_flag[session.upload_progress.enabled] = off
; PHP 8.5 only:
php_admin_flag[fatal_error_backtraces] = off

Against CVE-2026-109618, the advisory recommends memory_limit = -1 enforced through php_admin_value, so that no application can redefine it, together with a bounded request size (post_max_size = 8M, or 32M if you need file uploads). In return, memory has to be capped somewhere else, with a container or cgroup limit. Do not use RLIMIT_AS or RLIMIT_DATA: they are exactly what causes the vulnerable allocation failure.

Then restart all PHP processes so that the settings take effect.

Add a barrier in front of PHP

Cap the size of requests at the web server level, before they ever reach PHP: client_max_body_size 8m; in nginx, LimitRequestBody 8388608 in Apache. If your site sits behind Cloudflare in proxied mode, WAF rules targeting the known PHP2Shell vectors have been rolled out to every account, the free plan included. These are safety nets only: nothing but the PHP update actually closes the flaw.

Look for traces of exploitation

The published attempts leave characteristic traces behind: PHP processes that crash, bursts of out-of-memory errors and multipart uploads that are abnormally large. If your server was exposed before you updated it, take ten minutes to check.

Checks to run after applying the fix

  1. Look for killed PHP-FPM processes

    sudo grep -E 'exited on signal (11|6)' /var/log/php*-fpm.log | tail -20

    A series of processes killed by signal 11 (SIGSEGV) since 21 October is a warning sign.

  2. Look for memory limit overruns

    sudo grep -rh 'Allowed memory size' /var/log/php* /var/log/nginx /var/log/apache2 2>/dev/null | tail -20
  3. Spot recently created PHP files

    sudo find /var/www /home -name '*.php' -newermt '2026-10-20' -not -path '*/vendor/*' -not -path '*/cache/*' -ls

    An unknown file with a generic name, in an upload folder or in /tmp, should be examined before you delete anything.

  4. When in doubt, treat the server as compromised

    Isolate the machine, keep a copy of the logs, then rotate the database passwords, the API keys and the secrets stored in .env files: a memory leak may have exposed them without leaving any other trace behind.

More signs to look for, according to primary sources

  • Unusually large or deeply nested multipart requests in the web server logs (watchTowr).
  • PHP processes that crash or hit their memory limit (watchTowr).
  • Files written to web roots and outbound connections from PHP processes that you cannot explain (watchTowr).
  • New web shells, unexpected cron jobs and recently created accounts tied to PHP processes (watchTowr).
  • Preserve before you clean up: PHP and web server logs, configuration snapshots and suspicious files, before changing anything (watchTowr). CISA also attaches forensic triage requirements to its KEV entry.
  • No numeric indicator is published: as of 27 October, neither watchTowr, nor PHP, nor the GitHub advisory for CVE-2026-109618 gives an IP address, a hash, a file name or a request signature. Be wary of unsourced indicator lists: the absence of an address in your logs proves nothing.

On shared hosting

On shared hosting, you are not the one who updates PHP: the hosting provider maintains the versions on offer. Your part of the job is to check that your site uses a branch that still receives fixes. In cPanel, the version selection tool (MultiPHP Manager, or Select PHP Version on a CloudLinux server) shows the active version for each domain. If your site still runs on 7.4, 8.0 or 8.1, those branches no longer receive official PHP releases: switch to 8.3 or 8.4 after testing your site, following our PHP migration checklist.

The official documentation

The technical details and the workaround settings are published in the PHP security advisory and in the GitHub advisories GHSA-w8hw-w9fx-j79w (CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026-109619). For distribution packages, see the Ubuntu announcement and the AlmaLinux one. For the follow-up: CISA's KEV catalog, the EasyApache 4 changelog, the cPanel & WHM release notes, CloudLinux's bulletin and watchTowr's FAQ.

Stay in control of your PHP versions

On a ServOrbit.com VPS, you have root access: you apply a PHP fix as soon as it is published, you choose the version used by each site and you restart your services whenever you decide to.