MarkRelayJuggling email for all your products and sites? Managing it in one place is simpler.Unlimited domainsUnlimited mailboxesTeam access controlMCP + SkillsWebmail + Broadcast + API + Webhook

PHP2Shell fix · patch guide

How to fix PHP2Shell

The fix is a PHP update plus a restart. No config change, plugin update or WAF rule replaces it. Commands for every common platform, and how to prove the patch actually took.

Last reviewed 2026-10-28

23 October follow-up: CVE-2026-115560

The PHP notice describes a separate RCE risk: configuration uses php_binary or php_serialize, application code switches to php with ini_set() before session_start(), and upload progress is enabled. Keep session.upload_progress.enabled = 0 until the next regular bugfix release; if already disabled, keep it off. Applications that never switch serializers at runtime may keep it enabled. This is separate from the two PHP2Shell fixes.

Target versions

8.5.12, 8.4.27, 8.3.36, 8.2.35 or newer — or a vendor build explicitly confirmed to fix both CVEs for your exact product and update channel. Match the installed revision to the vendor advisory; a changelog search alone is not decisive. CloudLinux documents fixed native builds whose changelogs omit CVE IDs. PHP 8.1 and older get no new PHP-project release — see below.

BranchVulnerableFixed in
8.5< 8.5.128.5.12
8.4< 8.4.278.4.27
8.3< 8.3.368.3.36
8.2< 8.2.358.2.35
8.1 · 8.0 · 7.4allSource backport only
5.4 – 7.3allNo fix

Verified vendor package versions

First fixed builds for the listed releases and source packages, covering both CVEs. These are vendor revisions, so an older upstream number can be patched. Check your exact release, official channel and installed build, then restart every web worker. Reviewed 2026-10-22.

Operating systemPHP packageFirst fixed vendor buildUpdate channelEvidence
SLE 15 SP7 · Web and Scripting Modulephp88.3.36-150700.3.24.1SUSE subscription / enabled moduleSUSE-SU-2026:4844-1
Ubuntu 26.10php8.58.5.9-0ubuntu2.1Security updatesUSN-8948-1
Ubuntu 26.04 LTSphp8.58.5.4-0ubuntu1.5Security updatesUSN-8948-1
Ubuntu 24.04 LTSphp8.38.3.6-0ubuntu0.24.04.13Security updatesUSN-8948-1
Ubuntu 22.04 LTSphp8.18.1.2-1ubuntu2.28Security updatesUSN-8948-1
Ubuntu 20.04 LTSphp7.47.4.3-4ubuntu2.29+esm5Ubuntu Pro / ESMUSN-8948-2
Ubuntu 18.04 LTSphp7.27.2.24-0ubuntu0.18.04.17+esm14Ubuntu Pro / ESMUSN-8948-2
Ubuntu 16.04 LTSphp7.07.0.33-0ubuntu0.16.04.16+esm22Ubuntu Pro + Legacy SupportUSN-8948-2
Ubuntu 14.04 LTSphp55.5.9+dfsg-1ubuntu4.29+esm18Ubuntu Pro + Legacy SupportUSN-8948-2
Debian 13 (trixie)php8.48.4.27-1~deb13u1Security updatesDSA-6574-1
Debian 12 (bookworm)php8.28.2.35-1~deb12u1Debian LTS / securityDLA-4848-1
RHEL 8 · php:7.4php7.4.33-8.module+el8.10.0+25117+30337ccaRed Hat AppStreamRHSA-2026:87080
AlmaLinux 10 · php 8.3 (default)php8.3.33-1.el10_2.alma.1AlmaLinux AppStreamAlmaLinux blog
AlmaLinux 10 · php8.4php8.48.4.24-1.el10_2.alma.1AlmaLinux AppStreamAlmaLinux blog
AlmaLinux 9 · php 8.0 (default)php8.0.30-8.el9_8.alma.1AlmaLinux AppStreamAlmaLinux blog
AlmaLinux 9 · php:8.3php8.3.33-1.module_el9.8.0+328+1fdeb728.alma.1AlmaLinux AppStreamAlmaLinux blog
AlmaLinux 9 · php:8.2php8.2.33-1.module_el9.8.0+327+37dfd4b4.alma.1AlmaLinux AppStreamAlmaLinux blog
AlmaLinux 9 · php:8.1php8.1.32-1.module_el9.8.0+326+528679fc.alma.1AlmaLinux, retired streamAlmaLinux blog
AlmaLinux 8 · php:8.2php8.2.33-1.module_el8.10.0+4320+c30d6dbb.alma.1AlmaLinux AppStreamAlmaLinux blog
AlmaLinux 8 · php:8.0php8.0.30-1.module_el8.10.0+4319+26f10322.alma.1AlmaLinux, retired streamAlmaLinux blog
AlmaLinux 8 · php:7.4php7.4.33-8.module_el8.10.0+4321+cf5fd4bbAlmaLinux AppStreamAlmaLinux blog
AlmaLinux 8 · php:7.3php7.3.20-1.module_el8.10.0+4316+90ea5fe5.alma.1AlmaLinux, retired streamAlmaLinux blog
AlmaLinux 8 · php:7.2php7.2.24-1.module_el8.10.0+4315+7b734bf3.alma.1AlmaLinux, retired streamAlmaLinux blog

This snapshot does not establish the status of other repositories, Ubuntu PPAs, Debian sid/forky or every third-party image. Later builds in the same official update channel may include these fixes; confirm the vendor advisory. The version checker conservatively asks you to verify vendor packages. AlmaLinux and RHEL rows apply to the named module stream; install that build or higher. Future fixes for retired streams are not guaranteed. Red Hat has additional advisories covering both CVEs, including RHSA-2026:87079 for RHEL 9 php:8.3. This table is a verified subset, not the complete Red Hat release list; check the advisory for your product and stream. The SUSE row covers only the Web and Scripting Module 15-SP7 (aarch64, ppc64le, s390x, x86_64). SUSE modules require the appropriate registered product and enabled repositories; they are included in the SLES subscription. Apply the advisory through YaST Online Update or zypper patch, then restart the actual PHP-FPM/Apache workers. A released PHP8 package does not establish that PHP7, SLE 16 or an SP6 installation without its required support channel is fixed.

1. Find every PHP runtime

Missed runtimes are the most common reason a fleet stays exposed. Run this on each host.

shell
# PHP processes running right now, and when they started
ps -eo pid,lstart,cmd | grep -E '[p]hp-fpm|[h]ttpd|[a]pache2|[f]rankenphp|[l]sphp'

# CLI and FPM can be different builds — check both
php -v
ls /usr/sbin/php-fpm* 2>/dev/null && for f in /usr/sbin/php-fpm*; do "$f" -v | head -1; done

# Is Apache running PHP in-process (mod_php)? That is the highest-risk setup.
apachectl -M 2>/dev/null | grep -i php

# PHP version inside every running container
docker ps --format '{{.Names}}' | while read c; do
  printf '%s: ' "$c"; docker exec "$c" php -r 'echo PHP_VERSION, PHP_EOL;' 2>/dev/null || echo 'no php'
done

Also check base images in your registry, CI runners, staging and preview environments, appliances that embed PHP, and old servers no one owns anymore. From the outside, curl -sI https://site | grep -i x-powered-by sometimes reveals the version, but most sites hide it. Use the NCSC-NL product inventory to find bundled PHP; confirm each product’s status with its vendor.

2. Install the fixed release

Use the update channel that supplied your PHP: OS packages and Ondřej Surý’s PPA / packages.sury.org are separate sources. Compare the exact installed revision with that source’s advisory; the changelog can help verify a backport.

shell
sudo apt update

# Upgrade only the PHP packages that are installed
sudo apt install --only-upgrade $(dpkg-query -W -f='${db:Status-Abbrev} ${binary:Package}\n' 'php*' | awk '$1=="ii"{print $2}')

# Proof the fix is in: the changelog should mention the CVE
# (replace 8.3 with your version)
apt changelog php8.3-common 2>/dev/null | grep -m1 -E 'CVE-2026-10961[89]' || echo 'NOT FIXED in this package yet'

3. Restart everything that runs PHP

Installing a package replaces files on disk. Running processes keep executing the old, vulnerable code until they restart.

shell
# Debian / Ubuntu (adjust the version)
sudo systemctl restart php8.3-fpm
sudo systemctl restart apache2        # if using mod_php

# RHEL family
sudo systemctl restart php-fpm httpd

# Anything still mapped to the old, deleted PHP binary needs a restart
sudo grep -l -E '(php|libphp)[^ ]* \(deleted\)' /proc/[0-9]*/maps 2>/dev/null
sudo needrestart -r l                 # Debian/Ubuntu helper, if installed

# Long-running PHP workers
php artisan queue:restart             # Laravel queues
php artisan octane:reload             # Octane (Swoole / RoadRunner / FrankenPHP)
sudo supervisorctl restart all        # supervisor-managed workers

4. Verify from the web server’s point of view

shell
# Version of the FPM binary that actually serves traffic
php-fpm8.3 -v 2>/dev/null || php-fpm -v

# Compare process start times with when you upgraded
ps -o pid,lstart,cmd -C php-fpm8.3,php-fpm,apache2,httpd

# Ask the web server itself (use a random name, delete straight after)
F="v-$(openssl rand -hex 8).php"
echo '<?php echo PHP_VERSION;' | sudo tee /var/www/html/$F >/dev/null
curl -s https://your-site.example/$F; echo
sudo rm /var/www/html/$F

New behaviour after the patch

The fix replaces PHP’s abort-on-OOM with a soft limit checked at a safe point, plus a second, higher hard memory limit that kills the process immediately with _exit(). When the hard limit hits, shutdown functions and destructors do not run. If your app does work in register_shutdown_function (logging, cleanup), test memory-heavy paths after upgrading.

On PHP 8.1, 8.0, 7.4 or older?

These branches are end-of-life. The PHP project will not ship releases for them, but because of the severity it published minimal source backports.

  1. Best: migrate to PHP 8.4 or 8.5. If you must move fast, 8.3 is a smaller jump from 8.1 (8.2 leaves support at the end of 2026).
  2. OS package: Ubuntu (including Pro / ESM), Debian LTS, RHEL and other LTS vendors may supply backports. Verify both CVEs for the exact package, release and support channel using the vendor advisory and changelog; then restart workers.
  3. Building from source: the PHP team’s backport branches — PHP 8.1, PHP 8.0, PHP 7.4. They fix these two CVEs only; the branch stays unsupported.
  4. Older branches: no PHP-project source backport is listed below this range. Distribution vendors may still provide fixes: Ubuntu confirms patched packages, including older branches through Pro / ESM. Confirm coverage of both CVEs and restart workers; if no patched build is available, isolate the host and migrate.

5. Then

  • Remove any temporary mitigations that hurt performance, e.g. pm.max_requests = 1 or memory_limit = -1, and go back to sane limits.
  • Check for signs of earlier exploitation: detection guide. Patching does not undo a compromise.
  • On PHP 8.2? Its security support ends 2026-12-31. Schedule the move to 8.4 or 8.5 now, while the upgrade path is fresh.

Related