Diego Betto
The word PHP cracked and struck by lightning on a dark blue background

Diego Betto · ·9 min di lettura

PHP2Shell: two critical PHP vulnerabilities, update now

PHP2Shell: two critical PHP vulnerabilities (CVE-2026-109618, CVE-2026-109619, CVSS 9.2) allowing unauthenticated RCE. Fixed in PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35: who is exposed, how to update and what to do if you can't.

Condividi:XLinkedInFacebookWhatsApp

On the PHP security team announced an out-of-band security release to fix two critical vulnerabilities, both rated CVSS v4.0 9.2. The community has already nicknamed the issue PHP2Shell, because the real risk is going from an HTTP request to a shell on the server: unauthenticated remote code execution.

⛔ The fix is out: update now

On , slightly ahead of the 14:00 UTC schedule, PHP released 8.5.12, 8.4.27, 8.3.36 and 8.2.35, which fix both vulnerabilities. According to the PHP team there are no complete mitigations: updating is the only safe choice.

Last updated: — fixed versions, official advisory and mitigations.

At a glance

Name PHP2Shell (unofficial nickname)
CVE CVE-2026-109618 (GHSA-w8hw-w9fx-j79w), CVE-2026-109619 (GHSA-6wmf-4rv5-p3rp)
Severity CVSS v4.0 9.2 for both
Impact Unauthenticated remote code execution (RCE)
Who is at risk Every PHP installation serving web requests: PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed
Fixed versions 8.5.12, 8.4.27, 8.3.36, 8.2.35
Unsupported versions No official release; source backports for 7.4, 8.0 and 8.1
Exploitation Early attempts reported by Cloudflare, no confirmed compromise, not in CISA KEV

What the two vulnerabilities are

The official advisory describes two flaws in how PHP manages internal request state:

  • CVE-2026-109618 — RCE via OOM corruption: out-of-memory handling can interrupt PHP while internal request data is only partially initialized. Request cleanup may then treat the invalid data as valid.
  • CVE-2026-109619 — RCE via stale global state: request-owned internal state can survive after its backing memory has been freed. A later request handled by the same worker may use that stale state before normal initialization resets it.

The PHP team considers the risk highest for mod_php, with a demonstrated exploit where an unauthenticated request triggers the attack before any application code runs. In other words: it does not matter how well written your application is, the problem is in the interpreter.

Full technical details are in the GitHub advisories GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp.

Who is exposed

In practice: anyone running an internet-facing PHP application. Not just custom apps, but every popular CMS and e-commerce platform too:

  • WordPress, Joomla, Drupal
  • PrestaShop, Magento, WooCommerce
  • frameworks such as Laravel and Symfony
  • admin panels and tools written in PHP (phpMyAdmin, Roundcube, etc.)

⛔ Updating your CMS does not update PHP

This is the most common misconception: updating WordPress, plugins or themes does not change the PHP version on the server. They are two separate layers. Here the vulnerability lives in the PHP interpreter, so PHP itself needs to be updated (by you or by your hosting provider).

How to update

1. Take an inventory of your PHP versions

A single server often runs several versions (CLI, FPM, separate pools per site, containers). Check them all:

# CLI version
php -v

# all installed versions (Debian/Ubuntu)
ls /usr/bin/php* /usr/sbin/php-fpm* 2>/dev/null

# active FPM services
systemctl list-units --type=service | grep -i php

# PHP version inside running Docker containers
docker ps --format '{{.Names}}' | xargs -I{} sh -c 'echo -n "{}: "; docker exec {} php -r "echo PHP_VERSION, PHP_EOL;" 2>/dev/null || echo "-"'

On a WordPress site, the version actually used by the web server is also shown under Tools → Site Health → Info → Server.

2. Install the fixed version

The target is 8.5.12, 8.4.27, 8.3.36 or 8.2.35, depending on your branch. Linux distributions and LTS vendors received the patches in advance, so packages are already landing.

  • Shared or managed hosting: your provider applies the patch. Ask them when the fixed build will be live and make sure the PHP version selected in your panel (cPanel, Plesk, etc.) is a supported one.

  • Debian/Ubuntu with Ondřej Surý’s repository (packages.sury.org / ppa:ondrej/php): the fixed packages are already available.

    sudo apt update
    sudo apt install --only-upgrade 'php*'
  • Distribution packages (official Debian, Ubuntu, RHEL, etc.): these backport fixes into the version you already run, so a patched package can keep an old-looking version number. Do not judge by php -v alone: check the package changelog for the CVE or compare the exact package version against your distribution’s advisory.

    # Debian/Ubuntu: look for the CVEs in the package changelog
    apt changelog php8.3-common 2>/dev/null | grep -E 'CVE-2026-10961[89]'
  • Remi’s repository (rpms.remirepo.net): per the PHP advisory it will not have updates today. The PHP team recommends switching to distribution packages or building from source so you can patch immediately.

  • Containers: rebuild your images from the new official php:* tags (e.g. php:8.3.36-fpm). Pulling the new image is not enough: you have to recreate the running containers.

  • Windows: binaries are on the php.net downloads page.

3. Restart and verify

Installing the package does not replace processes already running. Both flaws live in long-lived workers, so restart every PHP-FPM pool and Apache if you use mod_php:

# adjust to the versions you actually run
sudo systemctl restart php8.3-fpm php8.4-fpm
sudo systemctl restart apache2   # if you use mod_php

Then verify the version seen by the web server, not the CLI one: a temporary phpinfo() page (delete it right after), Site Health in WordPress, or the X-Powered-By header if it is enabled.

After updating, review your logs for anything suspicious that happened before the patch: crashes and segfaults of PHP workers, unusual requests, new files in web directories.

4. Unsupported versions (8.1, 8.0, 7.x, 5.x)

The PHP project no longer releases updates for 8.1 and older. Given the severity, though, the security team has published minimal source backports for 7.4, 8.0 and 8.1, for those who build from source or have no LTS vendor. Some distributions and extended-support vendors also ship backported fixes.

For anything older than 7.4 there is nothing: migrate, or isolate the application. And whatever happens, use this as the push to move to a supported version (8.2 is in security-only maintenance until the end of 2026, so aim for 8.4 or 8.5).

If you cannot update right away

The PHP advisory is clear: only an update is a complete solution. The following measures reduce exposure to specific attack paths but do not make an installation safe.

Against CVE-2026-109618 (OOM corruption)

  • Set memory_limit=-1, preferably in a way applications cannot override (php_admin_value in the FPM pool or Apache config), and check that no pool, directory or application setting puts a finite limit back. This removes the controllable memory-limit failure, but an underlying allocation failure can still reach the vulnerable path.
  • Set a low post_max_size: 8M for applications without uploads, 32M if uploads are needed, with upload_max_filesize below post_max_size.
  • Enforce limits at the web server or proxy on decoded request size, number of input fields, multipart parts, headers and nesting depth, including for requests without a usable Content-Length.
  • Use a container or cgroup memory limit as an availability boundary, ideally one that kills only the affected worker. Do not use RLIMIT_AS or RLIMIT_DATA instead: they can trigger the vulnerable allocation failure themselves.

With memory_limit=-1 a single request can eat all the RAM: that is exactly why the cgroup limit is part of the package.

Against CVE-2026-109619 (stale global state)

  • Set session.upload_progress.enabled=0 and restart every worker. It must be set in php.ini, in the FPM pool or in server config that applies before request parsing: do not rely on ini_set() or on .user.ini. It blocks the confirmed session paths, not other variants.
  • On PHP 8.5, also set fatal_error_backtraces=0 and restart the workers: it blocks the confirmed fatal-backtrace path only.
  • Remove invalid or deprecated settings applied per request that emit diagnostics before initialization. Hiding diagnostics with error_reporting, display_errors or @ does not help.
  • Fix fatal errors and execution timeouts that an attacker can trigger, including failures in callbacks, upload or import processing, and cleanup.

⚠ session.upload_progress is not a fix

Before the release, session.upload_progress.enabled=0 was being passed around on social media as “the” fix. The PHP advisory confirms it helps, but only against some paths of CVE-2026-109619, and does nothing against CVE-2026-109618. Useful as a stopgap, not as protection.

Network and blast radius

  • For applications that cannot be patched promptly, CERT-EU recommends considering isolating them from public networks. Short of that, put them behind a VPN, IP allowlist or authentication at the edge, and make sure the origin cannot be reached directly around those controls.
  • Cloudflare has pushed emergency WAF rules for the known exploit paths, free plan included. Its CTO calls them “a STOPGAP only”: they only see traffic going through Cloudflare and may miss new variants.
  • Shrink what an attacker would get if the server were compromised: less data stored on the server, fewer privileges for the PHP user, no credentials or cloud roles lying around, and logs shipped somewhere safe.

Timeline

  • — The PHP team announces a security release for two vulnerabilities rated CVSS 9.2.
  • – — CERT-EU publishes advisory 2026-018 with identifiers CVE-2026-109618 and CVE-2026-109619.
  • — GMO Flatt Security clarifies that session.upload_progress.enabled=0 is not a complete fix.
  • — Cloudflare reports early exploitation attempts and releases emergency WAF rules.
  • , just before 14:00 UTC — PHP releases 8.5.12, 8.4.27, 8.3.36 and 8.2.35 together with the security advisory and backports for 7.4, 8.0 and 8.1.

Sources

If you manage WordPress sites, use this moment to go through my WordPress security checklist as well: updating PHP closes this hole, but not the others. And if you need to test compatibility with a newer PHP version before migrating production, WPDockerize lets you set up a local environment in a few minutes.

Last updated:

Condividi:XLinkedInFacebookWhatsApp
Diego Betto

Written by

Diego Betto

Co-Founder & CTO at PAPION. Senior full-stack engineer specializing in React, TypeScript, Node.js, and application security.