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.
| Branch | Vulnerable | Fixed in |
|---|---|---|
| 8.5 | < 8.5.12 | 8.5.12 |
| 8.4 | < 8.4.27 | 8.4.27 |
| 8.3 | < 8.3.36 | 8.3.36 |
| 8.2 | < 8.2.35 | 8.2.35 |
| 8.1 · 8.0 · 7.4 | all | Source backport only |
| 5.4 – 7.3 | all | No 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 system | PHP package | First fixed vendor build | Update channel | Evidence |
|---|---|---|---|---|
| SLE 15 SP7 · Web and Scripting Module | php8 | 8.3.36-150700.3.24.1 | SUSE subscription / enabled module | SUSE-SU-2026:4844-1 |
| Ubuntu 26.10 | php8.5 | 8.5.9-0ubuntu2.1 | Security updates | USN-8948-1 |
| Ubuntu 26.04 LTS | php8.5 | 8.5.4-0ubuntu1.5 | Security updates | USN-8948-1 |
| Ubuntu 24.04 LTS | php8.3 | 8.3.6-0ubuntu0.24.04.13 | Security updates | USN-8948-1 |
| Ubuntu 22.04 LTS | php8.1 | 8.1.2-1ubuntu2.28 | Security updates | USN-8948-1 |
| Ubuntu 20.04 LTS | php7.4 | 7.4.3-4ubuntu2.29+esm5 | Ubuntu Pro / ESM | USN-8948-2 |
| Ubuntu 18.04 LTS | php7.2 | 7.2.24-0ubuntu0.18.04.17+esm14 | Ubuntu Pro / ESM | USN-8948-2 |
| Ubuntu 16.04 LTS | php7.0 | 7.0.33-0ubuntu0.16.04.16+esm22 | Ubuntu Pro + Legacy Support | USN-8948-2 |
| Ubuntu 14.04 LTS | php5 | 5.5.9+dfsg-1ubuntu4.29+esm18 | Ubuntu Pro + Legacy Support | USN-8948-2 |
| Debian 13 (trixie) | php8.4 | 8.4.27-1~deb13u1 | Security updates | DSA-6574-1 |
| Debian 12 (bookworm) | php8.2 | 8.2.35-1~deb12u1 | Debian LTS / security | DLA-4848-1 |
| RHEL 8 · php:7.4 | php | 7.4.33-8.module+el8.10.0+25117+30337cca | Red Hat AppStream | RHSA-2026:87080 |
| AlmaLinux 10 · php 8.3 (default) | php | 8.3.33-1.el10_2.alma.1 | AlmaLinux AppStream | AlmaLinux blog |
| AlmaLinux 10 · php8.4 | php8.4 | 8.4.24-1.el10_2.alma.1 | AlmaLinux AppStream | AlmaLinux blog |
| AlmaLinux 9 · php 8.0 (default) | php | 8.0.30-8.el9_8.alma.1 | AlmaLinux AppStream | AlmaLinux blog |
| AlmaLinux 9 · php:8.3 | php | 8.3.33-1.module_el9.8.0+328+1fdeb728.alma.1 | AlmaLinux AppStream | AlmaLinux blog |
| AlmaLinux 9 · php:8.2 | php | 8.2.33-1.module_el9.8.0+327+37dfd4b4.alma.1 | AlmaLinux AppStream | AlmaLinux blog |
| AlmaLinux 9 · php:8.1 | php | 8.1.32-1.module_el9.8.0+326+528679fc.alma.1 | AlmaLinux, retired stream | AlmaLinux blog |
| AlmaLinux 8 · php:8.2 | php | 8.2.33-1.module_el8.10.0+4320+c30d6dbb.alma.1 | AlmaLinux AppStream | AlmaLinux blog |
| AlmaLinux 8 · php:8.0 | php | 8.0.30-1.module_el8.10.0+4319+26f10322.alma.1 | AlmaLinux, retired stream | AlmaLinux blog |
| AlmaLinux 8 · php:7.4 | php | 7.4.33-8.module_el8.10.0+4321+cf5fd4bb | AlmaLinux AppStream | AlmaLinux blog |
| AlmaLinux 8 · php:7.3 | php | 7.3.20-1.module_el8.10.0+4316+90ea5fe5.alma.1 | AlmaLinux, retired stream | AlmaLinux blog |
| AlmaLinux 8 · php:7.2 | php | 7.2.24-1.module_el8.10.0+4315+7b734bf3.alma.1 | AlmaLinux, retired stream | AlmaLinux 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.
# 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'
doneAlso 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.
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.
# 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 workers4. Verify from the web server’s point of view
# 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/$FNew 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.
- 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).
- 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.
- 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.
- 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 = 1ormemory_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
- Linux distro packagesFirst fixed PHP packages for PHP2Shell on Ubuntu, Debian, RHEL, AlmaLinux and SUSE, why the version can look old, and how to verify your build.
- Website & version checkerCheck a website’s public PHP signals by URL, or paste a PHP version. Understand PHP2Shell patch status, limits and what to ask your hosting provider.
- MitigationsExplore temporary PHP2Shell mitigations: memory and upload limits, upload progress, backtraces and cgroups, with their coverage and tradeoffs.
- Hosting providersPHP2Shell on shared and managed hosting: check and switch PHP versions in cPanel or Plesk, review provider updates and ask your host about patches.
- DetectionSignals of PHP2Shell exploitation, log searches you can run now, vendor detections, and what to do if you find signs of compromise.
- Cloudflare WAFCloudflare’s PHP2Shell WAF rule IDs for Free and paid plans, how to confirm they are active and blocking, and why you still need to patch PHP behind Cloudflare.
- session.upload_progressDoes disabling session.upload_progress stop PHP2Shell? Learn what it covers, where to configure it, how to verify it and what to restart.