Tutorial

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

Security & Monitoring9 min read13 steps

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. 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.

Contents· PHP2Shell at a glance1/14
  1. 01PHP2Shell at a glance
  2. 02Key takeaways
  3. 03Why these two flaws are so dangerous
  4. 04Which version to install for your branch
  5. 05Check whether your servers are exposed
  6. 06Take an inventory of the PHP versions being served
  7. 07Apply the fix
  8. 08Update PHP for your environment
  9. 09If you cannot update right away
  10. 10Add a barrier in front of PHP
  11. 11Look for traces of exploitation
  12. 12Checks to run after applying the fix
  13. 13On shared hosting
  14. 14The official documentation

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: researchers believe the affected branches go all the way back to PHP 5.4, although not every single version has been tested individually.

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, on the other hand, had no update available at the time of the announcement: 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 still set to a branch with no fix available (7.x, 8.0, 8.1) must be switched over to a maintained version.

  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.

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.

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.

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.

Need help?

Browse our help center and FAQ, or reach our team — callback, WhatsApp or email. Support in French, English and Arabic.

Message us on WhatsAppopens in a new tab