PHP2Shell: What Happened, Who Is at Risk, and What PHP Administrators Should Do
On October 21, 2026, PHP shipped emergency fixes for two serious security flaws: CVE-2026-109618 and CVE-2026-109619. Both are rated Critical at 9.2 under CVSS v4.0, and both can lead to remote code execution without authentication under the right conditions. The fixed releases are PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12.
The vulnerabilities have become known as PHP2Shell. The name suggests a WordPress exploit or a single request that works everywhere, which overstates what is known.
The root problem is in the PHP runtime. WordPress, Drupal and Joomla were among the applications researchers used to demonstrate the consequences.
PHP says the risk applies to every installation that serves web requests: PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. It rates mod_php highest, because one demonstrated exploit runs before any application code executes.
The Wiz timeline is real, but it is only part of the story
Wiz Research says it found its first relevant PHP vulnerability on August 19, achieved unauthenticated RCE against Drupal on August 20 and WordPress on August 21, and sent its findings to PHP on August 24. PHP acknowledged Wiz’s reports on September 21, and the fixes were published on October 21.
Read on its own, that timeline suggests the story started on August 19, but the underlying problem was known well before then. PHP also says its fix work was already under way from August 11. The September 21 acknowledgement marks when Wiz’s reports were acknowledged, not when PHP started fixing the problem.
PHP’s own account says the underlying out-of-memory safety problem was noticed during follow-up work to a 2024 security audit. At the time, developers suspected exploitation might be possible, but they had no practical RCE vector, and a suitable fix proved difficult to find.
Then the situation moved quickly. Calif began auditing PHP on July 30 through OpenAI’s Patch the Planet program. Calif says it reached a WordPress RCE in early August, and the PHP Foundation says Calif formally reported an unconditional RCE with the same root cause on August 11. OpenAI independently reported that root cause the same day. From then on, PHP treated the problem as its top security priority. Wiz’s work was an independent discovery, but it was not the first time anyone had reached the underlying flaw.
This was not one researcher finding one isolated bug. Several teams converged on the same weakness: how PHP behaves when request processing is interrupted at the wrong moment.
What goes wrong inside PHP
CVE-2026-109618 involves PHP’s handling of out-of-memory conditions. Historically, PHP could jump out of the current execution path when an allocation failed. If that happened while request structures were only partly initialized, cleanup could treat unfinished memory as valid. Carefully constructed multipart HTTP requests provide several ways to create the required memory pressure.
Apache mod_php is the highest-risk confirmed configuration. PHP has validated an attack in which the problem occurs during request startup, before the application runs. WordPress or Drupal never gets the chance to reject the malicious input.
CVE-2026-109619 crosses request boundaries. PHP workers persist between requests so they can handle many efficiently, and some internal state persists with them. If a fatal error leaves a global pointer referring to memory owned by the previous request, that memory can be freed while the pointer survives. A later request handled by the same worker can then use the stale state before normal initialization replaces it. With controlled heap manipulation, that use-after-free can become code execution.
Wiz demonstrated several forms of these problems, including a memory-disclosure primitive, an upload-progress use-after-free, a fatal-error-backtrace use-after-free and an OPcache type confusion. It combined them into unauthenticated RCE demonstrations against WordPress, Drupal and Joomla.
The two CVEs at a glance
| Issue | Root mechanism | Authentication | Demonstrated environments | Fixed in |
|---|---|---|---|---|
| CVE-2026-109618 (out-of-memory corruption) | PHP’s historical longjmp() escape from an out-of-memory condition can run teardown code over partly initialized request data. | None (CVSS PR:N/UI:N). | Full HTTP RCE on mod_php. FPM is susceptible after normal startup, though the advisory says no complete FPM exploit was shown for this specific path. | 8.2.35, 8.3.36, 8.4.27, 8.5.12 |
| CVE-2026-109619 (stale global state) | Request-owned data stays reachable from persistent worker globals after its heap is freed, so a later request on the same worker can use a dangling pointer. | None. Needs a producer request, a later consumer and same-worker sequencing. | Fatal-backtrace exploitation on mod_php. A session-upload-progress path validated on FPM when the application supplies a suitable fatal error. | Same versions as above |
FrankenPHP is considered likely exposed based on source analysis. LSAPI exposure depends on the application, and PHP has not validated it.
The advisories give exact version ranges only for maintained branches. Wiz reports a vulnerable path back to PHP 5.4, and Calif reports its earliest affected path at 5.4.0. The emergency backports for 7.4, 8.0 and 8.1 are not ordinary supported releases, and older versions with no listed package should not be assumed safe.
The request that triggers the out-of-memory case does not need to be large. PHP’s advisory describes several ways a small request can use much more heap than its size suggests: a request without a useful declared length can skip the early post_max_size check, multipart parts can carry many headers, and empty file fields can create metadata without counting toward max_file_uploads. Deeply nested field names multiply those allocations.
Wiz also reported a memory-disclosure primitive. Stale session state can cause memory from a reclaimed PHP worker to be URL-encoded into a PHPSESSID response cookie. A worker can hold data from unrelated requests, so this can leak passwords, tokens or API keys. Wiz demonstrated it without the application calling session_start().
The earlier history
The out-of-memory behaviour is older than this research. A 2020 PHP bug report, #78876, described an out-of-memory condition that failed to clean up temporary files. Calif later linked that behaviour to the 2026 findings.
In 2024, during a security audit of PHP run by Quarkslab and OSTIF and funded through Germany’s Sovereign Tech Agency, a PHP core developer found the first signs of the problem later tracked as CVE-2026-109618. Developers suspected it could be exploited, but no concrete route to code execution was known, and the competing fixes they considered were never shipped.
The next development came from outside the core team. OpenAI’s Patch the Planet program supplied the GPT-5.6 Sol work that Calif used in its July audit, and on September 18 the PHP Foundation said OpenAI had supplied further attack vectors for the same root causes.
This is not a WordPress or Drupal patching problem
Wiz’s WordPress and Drupal demonstrations do not mean PHP2Shell is a defect in WordPress or Drupal core. The vulnerable code sits underneath them. A clean CMS running on vulnerable PHP remains exposed, and updating CMS plugins while leaving PHP untouched does not fix these CVEs. Wiz also says that even a plain PHP “Hello World” deployment can expose relevant engine-level behavior.
PHP-FPM is not immune either. FPM kills a child when request startup itself fails, which blocks the simplest application-independent CVE-2026-109618 sequence. That is why some claim FPM is unaffected, and that claim is wrong. PHP’s advisory validates a CVE-2026-109619 sequence on FPM in which normal startup succeeds, the application triggers the fatal error, and a later upload-progress request on the same worker uses stale state. Calif reports RCE on FPM through application-level paths too.
Wiz’s WordPress demonstration used the upload-progress state from CVE-2026-109618 and an OPcache type confusion grouped under CVE-2026-109619. Calif reports pre-authentication RCE against WordPress 7.1.2 and Drupal 11.4.3, and separately built its first WordPress RCE in early August. The WordPress and Drupal security pages list no PHP2Shell core advisory, which fits a bug that sits in the runtime underneath them.
The disclosure did not stay fully coordinated
The planned release date was October 22. On October 20, the CVE identifiers began circulating publicly with an incorrect CVSS score of 10.0. PHP announced the emergency update and moved the release forward a day.
A Calif exploit-demonstration video then appeared by accident and was copied before it was removed. At 07:41 UTC on October 21, Chainguard published fixed container images before the coordinated release, so the patch could be studied before most users could install it. PHP says it had not identified a working public exploit at the moment of official disclosure, but exploit development was clearly accelerating.
Attackers were watching too. Cloudflare has observed attack traffic and attempts to modify payloads to get past its protections. Its WAF rules were active before disclosure, and it later added request-and-response correlation because request signatures alone cannot identify every attack path. Cloudflare still tells customers to install the PHP update. watchTowr reports exploitation attempts against both CVEs. AWS says retrospective honeypot analysis found a PHP2Shell exploitation attempt on October 17, four days before public disclosure. That is evidence of earlier activity, not proof that vulnerable servers were already compromised.
Exploitation status
- Did Wiz reproduce RCE? Yes. Wiz says its exploit achieved unauthenticated code execution against WordPress, Drupal and Joomla containers, and separately demonstrated the memory disclosure.
- Did another team reproduce it? Yes. Calif documents its own RCE work, and watchTowr says it independently reproduced both CVEs.
- Was exploit code public at release? Partly. Calif’s demonstration video was published by mistake on October 20 and copies circulated. PHP says it had not identified a functional exploit at official disclosure, which is a statement about that moment only. By October 23, Sophos reported a published proof of concept.
- Is attack traffic occurring? Yes. Cloudflare reports attackers changing payloads to evade its WAF, and watchTowr reports exploitation attempts against both CVEs.
- Is widespread compromise confirmed? No. The sources establish exploitability and active attempts, not how many servers were compromised.
What we cannot yet confirm: how the October 17 attempt’s operator got their knowledge of the bug, and whether it succeeded.
What administrators should do
For the two original CVEs, the fix is to update PHP to 8.2.35, 8.3.36, 8.4.27 or 8.5.12, whichever matches your branch. PHP 8.1 and older no longer receive normal upstream security releases, although minimal source backports were published for 7.4, 8.0 and 8.1. A supported branch is the better long-term target.
- Inventory every PHP runtime that serves web requests. That includes hosts that present themselves as WordPress, Drupal or a control panel, plus containers and forgotten development systems. Do not search only for old PHP versions, because supported versions were affected too. Linux distributions and LTS vendors received the patches in advance, so check your vendor’s packages first. Windows source and binaries are on PHP’s downloads page.
- Upgrade the runtime to a fixed version on your branch, then restart all PHP workers, including FPM children, Apache processes and container replicas.
- Check CVE-2026-115560. PHP’s October 23 notice covers a separate, uncommon RCE that the October 21 releases do not fix. It needs a
session.serialize_handlerofphp_binaryorphp_serialize, a switch tophpbeforesession_start(), and session upload progress still enabled. Setsession.upload_progress.enabled=0where that could apply until the follow-on release. Put the setting inphp.ini, an FPM pool or server configuration that applies before request parsing.ini_set()and FPM.user.inifiles are not enough. - If you cannot patch immediately, apply every relevant temporary mitigation: enforce
memory_limit=-1, keeppost_max_sizeconservative, cap multipart size, part, header and nesting counts at the frontend, block multipart uploads where they are not needed, and disable session upload progress. On PHP 8.5, also setfatal_error_backtraces=0. For CVE-2026-109619 on PHP 8.5, also setfatal_error_backtraces=0, and remove per-request settings that emit diagnostics before initialization. Hiding errors witherror_reporting,display_errorsor@does not help. Fix attacker-reachable fatal errors and timeouts in upload, import and cleanup code. Do not useRLIMIT_ASorRLIMIT_DATAto limit memory, because they can trigger the unsafe allocation-failure path. A container or cgroup limit can serve as an availability boundary. PHP says all these measures are incomplete, and a WAF is defense in depth, not a fix. - Hunt for compromise, before and after patching. Look for unusual multipart traffic followed by memory-limit errors or repeated HTTP 500s, oversized or malformed
PHPSESSIDcookies, unexplained PHP worker crashes, and web or PHP processes starting shells, interpreters or download tools. Extend the review back to at least October 17 where logs allow. - If compromise is plausible, preserve evidence, look for web shells, unexpected scheduled jobs, new accounts and outbound connections, and rotate any secret that could have been exposed. The memory-disclosure primitive can leak live worker data, and RCE runs with the web worker’s privileges.
- If a system cannot be patched or mitigated, take it offline rather than leaving it exposed to the Internet. That is the UK NCSC’s current advice.
How it was found
The underlying conditions survived for years, partly because turning them into reliable exploits required slow, repetitive memory-corruption work. Wiz says it used Wiz Atlas, Gemini 4 Argon and GPT-5.6 Sol across discovery, exploitation and patch validation, and that Gemini 4 Argon made a particularly significant contribution. Calif worked with GPT-5.6 Sol through OpenAI’s Patch the Planet program.
AI lowered the cost of that work, and it also helped defenders investigate variants and test fixes. Statements about how much human research time the models saved are the researchers’ own assessments, not independently measured benchmarks.
Teams should not assume a bug is too tangled to exploit, and maintainers have better tools for examining old, tangled code. Those are the researchers’ own accounts, and they have not been independently benchmarked. For PHP operators the immediate steps are unchanged: find the runtime, patch it, restart it, and check what happened before you patched. The CMS on top of PHP is not the boundary that matters here.
Sources
- Wiz Research: PHP2Shell
- PHP security archive, October 2026
- GHSA-w8hw-w9fx-j79w (CVE-2026-109618)
- GHSA-6wmf-4rv5-p3rp (CVE-2026-109619)
- PHP Foundation: today’s PHP security release
- Calif Research: PHP2Shell
- Cloudflare: PHP vulnerabilities and WAF protection
- AWS Security Blog: PHP2Shell exploitation prior to public disclosure
- UK NCSC: critical security issue affecting PHP
- watchTowr: PHP2Shell
- Sophos: proof-of-concept exploit released