
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.
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: — working ASLR-bypass exploit (VulnCheck) and internet exposure data.
⛔ Update 3 November: ASLR bypassed, 2.6 million exposed hosts
VulnCheck has built a working exploit for CVE-2026-109618 that bypasses ASLR on x86-64 with production-like settings (display_errors=off), and has extended it to most pre-patch releases of 8.2, 8.3, 8.4 and 8.5, including default WordPress Docker installs. Its scan counts 2,608,039 internet-facing PHP hosts, practically all vulnerable or likely vulnerable. Details in working exploits and exposure.
⚠ Update 23 October: claimed bypass on 8.5.12
Claims of an RCE on the patched PHP 8.5.12 are circulating. PHP has confirmed a residual issue (CVE-2026-115560) that only hits a specific session configuration, and recommends keeping session.upload_progress.enabled=0 even after updating, until the next bugfix release. Updating is still mandatory. Details in the 8.5.12 bypass claims.
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 and VulnCheck (including a webshell plant attempt), working ASLR-bypass exploit held privately by VulnCheck, no confirmed compromise, not in CISA KEV |
| Exposure | 2,608,039 internet-facing PHP hosts, 99.998% vulnerable or likely vulnerable (VulnCheck) |
| Residual issue | CVE-2026-115560: narrow session-handler case, fix in next bugfix release; keep session.upload_progress.enabled=0 meanwhile |
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.
Working exploits and exposure
Early on, the bugs were considered practical only on systems without ASLR (the memory randomization that hides where code and data live). That is no longer true. VulnCheck has built an exploit for CVE-2026-109618 that works in two stages: first it leaks memory addresses from the vulnerable process, defeating ASLR, then it uses them to get remote code execution. The initial target was PHP 8.4.26 on x86-64 with production-like settings, including display_errors=off: hiding errors does not stop the leak.
As of , according to VulnCheck, the exploit supports (all with mod_php and default settings):
| Branch | Vulnerable releases supported | Architectures |
|---|---|---|
| 8.2 | 8.2.28 – 8.2.34 | x86-64 |
| 8.3 | 8.3.22 – 8.3.35 | x86-64 |
| 8.4 | 8.4.10 – 8.4.26 | x86-64, ARM64 |
| 8.5 | 8.5.10 – 8.5.11 | x86-64, ARM64 |
It also works against WordPress on default Docker installs running those versions. The full tool is only available to customers of VulnCheck’s Initial Access Intelligence service (since ), not publicly. Among the hundreds of “PoC” repositories on GitHub, VulnCheck says only a handful are real exploits; at least one public PoC bypasses ASLR, but only on PHP 8.4.26 x86-64 with the default heap configuration.
This is not theoretical: on VulnCheck’s honeypots already recorded scanning and exploitation attempts, including an attempt to plant a webshell via PHP_SESSION_UPLOAD_PROGRESS. Expect mass exploitation attempts whether or not a given payload works.
How many servers are exposed
VulnCheck’s internet scan gives an idea of the scale (it covers only detectable hosts, so it is a floor, not the total):
- 2,608,039 internet-facing hosts run PHP, about 1.86% of ~140 million IPs answering on the web;
- 2,607,985 (99.998%) are vulnerable or likely vulnerable: 1,474,985 advertise a version below the fixed release of their branch, and 1,133,000 hide the version and are counted as likely vulnerable;
- over 1.1 million (about 42%) run end-of-life branches with no official fix: over 700,000 are on PHP 3.x to 7.3, and around 400,000 on 7.4, 8.0 and 8.1, which only have source backports;
- by application: WordPress 469,699 hosts, Laravel 124,866, Joomla 26,591, Drupal 24,116.
ℹ️ Hiding the version does not protect you
More than a million hosts were counted as “likely vulnerable” precisely because they do not expose the PHP version. Turning off expose_php reduces noise, but attackers do not need to know your version to try: they simply send the payload.
The 8.5.12 bypass claims
Since , several posts on X have claimed a working RCE on PHP 8.5.12, the very release that was supposed to close PHP2Shell, and GitHub has filled up with “PoC” repositories. The quality of the evidence varies a lot: some is technical write-ups, some is a 34-second video with no explanation.
On the PHP team published an informational advisory that confirms a residual issue, tracked as CVE-2026-115560, but limits it to an unusual combination. All of these must be true:
- the configuration sets
session.serialize_handlertophp_binaryorphp_serialize; - the application switches it to
phpat runtime withini_set()before callingsession_start(); session.upload_progressis still enabled.
Reports of the bypass also mention the need for a suitable “gadget” (for example a destructor) in the application code. PHP considers the scenario unlikely, so it will fix it in the next regular bugfix release, not with another emergency release, and will publish the full advisory then.
What to do:
- Do not hold back the update: 8.5.12, 8.4.27, 8.3.36 and 8.2.35 still close the two critical vulnerabilities, which are exploitable with no special conditions.
- Set
session.upload_progress.enabled=0inphp.inior in the FPM pool and restart the workers, even on patched versions, until the next release. If you already did it on 21 October, leave it in place. - Check whether your code or plugins change the serializer at runtime:
grep -rn "serialize_handler" /var/www /etc/php 2>/dev/null
⚠ Do not run PoCs you found on GitHub
During incidents like this one, fake “exploits” that actually contain malware for whoever runs them show up regularly. If you want to verify your exposure, use a test environment and code you have read line by line, never production.
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 -valone: 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_valuein 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:8Mfor applications without uploads,32Mif uploads are needed, withupload_max_filesizebelowpost_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_ASorRLIMIT_DATAinstead: 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=0and restart every worker. It must be set inphp.ini, in the FPM pool or in server config that applies before request parsing: do not rely onini_set()or on.user.ini. It blocks the confirmed session paths, not other variants. - On PHP 8.5, also set
fatal_error_backtraces=0and 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_errorsor@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. Since , PHP also recommends keeping it on updated versions, because of CVE-2026-115560.
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=0is 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.
- — Posts on X and PoCs on GitHub claim an RCE bypass on PHP 8.5.12.
- — VulnCheck makes a weaponized ASLR-bypass exploit for CVE-2026-109618 available to its Initial Access Intelligence customers.
- — PHP publishes an informational advisory on CVE-2026-115560 and recommends
session.upload_progress.enabled=0until the next bugfix release. - — VulnCheck extends its exploit to 8.2, 8.3, 8.4 and 8.5 (x86-64, and ARM64 for 8.4/8.5) and to default WordPress Docker installs, and publishes exposure data: 2.6 million internet-facing PHP hosts.
Sources
- VulnCheck — PHP2Shell Vulnerabilities: CVE-2026-109618 and CVE-2026-109619
- Red Hot Cyber — Milioni di server PHP nel mirino (Italian)
- PHP.net — Informational advisory: Session upload progress
- PHP.net — Security advisory: Critical remote code execution vulnerabilities, update now
- GitHub — GHSA-w8hw-w9fx-j79w (CVE-2026-109618)
- GitHub — GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- PHP.net — PHP 8 ChangeLog
- PHP.net — Upcoming Critical PHP Security Release
- CERT-EU — Security Advisory 2026-018: Critical Vulnerabilities in PHP
- The Hacker News — PHP2Shell: what actually protects you
- Cloudflare — Emergency WAF release
- Serverplan — PHP2Shell: due vulnerabilità critiche in PHP (Italian)
- PHP.net — Supported Versions
Updating PHP is not the end of the story for WordPress either: a separate bug in the Brotli library, nicknamed HTTP2Shell, has been exploited in the lab against WordPress running the patched PHP 8.3.36. I covered it in HTTP2Shell: the Brotli vulnerability that also hits WordPress.
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.

Co-Founder & CTO at PAPION. Senior software engineer with AI skills: React, TypeScript, Node.js, LLM and AI agent integration, and application security.