Skip to content
Tech

How to Fix PHP2Shell (CVE-2026-109618 & CVE-2026-109619): A Patch Guide for Every PHP Setup

Step-by-step fixes for the PHP2Shell flaws: check your version, patch on Ubuntu, Debian, RHEL, Docker, cPanel or Windows, apply stopgap mitigations, and check for compromise.

Dark server rack with glowing blue status lights beside a laptop showing blurred code under a faint red warning glow
Dark server rack with glowing blue status lights beside a laptop showing blurred code under a faint red warning glow
In this article

    As of 23 October 2026, 9:30 AM IST. Version numbers and commands come from php.net, the GitHub security advisories, and the vendor and distribution notices listed under Sources.

    This is a hands-on guide to fixing PHP2Shell, the two critical PHP flaws disclosed on 21 October 2026. If you want the news background first, read our report: "PHP2Shell": AI-found PHP flaws force government websites offline across Europe.

    The short version

    • The bugs: CVE-2026-109618 (GHSA-w8hw-w9fx-j79w, "RCE via OOM corruption") and CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp, "RCE via stale global state"). The PHP project rates both 9.2 (critical) on CVSS v4.0.
    • The fix: PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35, or a distribution or host package that contains the backported patch.
    • The step people forget: restart every PHP worker (PHP-FPM, Apache, LiteSpeed) or rebuild your containers. Until you do, the old code is still running.
    • Can't patch today? CISA and CERT-EU advise taking internet-facing PHP systems offline or isolating them until they are fixed. php.net lists some partial mitigations, covered below, but says plainly that only an update is a complete fix.

    Step 1: Check whether you're affected

    Affected and fixed versions (official)

    These are the branch versions as listed by php.net, CERT-EU and the Canadian Centre for Cyber Security:

    PHP branchVulnerableFixed upstream release
    8.5before 8.5.128.5.12
    8.4before 8.4.278.4.27
    8.3before 8.3.368.3.36
    8.2before 8.2.358.2.35
    8.1 and older (end of life)allNo new upstream release. php.net published minimal source backports for 7.4, 8.0 and 8.1, and some distributions and LTS vendors ship patched packages. Otherwise, upgrade.

    Per php.net, every PHP installation that serves web requests is at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. The PHP team rates mod_php as the highest-risk setup.

    Important: distribution version numbers can look "old" and still be patched

    Ubuntu, Debian, Red Hat, AlmaLinux and cPanel often backport security fixes into their existing package version instead of moving to the new upstream number. Ubuntu 24.04's patched package, for example, is 8.3.6-0ubuntu0.24.04.13, not 8.3.36. If your PHP came from your OS or your host, check the package version against your vendor's advisory (see Step 2). The upstream number alone won't tell you.

    How to see what you're running

    • Command line: php -v. The CLI can be a different build from the one serving your website, so also check the web-facing binary. On Debian/Ubuntu that is, for example, php-fpm8.3 -v; on RHEL-family systems, php-fpm -v.
    • Installed packages:
      • Ubuntu suggests apt list '?installed ?name(^php[0-9.]+$)'.
      • On any Debian/Ubuntu system, dpkg -l | grep php lists the full package versions.
      • On RHEL, Alma or Rocky, run rpm -q php-common (AlmaLinux's own instruction).
    • Through the web server: create a temporary file containing <?php phpinfo();, load it in a browser, and note the version and "Server API" line. Delete the file straight away.
    • Hosting panel: in cPanel, open MultiPHP Manager (or Select PHP Version on CloudLinux hosts) to see which PHP version each domain uses. On managed WordPress hosts, look for a "PHP version" setting or ask support directly whether the PHP2Shell patches are installed.
    • Hidden PHP: CISA notes that PHP is often bundled inside other software. CERT-EU points to the NCSC-NL php2shell repository, which tracks third-party products that embed PHP.

    Step 2: Patch or upgrade, by setup

    Ubuntu (official archive)

    Ubuntu published USN-8948-1 for supported releases and USN-8948-2 for ESM releases. Ubuntu's own recommendation is a full upgrade:

    sudo apt update && sudo apt upgrade

    Patched versions per USN-8948-1:

    Ubuntu releasePackageFixed version
    26.10php8.58.5.9-0ubuntu2.1
    26.04 LTSphp8.58.5.4-0ubuntu1.5
    24.04 LTSphp8.38.3.6-0ubuntu0.24.04.13
    22.04 LTSphp8.18.1.2-1ubuntu2.28
    20.04 / 18.04 / 16.04 / 14.04php7.4 / 7.2 / 7.0 / php5Fixed through Ubuntu Pro (ESM); see USN-8948-2

    To upgrade only PHP, name the packages you actually use. --only-upgrade skips anything that isn't installed. For example, on 24.04:

    sudo apt update
    sudo apt install --only-upgrade php8.3-cli php8.3-common php8.3-fpm php8.3-cgi libapache2-mod-php8.3

    Ubuntu notes that unattended-upgrades applies security updates automatically within about 24 hours. Affected services still need a restart, though.

    Debian

    Debian releasePackageFixed versionAdvisory
    13 (trixie)php8.48.4.27-1~deb13u1DSA-6574-1
    12 (bookworm)php8.28.2.35-1~deb12u1DLA-4848-1
    sudo apt update && sudo apt upgrade

    Then confirm with dpkg -l | grep php8 that the installed version matches the table.

    Ondřej Surý's PPA (ppa:ondrej/php) and packages.sury.org

    Many servers run newer PHP branches from this third-party repository. Launchpad lists builds such as 8.4.27-1+ubuntu24.04.1+deb.sury.org+1 and 8.3.36-1+ubuntu24.04.1+deb.sury.org+1. Update the same way as above, naming your branch:

    sudo apt update
    sudo apt install --only-upgrade php8.4-cli php8.4-common php8.4-fpm libapache2-mod-php8.4

    Then confirm the result. php8.4-fpm -v should report 8.4.27, and the equivalent check should show 8.3.36, 8.2.35 or 8.5.12 on other branches. We confirmed the Ubuntu PPA builds on Launchpad. If you use the Debian repository at packages.sury.org, check that the installed version also reports the fixed upstream number.

    RHEL, AlmaLinux and Rocky Linux

    • Red Hat rated its updates "Important". It released fixes including:
      • RHSA-2026:87080 for the php:7.4 module on RHEL 8.
      • RHSA-2026:87073 for the php:8.2 module on RHEL 8.
      • RHSA-2026:87151, which also lists both CVEs.
      Check Red Hat's errata for your exact stream.
    • AlmaLinux says patched packages for every PHP stream on AlmaLinux 8, 9 and 10 are in its production repositories. These include streams for which Red Hat had not yet shipped a fix at the time of AlmaLinux's post.
    • Rocky Linux published RLSA-2026:87080 for Rocky 8.

    AlmaLinux's published commands:

    sudo dnf clean metadata && sudo dnf upgrade 'php*'
    sudo systemctl restart php-fpm

    Then confirm with rpm -q php-common. You can also check that the package changelog mentions the fix:

    rpm -q --changelog php-common | grep -m1 CVE-2026-109618

    Again, expect backported version strings. AlmaLinux 9's patched PHP 8.3 stream, for example, is php-8.3.33-1.module_el9.8.0+328+1fdeb728.alma.1.

    Remi's repository (RHEL-family and Fedora)

    Remi Collet announced on 22 October that RPMs of 8.5.12, 8.4.27, 8.3.36 and 8.2.35 are available in remi-modular, and as Software Collections in remi-safe. Earlier, CERT-EU had warned that this volunteer-run repository would not have updates on release day, so make sure you actually received the new build:

    sudo dnf upgrade --refresh 'php*'
    php -v

    If you are on PHP 8.1 or older from Remi, that branch is end of life. Remi's documented way to switch the default PHP to a supported branch on Enterprise Linux is:

    sudo dnf module switch-to php:remi-8.4/common

    Test your application before doing this on production. A branch upgrade is a bigger change than a patch.

    Windows and XAMPP

    • Official Windows builds of the fixed releases are on windows.php.net:
      • php-8.5.12-Win32-vs17-x64.zip
      • php-8.4.27-Win32-vs17-x64.zip
      • php-8.3.36-Win32-vs16-x64.zip
      • php-8.2.35-Win32-vs16-x64.zip
      Each also comes in a non-thread-safe "nts" variant. Use the thread-safe (Win32) build for Apache with mod_php, and NTS for IIS/FastCGI.
    • XAMPP: at the time of writing, the Apache Friends download page still listed XAMPP for Windows with PHP 8.2.12, which is vulnerable. There was no fixed XAMPP installer yet. Swapping in a newer PHP build by hand is possible but unofficial; the steps are below.

    To replace XAMPP's PHP manually:

    1. Stop Apache in the XAMPP Control Panel.
    2. Back up the whole C:\xampp\php folder.
    3. Extract php-8.2.35-Win32-vs16-x64.zip over it, keeping your existing php.ini.
    4. Start Apache and confirm the version with C:\xampp\php\php.exe -v and a phpinfo page.

    XAMPP is designed for local development. If a XAMPP install is reachable from the internet, take it offline first.

    Docker (official php images)

    Docker says its Official Images and Hardened Images were updated within 24 hours of the upstream release. Fixed tags on Docker Hub include, for example:

    • php:8.5.12-fpm
    • php:8.4.27-fpm
    • php:8.4.27-apache
    • php:8.3.36-fpm
    • php:8.3.36-fpm-alpine
    • php:8.2.35-fpm

    Pin a fixed tag in your Dockerfile:

    FROM php:8.4.27-fpm

    Then rebuild and recreate the containers. Pulling the image is not enough on its own:

    docker compose build --pull
    docker compose up -d --force-recreate
    docker compose exec <service> php -v

    If you use floating tags like php:8.4-fpm, --pull picks up the patched image, but only for that rebuild. Scan your registry for older images that are still deployed somewhere. If you run Laravel in containers, our Docker Compose setup guide for Laravel shows where the PHP base image is defined.

    cPanel/WHM servers and shared hosting

    cPanel lists these patched EasyApache 4 packages:

    • ea-php85 8.5.12
    • ea-php84 8.4.27
    • ea-php83 8.3.36
    • ea-php82 8.2.35
    • ea-php81 8.1.34-5
    • ea-php80 8.0.30-12
    • ea-php74 7.4.33-19

    Note that the 7.4, 8.0 and 8.1 fixes keep their old version numbers. For servers you administer, cPanel's instructions are:

    # CentOS/RHEL-based:  yum clean all && yum makecache
    # Ubuntu-based:       apt clean && apt update
    /scripts/update-packages

    Check each installed branch, not just the default. Replace php81 with each version you have:

    rpm -q --changelog ea-php81-php-cli | grep -m1 CVE-2026-109618
    # Ubuntu-based cPanel:
    zgrep -E 'CVE-2026-109618' /usr/share/doc/ea-php81-php-cli/changelog.Debian.gz

    Then restart PHP-FPM and Apache:

    /scripts/restartsrv_apache_php_fpm
    /scripts/restartsrv_httpd

    cPanel's notice points to TuxCare's Extended Lifecycle Support for PHP versions older than 7.4. CloudLinux's alt-php packages are tracked separately on CloudLinux's status page.

    If you're a shared-hosting customer, you can't patch the PHP binary yourself. What you can do:

    • In MultiPHP Manager (or Select PHP Version), move each site off end-of-life versions to 8.2 or newer.
    • Ask your host in writing to confirm that the PHP2Shell patches are installed and that PHP workers have been restarted.

    Built from source

    Download 8.5.12, 8.4.27, 8.3.36 or 8.2.35 from php.net/downloads, rebuild with your usual configure flags, and reinstall. For 7.4, 8.0 or 8.1, php.net says its security team published minimal source backports. Treat these as a stopgap while you migrate.

    Then restart everything that runs PHP

    A running FPM child or Apache process keeps the old code in memory. Use the commands for your setup:

    • PHP-FPM on Debian/Ubuntu: sudo systemctl restart php8.3-fpm (use your branch number).
    • PHP-FPM on RHEL/Alma/Rocky: sudo systemctl restart php-fpm.
    • Apache with mod_php: sudo systemctl restart apache2 on Debian/Ubuntu, or sudo systemctl restart httpd on RHEL-family systems.
    • Nginx: Nginx doesn't run PHP itself; restarting PHP-FPM is what matters. sudo systemctl reload nginx is harmless if you also changed configuration.
    • LiteSpeed / OpenLiteSpeed: restart the web server, typically sudo systemctl restart lsws, so that lsphp processes are respawned.
    • Can't tell what's still running old code? Ubuntu's advice is simply to reboot.

    Finally, verify through the web server, not just the CLI. Load a temporary phpinfo page, check the version or package, then delete the page.

    Step 3: Temporary mitigations if you truly can't patch yet

    The first choice, per CISA, CERT-EU and the Canadian Centre for Cyber Security, is to take unpatched internet-facing PHP offline or restrict access to it, for example behind a VPN or IP allow-list. CISA says a web application firewall "may provide some protection" until you update. Everything below comes from php.net's advisory, which states that these measures "reduce exposure to specific attack paths but do not make an installation safe".

    Against CVE-2026-109618 (OOM corruption)

    • Set memory_limit=-1 in a way applications can't override. In an FPM pool, use php_admin_value[memory_limit] = -1; in Apache, use php_admin_value memory_limit -1. Then make sure no pool, directory or application setting restores a finite limit. php.net warns that an underlying allocation failure can still reach the bug.
    • Lower post_max_size: 8M if the app doesn't take uploads, 32M if it does. Keep upload_max_filesize below it.
    • Enforce limits at the web server or proxy on decoded request size, input fields, multipart parts, headers and nesting depth, including for requests without a usable Content-Length.
    • Use a container or cgroup memory limit as the availability backstop, ideally one that kills only the affected worker. Do not use RLIMIT_AS or RLIMIT_DATA, because php.net says they can trigger the vulnerable path.

    Against CVE-2026-109619 (stale global state)

    • Set session.upload_progress.enabled=0 in php.ini, the FPM pool or the server configuration, then restart every worker. ini_set() and FPM .user.ini files apply too late. php.net says this blocks the confirmed session paths but not other variants.
    • On PHP 8.5 only, set fatal_error_backtraces=0 and restart.
    • Remove invalid or deprecated per-request INI settings that produce warnings before a request starts. Places to look:
      • php_value lines in .htaccess under mod_php.
      • PHP_VALUE parameters passed by Nginx to FPM.
      Hiding warnings with error_reporting, display_errors or @ does not help.
    • Fix fatal errors and timeouts that attackers can trigger, such as failing upload or import handlers.

    Example FPM pool entries (for instance in /etc/php/8.3/fpm/pool.d/www.conf), followed by an FPM restart:

    php_admin_value[memory_limit] = -1
    php_admin_flag[session.upload_progress.enabled] = off
    php_admin_value[post_max_size] = 8M
    php_admin_value[upload_max_filesize] = 6M

    Removing the memory limit lets a runaway script consume the whole machine. Treat these settings as a bridge of hours or days and undo them after you patch.

    Step 4: Check whether you were already compromised

    Patching closes the door, but it doesn't remove anyone who got in first. CISA's alert said it had not seen exploitation at publication. Other reports conflict on this point. Amazon, quoted by Reuters in our news report, said the flaws were being broadly exploited, while the Canadian Centre noted a public proof of concept. Given that, assume an internet-facing server that sat unpatched after 21 October needs checking. The official guidance is to look for:

    • PHP workers spawning shells or downloading tools (CISA). Inspect the process tree with ps -ef --forest and look for sh, bash, curl, wget or unknown binaries under php-fpm, apache2/httpd or lsphp.
    • Unexpected files (Canadian Centre). List PHP files changed recently in your web root, adjusting the path and date:
      sudo find /var/www -type f -name '*.php' -newermt '2026-10-15' -ls
      Also check upload, cache and tmp directories for .php files that shouldn't be there. Look for code that uses eval(, base64_decode(, assert( or system( on request input; these are typical web-shell patterns, though legitimate code uses some of them too. On WordPress, wp core verify-checksums (WP-CLI) compares core files with the official release.
    • New accounts, cron jobs and SSH keys. Check crontab -l for the web user (www-data, apache or nginx), /etc/cron.* and ~/.ssh/authorized_keys, plus admin users in your CMS.
    • Outbound connections (Canadian Centre). sudo ss -tnp shows established connections and the process that owns each one.
    • Logs. Review web server, PHP-FPM, authentication and endpoint logs. Look for bursts of large multipart POSTs, repeated "Allowed memory size exhausted" or segmentation-fault messages, and requests to files you don't recognise.

    If you find signs of compromise, don't just delete the shell. Take the server offline and preserve logs. Rebuild from a known-clean source or backup, patch before reconnecting, and rotate every secret the web user could read:

    • database passwords in wp-config.php or .env
    • app keys and API tokens
    • SMTP credentials
    • cloud keys
    • SSH keys

    Report the incident to your national CERT where that applies. In India, that is CERT-In.

    Step 5: Hardening checklist

    • Inventory every PHP runtime, including appliances and vendor products that embed it (see the NCSC-NL list).
    • Get off end-of-life PHP. PHP 8.1 and older receive no upstream fixes. php.net also notes that 8.2 reaches end of life on 31 December 2026, so plan the next upgrade now. Our guide to what PHP 8.3 and 8.4 change for Laravel developers covers the code side of the move.
    • Automate security updates and the restart. Unattended-upgrades or dnf-automatic install packages but don't always restart PHP. On Debian/Ubuntu, the needrestart tool flags services still running old libraries.
    • Prefer PHP-FPM over mod_php where you can. The PHP team rated mod_php the highest-risk setup for this flaw, and FPM lets each site run in its own pool and as its own user.
    • Least privilege. Never run PHP as root. The web user shouldn't be able to write to application code, only to specific upload or cache folders, and those folders should not execute PHP.
    • Keep per-request INI overrides clean. Audit .htaccess php_value lines and Nginx PHP_VALUE parameters for deprecated or invalid settings.
    • Sensible request limits at the proxy and in post_max_size, even after patching. php.net says these reduce denial-of-service risk.
    • Put a WAF or CDN in front of public sites, and make sure the origin accepts traffic only from it.
    • Watch the right feeds: php.net news, your distribution's security list, and CERT-In, CISA or CERT-EU advisories.
    • Have tested backups and central logs. They make Step 4 possible. For Laravel apps, see our 12 security checks to run before launch.

    What we couldn't confirm

    • Exploitation status: CISA (21 October) had not observed exploitation; Amazon, as reported by Reuters, said it was broad. Treat the risk as active.
    • Exact release time: php.net had scheduled the release for 14:00 UTC; CERT-EU recorded 13:30 UTC.
    • Third-party packages: availability for every package and mirror can lag. Always confirm the version you actually have installed.
    • XAMPP: the manual PHP swap described above is not an Apache Friends procedure.

    Sources

    Written by Raj

    Founder & Editor

    I write HorizonDesk: practical, no-hype articles on technology, money, business and the vehicles we drive. Everything here is something I’ve researched, tested or wanted explained clearly myself.