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

Target versions

8.5.12, 8.4.27, 8.3.36, 8.2.35 or newer — or an OS-vendor package whose changelog lists CVE-2026-109618 and CVE-2026-109619. PHP 8.1 and older get no new 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

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.

2. Install the fixed release

Covers OS packages and Ondřej Surý’s PPA / packages.sury.org. Stock Debian/Ubuntu packages keep their old upstream number and backport the fix, so the changelog check is the one that counts.

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 (incl. Ubuntu Pro / ESM), Debian LTS, RHEL and other LTS vendors received the patches in advance. Check your package changelog for the CVE, as shown above.
  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. 5.4 – 7.3: no backport. Isolate the host, apply mitigations, 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