Under attack? Get incident response help

[email protected]
Vulnerabilities

PHP2Shell: commands to find, patch and verify every PHP server

PHP2Shell lets attackers run code on unpatched PHP without logging in. Copy-paste commands to find every PHP runtime, update, restart and verify.

Also in:TürkçeDeutschFrançaisEspañolItalianoPolskiNederlandsEesti

Illustration: PHP2Shell: commands to find, patch and verify every PHP server
Video. It loads from YouTube (Google) only when you press play.

PHP2Shell is a pair of critical vulnerabilities in the PHP engine itself, CVE-2026-109618 and CVE-2026-109619, published on 21 October 2026 with a severity of CVSS 9.2. On an unpatched runtime, an attacker without an account can execute code as the PHP worker or read process memory that holds passwords, session tokens, API keys and certificates. Your application code does not need a bug: the researchers say even a "Hello World" page on an affected PHP version can be exposed. The only real fix is a fixed PHP release, followed by a restart of every process that runs PHP. This guide gives you the commands.

Which versions are fixed

BranchVulnerableFixed in
8.2below 8.2.358.2.35
8.3below 8.3.368.3.36
8.4below 8.4.278.4.27
8.5below 8.5.128.5.12

PHP 8.1, 8.0 and 7.x are end of life and get no fix. If you still run them, the patch is a move to a supported branch.

Linux distributions often backport security fixes into their own packages without changing to the upstream version number. On those systems the version alone does not tell you enough: check the package changelog for the two CVE numbers (step 2).

Step 1: find every PHP runtime

Start with the inventory, because the runtime nobody remembered is the one that gets hit. On each server:

php -v
systemctl list-units --type=service --all 'php*' 'apache2*' 'httpd*'
ls /usr/sbin/php-fpm* /usr/bin/php[0-9]* 2>/dev/null

The first line shows the CLI version, the second the PHP-FPM pools and web servers that run, and the third every PHP version installed side by side (a common source of forgotten runtimes). List the installed packages too:

dpkg -l 'php*' | grep '^ii'      # Debian, Ubuntu
rpm -qa 'php*' 'ea-php*'         # RHEL, AlmaLinux, Rocky, cPanel

For containers, check the PHP version inside every running image:

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

Do not forget staging, cron and queue servers, build agents, and managed hosting plans. For a hosted plan, ask the provider in writing which PHP build you run and when it was patched.

Step 2: install the fixed release

Debian and Ubuntu (replace 8.3 with your branch):

sudo apt update
sudo apt install --only-upgrade 'php8.3*'
apt changelog php8.3-fpm | grep -E 'CVE-2026-10961[89]'

RHEL, AlmaLinux, Rocky Linux:

sudo dnf upgrade 'php*'
rpm -q --changelog php-fpm | grep -E 'CVE-2026-10961[89]'

cPanel servers with EasyApache 4 packages:

sudo dnf upgrade 'ea-php*'      # older systems: sudo yum update 'ea-php*'

Alpine Linux (package names carry the branch, e.g. php83):

apk update && apk upgrade 'php83*'

Docker images: pin the fixed tag in the Dockerfile, for example FROM php:8.3.36-fpm, then rebuild without the cache and replace the running containers:

docker build --pull --no-cache -t myapp:php2shell .
docker compose up -d --build --force-recreate

If the changelog grep prints nothing, your distribution has not shipped the fix yet. Keep checking, use the official release from php.net where you build PHP yourself, and apply the interim measures below in the meantime.

Step 3: restart everything that loaded the old PHP

A new package changes nothing in processes that are already running. Restart them:

sudo systemctl restart php8.3-fpm     # Debian, Ubuntu (one per installed version)
sudo systemctl restart php-fpm        # RHEL family
sudo systemctl restart apache2        # Apache with mod_php (httpd on RHEL)
sudo supervisorctl restart all        # long-running workers under Supervisor
php artisan queue:restart             # Laravel queue workers

On Debian and Ubuntu, sudo needrestart -r l lists services that still use old libraries.

Step 4: verify

Check each runtime again, this time the one the web server uses, not only the CLI:

php-fpm8.3 -v                         # or: php-fpm -v
systemctl status php8.3-fpm | grep Active

The version must be at or above the fixed release (or the package changelog must list both CVEs), and the "Active: since" time must be after the update. On WordPress you can also read the version under Tools, Site Health, Info, Server. Do not put a phpinfo() page online to check: it discloses exactly what attackers look for.

When the update has to wait

These settings from the researchers reduce exposure while you prepare the update. They are not a fix:

; php.ini
post_max_size = 8M                  ; as low as your uploads allow
max_input_nesting_level = 32        ; as low as your forms allow
session.upload_progress.enabled = 0
fatal_error_backtraces = 0          ; where your PHP version supports it

; PHP-FPM pool: recycle workers more often (costs performance)
pm.max_requests = 100

The values are examples; test them against your application. Setting memory_limit = -1 blocks some attack paths, but only use it together with an operating-system limit such as cgroup v2 memory.max, and never replace it with RLIMIT_AS or RLIMIT_DATA. WAF and CDN rules from your provider help as a bridge, not as a replacement for the update.

Look for signs of an attack

There is no single detection rule. These commands surface the signals the researchers describe:

grep -E 'Allowed memory size of [0-9]+ bytes exhausted' /var/log/php*-fpm.log /var/log/apache2/error.log
grep -E 'exited on signal (6|11)' /var/log/php*-fpm.log
awk '$9 == 500' /var/log/nginx/access.log | wc -l

Memory-limit errors after unusual multipart requests, bursts of HTTP 500 responses, PHP workers that crash on signals, and unusually large PHPSESSID cookies in responses deserve a closer look. None of them proves an attack, and their absence does not prove you were not affected. If you find them, keep the logs, patch, and rotate the secrets that lived in PHP memory: database passwords, application keys and API tokens. Our incident response team can take over the investigation, and our managed security service can watch your servers after the patch.

Where to start

  1. Run the inventory commands on every server and container and list each PHP version.
  2. Update with your package manager or rebuild your images on a fixed tag.
  3. Restart PHP-FPM, Apache and every PHP worker.
  4. Verify the version and the restart time, and keep the output as evidence.
  5. Grep your logs for the signs above and rotate secrets on any system that shows them.

Details: PHP2Shell advisory site, PHP Foundation announcement, Wiz Research.

How we can help

Incident response

When something goes wrong, we contain it, find the root cause and help you recover.

Contact us
AngelForce team

Written by the AngelForce team: security consultants with CEH, LPT Master and CISA certifications who test, monitor and defend systems for organisations across Europe and Türkiye.

Written with AI assistance from public sources listed below, and reviewed by the AngelForce team before publication.

Sources

  1. www.php2shell.com/
  2. thephp.foundation/blog/2026/10/21/todays-php-security-release-and-how-were-strengthening-php-security/
  3. www.wiz.io/blog/php2shell-php-rce
  4. www.php.net/downloads.php

Articles about Vulnerabilities

Tell us what you need to protect

Describe your environment and what you want to achieve. We will reply by email.

Contact us
[email protected]

AngelForce OÜ · Tallinn, Estonia