Guide · Intermediate · System administrators, platform engineers and technical leads

Check whether your PHP2Shell fix is active

A practical verification sequence for the serving runtime: SAPI, package backports, container images, worker restarts and evidence you can keep.

Published22 October 2026 Last verified23 October 2026, 15:45 UTC Reading time4 min StatusPublished

The short version

The fixed PHP releases are 8.2.35, 8.3.36, 8.4.27 and 8.5.12. A complete fix also requires that the running workers are using the new binary. A package upgrade that leaves old FPM workers or old container replicas serving traffic has not fixed the running service.

Scope of these releases

These releases address CVE-2026-109618 and CVE-2026-109619. Separately, review PHP’s 23 October session-upload advisory if your application changes session.serialize_handler at runtime before session_start(). That issue, CVE-2026-115560, has no fixed release yet; PHP suggests session.upload_progress.enabled=0 until the next regular bugfix release. This does not make the four PHP2Shell fixes invalid.

Confirm four things, in this order:

  1. Which PHP binary and SAPI actually serve requests.
  2. Whether that binary contains the vendor fix, not merely which version string it reports.
  3. Whether every worker was restarted or replaced after the update.
  4. Whether the evidence is recorded somewhere reviewable.

1. Find the serving runtime, not just the CLI

Running php -v on a shell proves what the command-line binary is. It does not prove what Apache, PHP-FPM, a container entrypoint or a control-panel pool loads.

Useful checks:

# Every PHP binary on PATH and common locations
command -v php && php -v
ls -l /usr/bin/php* /usr/local/bin/php* 2>/dev/null

# The SAPI and loaded configuration reported by the CLI
php -i | grep -E 'Server API|Loaded Configuration File|Configuration File \(php.ini\) Path'

# Apache: which PHP module is loaded
apachectl -M 2>/dev/null | grep -i php

# systemd: which FPM units exist and what they run
systemctl list-units --type=service | grep -i php
systemctl status php*-fpm --no-pager 2>/dev/null

For a web-only check, prefer the CLI output above. If you must confirm what the web SAPI loads, do it only on localhost, a restricted management network, or an authenticated location that is already access-controlled. Create the smallest possible diagnostic output, not a full public phpinfo() page, and delete it immediately afterwards. A phpinfo() page exposes configuration, environment values and absolute paths; being public for a few minutes is not access control.

On a container platform, inspect the image digest and the process inside the running container, not only the Dockerfile tag:

docker inspect --format '{{.Image}}' <container>
docker exec <container> php -v
docker exec <container> php -i | grep -E 'Server API|Loaded Configuration'

If the image is built from a base image that bundles PHP, the fix arrives when the base image is rebuilt. Changing application code does not update the runtime.

2. Check the package, not only the version string

Distributions and LTS vendors received patches in advance. A backported package can keep a version string such as 8.3.6-1ubuntu0.1 while containing the fix. That is why a scanner that only compares 8.3.6 with 8.3.36 can produce a false negative or a false positive.

Examples of package-level inspection:

# Debian / Ubuntu
dpkg-query -W -f='${Package} ${Version}\n' 'php*' | sort
apt changelog php8.3-fpm 2>/dev/null | head -80

# RHEL / Fedora / Rocky / Alma
rpm -qa 'php*' | sort
rpm -q --changelog php-fpm | head -80

# Alpine
apk info -a php83-fpm 2>/dev/null | head -40

Look for the CVE identifiers or the GHSA identifiers in the changelog. Debian and Ubuntu security updates usually name the CVE. A vendor advisory page is stronger evidence than a changelog line, because it states which package versions were rebuilt and for which release.

Do not use string comparison to decide that 8.4.9 is newer than 8.4.27. Parse the version components numerically or, better, rely on the package manager’s version ordering. The same rule applies to PHP: the branch and package revision matter.

3. Restart or replace every worker

A new binary on disk is not a patched service. PHP-FPM keeps worker processes alive; Apache with mod_php keeps the module loaded in its processes; containers keep running the old image until they are replaced.

Typical restart commands (verify the unit names on your system):

sudo systemctl restart php8.3-fpm
sudo systemctl restart apache2     # or httpd

For containers, recreate the container from the rebuilt image rather than restarting the old one. For Kubernetes, confirm the rollout completed and the new pods are running, then confirm the image digest in the running pod.

After restarting, repeat the SAPI check from step 1. Record the output with a timestamp and the host or service name. This is the evidence that closes the loop.

4. Handle the branches without an upstream release

PHP 8.1 and older are end-of-life and receive no upstream release. The PHP project published minimal source backports for 7.4, 8.0 and 8.1, and distributions or LTS vendors may provide their own. Those are different support paths:

  • A distribution backport is a vendor-maintained package. Check the vendor’s advisory and package version.
  • A source backport requires you to build and maintain the runtime yourself. Confirm the branch and commit, then treat the build as a custom artefact that needs its own patch process.
  • A third-party repository may not have updates on release day. PHP’s advisory specifically urged users of rpms.remirepo.net to switch to distribution packages or build from source because updates would not be available there on release day.

The strategic answer for an end-of-life branch is to upgrade to a supported branch. An interim backport is a bridge, not a destination.

5. What a version check cannot tell you

A version check cannot tell you whether a host was exploited before the update. It cannot tell you whether a WAF blocked every attempt. It cannot tell you whether a container image contains a second, bundled PHP binary that the application also invokes.

If you need a compromise assessment, use the indicators described in the mitigation guide: unusual multipart requests followed by memory-exhaustion errors, repeated worker crashes, unexpected PHPSESSID values, new files in upload or cache directories, and processes whose parent is a web worker. Treat the absence of those indicators as an absence of evidence, not proof of cleanliness.

A defensible record

For each affected service, keep:

  • the service name and environment;
  • the PHP version and SAPI reported by the running process;
  • the package version and vendor advisory, or the image digest;
  • the restart or rollout time;
  • the person or automation that verified it;
  • any interim controls still in place and their removal date.

That record is more useful than a screenshot of a version string, because it shows the runtime that actually served traffic.

Sources for this page

Revision history

Time (UTC) Recorded change
22 October 2026, 16:45 UTC Initial guide. Commands are examples; confirm package-manager and service names against your own platform.
23 October 2026, 15:45 UTC Adds the scope boundary for the separate CVE-2026-115560 session-upload advisory.
23 October 2026, 15:45 UTC Current published version.

Commands and configuration examples are illustrative. Confirm package names, service names and directive scope against your own platform and PHP version. This guide is not a substitute for the vendor advisory.

Search PHP2Shell Live

Type to search the local index Results stay in your browser
↑ ↓ move Enter open Esc close