On 21 October 2026, the PHP project shipped emergency releases for two critical vulnerabilities that the security community has nicknamed PHP2Shell: CVE-2026-109618 and CVE-2026-109619. Both are rated CVSS v4.0 9.2 (Critical) by PHP’s own CNA, and both allow unauthenticated remote code execution — not against a specific framework, plugin, or piece of careless application code, but against the PHP engine itself. If your server runs PHP to handle web requests, this applies to you regardless of how well-written your application is.
1. Summary
PHP’s own security advisory states it plainly: “all PHP installations serving web requests, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed” are at risk, and the team considers mod_php the highest-risk configuration, with a demonstrated exploit path that triggers before any application code runs. That last detail is the one worth sitting with — this isn’t “upgrade your outdated WordPress plugin.” A freshly built, fully patched, zero-known-vulnerabilities application sitting on top of a vulnerable PHP binary is still exploitable.
Both bugs trace back to how PHP unwinds execution using longjmp() when something goes badly wrong mid-request — an out-of-memory condition for CVE-2026-109618, a fatal error for CVE-2026-109619. In both cases, that abrupt unwind can leave internal engine state corrupted in a way that a later part of request handling (or, in one case, a different, later request entirely) ends up trusting as valid.
The fix: update to PHP 8.5.12, 8.4.27, 8.3.36, or 8.2.35. For anyone still on 8.1 or earlier — versions that no longer receive official updates from the PHP project at all — the security team cut minimal source-only backport branches for 7.4, 8.0, and 8.1 as a stopgap for people building from source without an LTS vendor.
2. First occurrences and remediations
The rough timeline, pieced together from the vendor advisory and the researchers who tracked it in real time:
- ~20 October 2026 — details reportedly leaked ahead of the coordinated disclosure embargo; PHP maintainers signaled emergency patches were imminent.
- 21 October 2026 — php.net published the official security advisory alongside the patched releases (8.5.12, 8.4.27, 8.3.36, 8.2.35) and the two GHSA advisories. This is also the date the 7.4/8.0/8.1 backport branches went up.
- Same day — security firm watchTowr reported observing exploitation attempts against its own honeypot network, and Cloudflare’s CTO publicly noted the company was already seeing early attack traffic.
- 22 October 2026 — Censys published an internet-wide snapshot estimating roughly 36.7 million internet-facing services running potentially affected PHP versions.
- Following days — the FBI and CISA urged organizations to patch immediately. As of the most recent checks at the time of writing, neither CVE had been added to CISA’s Known Exploited Vulnerabilities (KEV) catalog, despite credible reports of in-the-wild exploitation attempts — a reminder that KEV inclusion lags real-world activity and shouldn’t be the bar you wait for.
Warning
The PHP security team has deliberately not published the full technical root cause or a working proof-of-concept, specifically to slow down mass exploitation while patching is still in progress. Several third-party write-ups note the exact vulnerable code path isn’t public. Treat any “detailed exploit walkthrough” you find online with suspicion — the people who actually found this are not the ones publishing step-by-step trigger instructions right now.
From day one, the vendor’s remediation guidance has been unambiguous: there is no supported workaround that fully closes either hole. Updating is, in the advisory’s own words, the only safe choice.
3. Risk / impact
CVE-2026-109618 — RCE via out-of-memory corruption. When PHP hits an out-of-memory condition mid-request, it uses longjmp() to abruptly unwind out of the current execution context. Some of those contexts aren’t prepared for that abrupt exit, so request-internal data can be left only partially initialized. Request cleanup code then runs anyway and treats that incomplete, invalid data as if it were fully formed — the memory corruption that follows is what gets turned into code execution.
CVE-2026-109619 — RCE via stale global state. The same longjmp()-based unwinding happens on a fatal error, but here the problem is worse in a specific way: request-owned internal state can survive the unwind pointing at memory that gets freed once the request ends. Because PHP-FPM workers and mod_php’s persistent Apache children both reuse the same worker process across multiple requests, a completely unrelated, later request handled by that same worker can read or act on that now-freed, stale state before normal per-request initialization resets it. This is effectively a cross-request use-after-free — one request’s crash can corrupt the next request’s execution, from a different client, for a different endpoint.
What makes both of these worse than a typical “critical” CVE:
- No authentication, no valid session, no specific vulnerable endpoint required. The attack surface is PHP’s own error-handling path, which every request can reach.
- Independent of application code quality. This isn’t a SQL injection in your login form or an XSS in a comment field — a perfectly reviewed, well-tested application running on an unpatched PHP binary is still exposed.
- mod_php is rated highest risk by PHP’s own team, specifically because researchers demonstrated an exploit path that completes before a single line of your application’s code executes.
- Scale. Censys’s ~36.7 million figure for potentially affected internet-facing services gives a sense of just how much of the web’s infrastructure this question touches, given how widely PHP is deployed across CMSes, custom applications, and legacy systems alike.
- Active exploitation, reported early. watchTowr’s honeypot telemetry and Cloudflare’s own observations both point to real attack traffic within hours of disclosure — this was never a theoretical bug sitting quietly waiting for a PoC.
Note
It’s worth noting a dissenting voice exists: at least one independent researcher published an analysis disputing that a use-after-free of the kind described exists in released PHP builds. That said, the vendor advisory itself — PHP’s own security team, shipping four simultaneous point releases plus three separate backport branches — is about as strong a primary source as this kind of disclosure gets. Treat the vendor advisory as authoritative and watch for updates if that dispute develops further.
4. Who is impacted, and how to check if you are impacted
Who’s impacted: any server-side PHP deployment that handles web requests — PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP, and LiteSpeed are all named explicitly in the vendor advisory. This cuts across every CMS and framework built on PHP — WordPress, Laravel, Symfony, Drupal, Magento, and fully custom applications are all exposed equally, because the bug sits below the application layer. PHP CLI scripts that never serve network requests are outside the described attack surface. Affected versions effectively span PHP 5 through the latest pre-patch releases of 8.5; PHP 8.1 and earlier no longer receive official updates from the PHP project at all, which matters a lot for the “how do I fix this” step below.
How to check:
-
Check the running version directly.
Terminal window php -vFor a web-facing check without CLI access, a temporary
phpinfo()call (removed immediately after checking — never leave one exposed) or your hosting provider’s control panel will show the active version. -
Compare against the patched list. You want
8.5.12,8.4.27,8.3.36, or8.2.35— or, on a distro package, a version string that matches your Linux distribution’s own backport announcement for these CVEs specifically. Distro-packaged PHP (Debian, Ubuntu, RHEL, Alpine all shipped their own backported builds) won’t necessarily show the upstream php.net version number, so check your distro’s security advisory rather than assuming a mismatch means you’re vulnerable. -
Check containers and pinned versions in CI/CD. Grep your Dockerfiles and
docker-composemanifests for pinned base images likephp:8.3-fpmorphp:8.2-apache, and confirm the actual resolved image digest was built after 21 October 2026. -
Inventory what you don’t directly control. Managed WordPress hosts, vendor appliances, and third-party SaaS-adjacent tools that happen to bundle PHP under the hood are easy to miss. An external attack surface management (EASM) scan, or simply asking every vendor directly “are you patched against CVE-2026-109618 and CVE-2026-109619,” closes a gap that version-checking your own servers won’t.
5. Remediation steps
Patch first — there is no complete workaround. The update path differs slightly by how PHP got onto your system:
# Debian / Ubuntusudo apt update && sudo apt install --only-upgrade php8.3 php8.3-fpmsudo systemctl restart php8.3-fpm
# RHEL / Rocky / AlmaLinuxsudo dnf update php php-fpmsudo systemctl restart php-fpm
# Alpinesudo apk upgrade php83 php83-fpmsudo rc-service php-fpm83 restart# Docker: bump the base image tag to a patched release and rebuildFROM php:8.3.36-fpmFor PHP 8.1 and earlier — officially end-of-life, no upstream binary release will ever ship this fix — you have two real options: pull a backported package from your Linux distribution’s LTS program (Debian LTS, Ubuntu ESM, RHEL’s extended support channel all shipped one), or, if you build from source, apply the vendor’s own minimal CVE-specific backport branch rather than attempting to hand-patch the memory-management code yourself.
Warning
Restart your PHP worker processes after upgrading. A package manager update alone doesn’t help if
the already-running php-fpm workers or Apache children are still executing the old binary in
memory — systemctl restart, not just the package upgrade, is what actually closes the hole.
If you genuinely cannot patch immediately — a change-control window, a legacy system, a vendor appliance you don’t control the update schedule for — reduce exposure while you wait:
- Virtual patching via IPS/WAF, if your security vendor has shipped signatures for this pair. Fortinet, for example, published signatures specifically named for each CVE (
PHP.longjmp.Remote.Code.Executionfor 109618,PHP.stale.global.state.Remote.Code.Executionfor 109619) — check whether your own WAF/IPS vendor has published an equivalent and turn it on. - Reduce direct internet exposure. Put anything that doesn’t strictly need to be public behind a VPN or similar access control until it’s patched. If you can’t adequately restrict access, temporarily taking the service offline is a smaller cost than an active RCE.
- Watch for the practical signs of exploitation, since there’s no official IOC list yet: unexpected PHP-FPM or mod_php worker crashes and restarts, unexplained process execution spawned from the web server’s user account, modified application files you didn’t deploy, or unusual outbound connections from a web server that normally has none.
- Build an honest inventory before anything else. You can’t patch — or even correctly judge your exposure — on a PHP instance you don’t know exists. This is the step teams skip and regret.
Finally: patching closes the vulnerability, but it does not undo anything that happened before you patched. If you have reason to believe a system was exploited during the exposure window — a crash you can’t explain, traffic matching the pattern, a worker restart with no deployment behind it — treat it as a potential compromise, not a routine update. Preserve logs, check for persistence mechanisms, and rotate any credentials the affected service had access to, rather than assuming the patch alone put things back to normal.
References: the official php.net security advisory, GitHub security advisories GHSA-w8hw-w9fx-j79w (CVE-2026-109618) and GHSA-6wmf-4rv5-p3rp (CVE-2026-109619), and the CISA Known Exploited Vulnerabilities catalog, which is worth bookmarking and rechecking as this situation develops further.