Aller au contenu principal

Guides / Corriger : guides pour les erreurs que l'on rencontre vraiment

PHP2Shell (CVE-2026-109618): check PHP version and patch

PHP2Shell (CVE-2026-109618 and CVE-2026-109619) is a pair of critical PHP flaws that let an unauthenticated attacker run code on your server, and the fix is to update to PHP 8.5.12, 8.4.27, 8.3.36 or 8.2.35 (or your distribution's patched build) and restart PHP. That is the position as of 27 October 2026: CISA lists both bugs as known exploited, and PHP 8.1 and older get no official release.

What the two bugs are

PHP2Shell is a nickname, not a product. It covers two separate vulnerabilities in the PHP engine itself, so the framework, CMS or plugin on top makes no difference. Neither bug needs a login on the target site.

  • CVE-2026-109618 is a memory corruption bug. If PHP runs out of memory while it is still building the data for a request, its error cleanup trusts structures that were never finished. A crafted multipart request, the encoding browsers use for forms and file uploads, can steer that into code execution.
  • CVE-2026-109619 is a stale-state bug. Global state left over from one request can still be in use when a later request is served, which can also end in code execution. It can leak memory belonging to other requests as well, such as passwords, tokens, API keys and certificates, without any code running.

The PHP security team scored both 9.2 on the CVSS v4.0 scale. The changelog lists the fix as "Avoid state corruption via bailout" and cites the advisories GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp.

CISA added both CVEs to its Known Exploited Vulnerabilities catalog on 26 October 2026. Others report exploitation, and the claims below are theirs:

  • Amazon Threat Intelligence says it saw an attempt against its MadPot honeypots on 17 October and now calls php2shell "broadly exploited in the wild".
  • The researcher Adel Karimi documented successful exploitation of three honeypot servers running PHP 8.4.26, along the CVE-2026-109618 path, in a setup Tenable describes as favorable to the exploit.
  • watchTowr reproduced both bugs and reports attempts as well.

These are the reporters' findings, not ours, and the details are still developing. Treat any server on an unfixed version as exposed.

Who is affected

Every PHP release below the fixed release of its branch is affected. The flaws sit in PHP itself, so every way of serving web requests is in scope: Apache with mod_php, PHP-FPM, php-cgi, FrankenPHP and LiteSpeed. PHP is also embedded in other software, so a forgotten staging box or a vendor product can be the one left behind.

BranchFixed inOfficial security support
8.58.5.12until 31 Dec 2029
8.48.4.27until 31 Dec 2028
8.38.3.36until 31 Dec 2027
8.28.2.35until 31 Dec 2026
8.1no official releaseended 31 Dec 2025
8.0no official releaseended 26 Nov 2023
7.4no official releaseended 28 Nov 2022

The dates come from php.net's supported-versions and end-of-life pages, read on 27 October 2026.

For branches that no longer receive releases, the PHP security team published source backports only, meant for people who compile PHP themselves. The practical options are to move to a supported branch or to run a build from a distribution or extended-support vendor that still maintains your branch. CISA's alert goes further: for systems that cannot be updated, or that run 8.1 or earlier, it advises taking them offline until they are fixed.

Distributions complicate the version number. Debian, Ubuntu, the RHEL family and CloudLinux usually fix a flaw inside their own package numbering, so a package that still reads 8.3.6 can contain the fix.

How to check which PHP version a site runs

From outside, without logging in

The PHP version checker makes one request to the address you enter and reads the X-Powered-By and Server response headers, which usually carry a string like PHP/8.3.6. If neither header names a version, it looks for signs that the site runs PHP at all, such as session cookie names or a WordPress or Drupal footprint. It then compares what it found with the four fixed releases. Its answer is one of: below the fix, on an end-of-life branch, current, a distribution build that needs a closer look, PHP present with the version hidden, or no sign of PHP.

Know what an outside check cannot tell you:

  • A hidden version is not a safe version. Many hardening guides set expose_php = Off, which removes the X-Powered-By header. The server is just as vulnerable without it.
  • A patched package can show an old number. Distribution backports keep the upstream version string, so the header can say 8.3.6 on a fixed machine.
  • It sees one URL, once, from one place. A CDN may serve cached headers, a load balancer may pick a different node than a visitor gets, and subdomains can run different PHP versions.
  • It does not prove exploitability. The check is passive and sends no exploit payloads, and it does not look for signs of compromise.

On the server

The command line binary and the web-facing PHP are often different installs, so check the one that serves traffic:

php -v                      # the CLI binary
php-fpm8.3 -v               # the FPM binary (php-fpm -v on RHEL-family systems)
php -r 'echo PHP_VERSION, PHP_EOL;'

For the version the web server really uses, put a one-line file in the web root, request it in a browser, and delete it straight after, because it exposes your configuration:

<?php phpinfo();

The page shows the PHP version and the "Server API" row that answered. To read the installed package or container instead:

dpkg -s php8.3-cli | grep Version          # Debian, Ubuntu
rpm -q php-cli php-fpm                     # RHEL, AlmaLinux, Rocky
apk info -v | grep '^php'                  # Alpine
docker image inspect myapp:latest --format '{{.Config.Env}}' | tr ' ' '\n' | grep PHP_VERSION

The last line needs an image based on the official php image, which sets PHP_VERSION; otherwise run docker exec with php -v.

Reading a distribution suffix

A string such as 8.3.6-0ubuntu0.24.04.x has two parts. 8.3.6 is the upstream PHP version the package started from. Everything after the dash is the distribution's own revision for Ubuntu 24.04. Ubuntu keeps the branch version and applies fixes on top, so a patched build usually raises the last number and leaves 8.3.6 alone. Debian uses forms like 8.2.x-1~deb12uN, RHEL-family packages carry .el9, and Alpine adds -rN.

To learn whether a given build contains the fix, ask the package, not the number:

apt changelog php8.3-fpm | grep -E 'CVE-2026-(109618|109619)'
rpm -q --changelog php-fpm | grep -E 'CVE-2026-(109618|109619)'

A match means the changelog names the fix. No match is not proof of exposure, because some vendors rebase to a PHP.net release and list no CVE ids. Then the distribution's tracker settles it: the Debian security tracker, the Ubuntu CVE page or the Red Hat CVE page list the fixed package version per release.

How to patch

Installing a new package does not change a process that is already running. PHP-FPM pools, Apache workers and anything started before the update keep the old code in memory until restarted, so each section below ends with a restart and a check. Long-lived processes need the same treatment: queue workers (for Laravel, php artisan queue:restart asks workers to exit after their current job) and application servers such as Octane, Swoole or RoadRunner. FrankenPHP bundles its own PHP, so update FrankenPHP itself rather than a system package.

One dated caution. As of 26 October 2026, Tenable's FAQ reports that PHP 8.4.27 and 8.5.12 can crash PHP processes in some applications that use SimpleXML (php-src issue #24488, caused by a separate garbage-collector fix). A fix is merged but not released. Try the update on a staging copy before a fleet-wide rollout and watch for the next bugfix release. That is a reason to stage the rollout, not to skip it.

Debian and Ubuntu

sudo apt update
sudo apt install --only-upgrade 'php8.3*'
sudo systemctl restart php8.3-fpm apache2
php -v

Replace 8.3 with the branch you run. With Apache and mod_php the Apache restart is what matters; with PHP-FPM behind Apache or nginx it is the FPM service.

RHEL, AlmaLinux and Rocky Linux

sudo dnf update 'php*'
sudo systemctl restart php-fpm httpd
rpm -q php-fpm

If PHP comes from an AppStream module or from Remi's repository, the same command applies; dnf module list --enabled php shows which stream you are on. Restart whichever of the two services you actually use, then confirm the version and the changelog entry.

Alpine

apk update
apk upgrade
rc-service php-fpm83 restart
php -v

Alpine names packages by branch, so PHP 8.3 is php83 and the service is php-fpm83. Inside a container, an Alpine package upgrade is not the fix; build a new image instead, as in the next section.

Docker

Pulling a newer tag changes nothing for containers that are already running from the old image. The fix is a rebuilt image plus recreated containers:

docker pull php:8.3-fpm
docker compose build --pull
docker compose up -d --force-recreate
docker compose exec app php -v

Replace app with your service name. If your Dockerfile pins a patch tag such as php:8.3.x-fpm or an image digest, change the pin to a fixed release.

cPanel and WHM (EasyApache ea-php)

dnf upgrade 'ea-php*'        # yum update 'ea-php*' on older systems
/scripts/restartsrv_apache_php_fpm
/scripts/restartsrv_httpd

cPanel serves PHP from EasyApache 4 packages named ea-php plus the branch (for example ea-php83), updated from cPanel's repository and independent of the operating system's php packages. WHM's MultiPHP Manager shows which version each domain uses. CloudLinux published a table of patched ea-php builds, so read it before assuming your update channel has the fix.

CloudLinux alt-php

dnf clean all && dnf upgrade 'alt-php*'     # yum on CloudLinux 7
/scripts/restartsrv_apache_php_fpm
rpm -q --changelog alt-php83-common | grep -E 'CVE-2026-(109618|109619)'

The versions that customers pick in CloudLinux's PHP Selector are the separate alt-php packages, so updating the system PHP does not touch them.

Plesk

Plesk can run PHP from the operating system's packages or from builds it ships itself. Install the second kind from the Updates page under Tools & Settings, and use the commands above for the first. Under each domain's PHP Settings, check which version it uses, because a domain pinned to an old branch stays exposed. Restart the PHP-FPM services from Tools & Settings, Services Management, then check one site per PHP version in use.

Managed WordPress and shared hosting

You usually cannot update PHP yourself here: the control panel offers a version selector, and the host decides which build sits behind each option. In WordPress, Tools, Site Health, Info, Server shows the PHP version your site runs. Then open a ticket with two questions: which PHP build serves my site, and does it include the fixes for CVE-2026-109618 and CVE-2026-109619? "We run the latest PHP" is not an answer; ask for the build number or the changelog entry. This guide names no host as patched, because the state differs by plan and changes daily. Once the host confirms a restart, run the checker on your own address.

Windows and IIS

Download the build for your branch from windows.php.net (IIS with FastCGI uses the non-thread-safe x64 zip), unpack it next to the old folder, and copy over your php.ini, keeping the extension DLLs from the same download so they match the binary. Point the FastCGI handler mapping at the new php-cgi.exe if the path changed.

php -v
iisreset

Recycling the application pool is the narrower option than iisreset.

If you cannot patch today

PHP's advisory, as reported by Tenable, lists settings that reduce exposure. PHP says they do not make an installation safe and that updating is the only complete fix. Restart every PHP worker after changing any of them, or nothing changes.

  • CVE-2026-109619: turn off upload progress tracking. Set session.upload_progress.enabled = 0, and on PHP 8.5 also fatal_error_backtraces = 0. Codeable suggests the first setting too as an interim step and notes that a typical WordPress site does not use the feature.
  • CVE-2026-109618: remove the memory limit. The advisory names memory_limit = -1, set where applications cannot override it. This is a last resort, because it takes away the memory ceiling: a runaway script or a traffic spike can now use all the RAM on the machine. Revert it once you have updated.
; php.ini
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0

; FPM pool file (php_admin_value cannot be overridden by applications)
php_admin_value[memory_limit] = -1

Put these in php.ini or the pool configuration, not in application code. With mod_php, use php_admin_value in the virtual host.

  • Put a web application firewall in front. CISA says a WAF may offer some protection until you patch. Cloudflare says it has deployed WAF rules for the known exploit vectors, available on all plans including free. Treat that as a stop-gap: it covers only traffic that passes through Cloudflare, and rules match request shapes that are already known. CloudLinux calls its own Imunify360 rules an interim mitigation, not a fix.
  • Shrink who can reach the application. An IP allowlist or authentication in front of the site cuts the number of people who can send the crafted request. If you take the site down for the update, answer with a 503 with a Retry-After header so visitors and crawlers know it is temporary.

Put a date on the real update. An interim setting that nobody revisits becomes the permanent state.

Verify and keep watching

After the restart, run the PHP version checker against the site again and compare it with php -v. If it still shows the old number, the usual causes are a process that was not restarted, a second PHP version assigned to that virtual host, or a CDN serving cached headers. Purge the cache and test an address that bypasses it.

Patching does not tell you whether someone used the hole first. CISA suggests watching for PHP workers that start shells or download tools, and added a SIGMA detection rule to its alert for log review. The version checker reads a version and nothing else.

Versions drift back. A rollback, a rebuilt image from an old base or a new server in the pool can reintroduce an unfixed PHP months later, and a scheduled check on the version header notices. In an HTTP check in assertion mode, add a rule on the exact string you saw after patching. For a site on upstream builds that exposes its header, with 8.3.36 as the example:

header("X-Powered-By") matches "^PHP/8\\.3\\.(3[6-9]|[4-9][0-9])"

That passes on 8.3.36 and later patch releases, and fails if the header shows an older number or disappears. It also fails when you move to another branch, which is the cue to update the rule. The rule language is in the assertion mode reference. On distribution builds, assert the full string you saw after patching, suffix included, so a different package shows up as a failure. If you hide X-Powered-By on purpose, do not switch it on for monitoring: serve PHP_VERSION from a private endpoint behind authentication and assert on its body.

HostTracker runs such checks on a schedule from locations around the world through its availability monitoring. Re-checking PHP after every update also fits the routine in the website maintenance checklist.

Sources

Release facts come from the PHP 8 changelog, and branch dates from php.net's supported versions and end-of-life pages. Government and vendor analysis: the CISA alert, the Tenable FAQ, Help Net Security, the watchTowr FAQ, the CloudLinux advisory and Codeable's write-up. HostTracker is not affiliated with the PHP project or any vendor named here.

Vérifier maintenant

Lancez la vérification gratuite sur votre propre site, sans créer de compte.

PHP version checker

Surveiller en permanence

Soyez alerté dès que quelque chose casse : HostTracker vérifie depuis plus de 300 emplacements et vous prévient par e-mail, SMS, Slack, Telegram et plus encore.

Fonctionnalités HostTracker

Plus dans cette section: Corriger : guides pour les erreurs que l'on rencontre vraiment