Overview
PHP2Shell is the collective name for two vulnerabilities in the PHP engine itself. CVE-2026-109618 is an out-of-memory handling flaw; CVE-2026-109619 is a stale-global-state use-after-free. Both can lead to unauthenticated remote code execution in affected web-serving configurations.
The important sentence for a site owner is not “critical RCE”. It is “this is below your application”. A patched CMS, a current framework and a clean plugin inventory do not fix a vulnerable PHP binary. The update has to reach the runtime that serves the request, and every worker has to be restarted or replaced afterwards.
PHP published fixes in 8.2.35, 8.3.36, 8.4.27 and 8.5.12. PHP 8.1 and older are end-of-life and have no upstream release; the project published minimal source backports for 7.4, 8.0 and 8.1, and distributions or LTS vendors may provide their own. Those are different support paths and need different verification.
Verified identifiers
| Record | Identifier | Vendor score | Fixed in |
|---|---|---|---|
| RCE via OOM corruption | CVE-2026-109618 GHSA-w8hw-w9fx-j79w | CVSS v4.0 9.2 | 8.2.35 · 8.3.36 8.4.27 · 8.5.12 |
| RCE via stale global state | CVE-2026-109619 GHSA-6wmf-4rv5-p3rp | CVSS v4.0 9.2 | 8.2.35 · 8.3.36 8.4.27 · 8.5.12 |
NVD separately publishes CVSS v3.1 9.8 for both records. That is a different scoring system, not a second vendor score. GitHub / PHP Security Team GitHub / PHP Security Team NIST NVD NIST NVD
The two flaws, separately
Treating the pair as one vulnerability loses the conditions that matter for mitigation. Each record below has its own trigger path and its own interim controls.
PHP uses longjmp() to exit on out-of-memory. During some contexts, request memory is
reachable before initialisation completes. If an allocation fails then, cleanup can treat invalid
memory as valid. PHP says multiple OOM strategies can reach remote code execution.
Request-owned state can remain in worker globals after the request heap is freed. A later request handled by the same worker may read that stale state before initialisation resets it. Confirmed paths involve session upload progress and, on PHP 8.5, fatal-error backtraces.
| Record | Primary trigger in the advisory | Confirmed paths / condition | Interim control named by PHP |
|---|---|---|---|
| CVE-2026-109618 | OOM exit while request data is partly initialised; cleanup then handles invalid state. | Demonstrated pre-application execution with Apache mod_php; not every request or deployment is automatically exploitable. | memory_limit=-1, conservative post_max_size, proxy request-shape limits and container memory boundaries. |
| CVE-2026-109619 | Request-owned global state survives after the request heap is freed. | Producer and consumer must reach the same worker; confirmed session upload-progress path; PHP 8.5 fatal-backtrace path. | session.upload_progress.enabled=0; on 8.5 fatal_error_backtraces=0; restart every worker. |
Both records were fixed together, but the trigger and interim controls differ. The table is a reading aid, not a substitute for the advisories; see the separate records.
Affected versions and deployments
PHP says all installations serving web requests are at risk, including PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. It rates Apache mod_php highest risk because a demonstrated attack can run before any application code executes.
| Branch | Affected | Fixed | Note |
|---|---|---|---|
| PHP 8.5 | < 8.5.12 | 8.5.12 | Upstream fix. |
| PHP 8.4 | < 8.4.27 | 8.4.27 | Upstream fix. |
| PHP 8.3 | < 8.3.36 | 8.3.36 | Upstream fix. |
| PHP 8.2 | < 8.2.35 | 8.2.35 | End of life 31 Dec 2026. |
| PHP 8.1 and older | No upstream support | Vendor / source backport | Wiz reports PHP 5.4+; CERT-EU says PHP 5 onward may be affected. |
“Version” is not the same as “deployment”. Container images, appliances and managed hosting may bundle PHP separately from the site’s application code. PHP Group CERT-EU Wiz Research
Patch and vendor backports
The complete fix is a PHP release containing the patch. Distributions and LTS vendors received patches in advance, so the right evidence is the vendor advisory and the package changelog, not only the version string reported by a scanner. A backport can keep an older upstream version number while containing the fix.
After updating, restart every PHP worker, Apache process or container replica. A new binary on disk with an old worker serving traffic is not a patched service. The verification guide gives a concrete sequence and a record you can keep.
Interim controls and their limits
PHP lists interim controls because some organisations cannot update immediately. They reduce specific attack paths. They do not make an installation safe, and PHP says updating is the only complete mitigation.
- OOM corruption: set
memory_limit=-1in a non-overridable place, reducepost_max_size, enforce request-shape limits at the proxy, and use a container or cgroup limit. Do not substituteRLIMIT_ASorRLIMIT_DATA. - Stale global state: set
session.upload_progress.enabled=0and, on PHP 8.5,fatal_error_backtraces=0. Apply before request parsing, not throughini_set()or.user.ini, then restart every worker. - Availability boundary: a WAF or reverse proxy can reduce exposure, but it is not a fix. Cloudflare’s own guidance says patch even with WAF protection.
The mitigation guide explains why
memory_limit=-1 is not a contradiction, how to verify the effective values, and how to
assess compromise without treating a clean log as proof.
Why WAF, backups or non-root are not enough
A WAF sees HTTP requests; these flaws execute inside the PHP worker. A rule can block known patterns and raise the cost of scanning, but a request that does not match reaches PHP. A backup restores availability after an incident; it does not stop code execution. A non-root PHP user limits the attacker’s next steps; it does not turn remote code execution into a non-event.
Each control addresses a different part of the risk. None of them changes the fact that the vulnerable code is in the runtime. That is why the site keeps patch status, exploitation status and KEV status separate instead of collapsing them into one “danger” label.
Common questions
If the database is on another server, are its credentials still at risk?
Short answer: Treat credentials readable by the PHP process as in scope until the runtime is patched and the evidence is reviewed. The advisories describe code execution, not a documented credential-theft campaign.
Conditions and what to check
Network separation does not erase the risk if the application can connect to the database. An attacker who executes code in the PHP process can use the application’s own configuration and environment. Patch first, then review access and authentication logs and rotate credentials according to your incident policy. This site has no evidence of a specific credential-theft campaign; the point is the access that code execution can provide.
Does updating WordPress, a theme or a plugin fix PHP2Shell?
Short answer: No. The flaw is in PHP, below WordPress. Updating those components is normal maintenance, but the PHP runtime must be updated and every worker restarted.
Where the fix belongs
A CMS update changes application files, not the PHP binary or the workers serving requests. If your host manages PHP, ask which branch and package revision is running, when it changed and whether the service was restarted. The WordPress runtime check shows how to separate the two layers without exposing server configuration.
Why does php -v show a different version from the website?
Short answer: The command line and the web server can use different PHP binaries,
php.ini files and process pools. php -v only describes the CLI.
How to compare them safely
Check the serving SAPI and its loaded configuration, not a public phpinfo() page.
The verification guide lists commands for
Apache, PHP-FPM and containers, including a restricted web check when the CLI is not conclusive.
How do I confirm the fix is active after restarting?
Short answer: Verify the process that serves requests, the package or image that contains the fix, and the restart time. A version string alone is not enough.
Evidence to keep
Record the service name, the runtime version and SAPI from the running process, the package changelog or image digest, and the restart or rollout time. A distribution backport may keep an older visible version while containing the fix. The verification guide has the full sequence.
Confirmed, reported, not established
The two CVEs, their affected branches, fixed releases, CVSS v4.0 9.2, the interim controls and the statement that no configuration is a complete fix come from PHP and the GitHub advisories.
Amazon observed an attempt on 17 October. Cloudflare observed attack traffic before disclosure and a sharp rise afterwards. WatchTowr, Hadrian, Patchstack, VulnCheck and others reported attempts with different results. These are attributed reports, not site-wide victim counts.
A confirmed mass compromise of production sites. A precise number of affected organisations. A guarantee that every variant is covered by the interim controls. Whether an appliance or managed host has applied the fix without a vendor advisory. The absence of these facts is not evidence that the risk is lower.
Follow the record
- Timeline — 18 dated events, with evidence category and direct sources.
- News and updates — original summaries of what changed, not republished articles.
- Guides — verification, mitigation and advisory reading.
- Vulnerability library — PHP, Node.js core, npm and historical context.
Latest updates
- Update
- Update
- Update
- Update
Sources
This dossier draws on 48 source records, including the PHP advisories, GitHub security advisories, NVD, CERT-EU, the UK NCSC, Japan’s IPA, CISA’s KEV JSON feed, Cloudflare, Wiz, Bitdefender and reporting by The Record and SecurityWeek. Read scope is shown on the sources page.