PHP2Shell: two critical PHP RCE flaws, and how to cut your risk while you patch
On 21 October 2026 the PHP project disclosed two critical vulnerabilities — nicknamed PHP2Shell — in the PHP runtime itself. Not a plugin, not a theme, not someone's untidy application code: the engine that executes all of it. Both are rated CVSS 9.2, and in the worst case an unauthenticated attacker reaches remote code execution on the server.
PHP has quietly run a large slice of the web for more than thirty years. When the flaw is in the runtime rather than the app, "is my code secure?" stops being the right question — the exposure follows the interpreter, across WordPress, Drupal, Joomla, Magento and every bespoke PHP service you forgot you were running. This post is the practical part: what PHP2Shell is, why "just patch it" is harder than it sounds, and exactly what to do to shrink your risk in the window before the fix is everywhere. It's our rewrite of FortiGuard Labs' advisory, through the lens of how we actually run this for customers.
The one-line version
Patch the PHP runtime (not just the CMS) to 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 or your vendor's backport, restart the workers, and verify the running process. Until that's done and confirmed everywhere, put IPS/WAF virtual patching in front of every exposed PHP service and assume you might already have been hit.
What PHP2Shell actually is
Two CVEs sit under the PHP2Shell name, both memory-safety defects in the runtime:
| CVE | What goes wrong | Rating |
|---|---|---|
| CVE-2026-109618 | Memory corruption during out-of-memory error handling — the cleanup routine operates on partially initialised request data. | CVSS 9.2 |
| CVE-2026-109619 | Stale global state — a PHP worker reaches references to memory freed after an earlier request. | CVSS 9.2 |
Both can lead to remote code execution, and the code runs with the privileges of the PHP process — enough to read application secrets, database credentials, API keys and anything that process can reach. The detail that should worry you most: researchers showed an attack path that executes before your application code runs. That means your login page, your API keys, your WAF-at-the-app-layer — the authentication you rely on may never get a vote. The PHP project flags mod_php (Apache's embedded module) as the highest-risk deployment.
A third, separate issue — CVE-2026-115560, disclosed 23 October and dependent on configuration — relates to session upload progress. It's not part of the original pair, but it's in the same family and worth handling in the same sweep.
Why the deployment model matters
The runtime ships many ways — PHP-FPM, Apache mod_php, php-cgi, FrankenPHP, LiteSpeed — and your risk and your fix both depend on which one you run. A single server often runs more than one. You cannot remediate what you haven't inventoried by deployment model, not just by "we run PHP".
Why "just patch it" is the hard part
On paper this is simple: upstream fixes both CVEs in PHP 8.2.35, 8.3.36, 8.4.27 and 8.5.12. In practice, four things turn a one-line instruction into a programme of work:
- Version numbers lie. Distributions and hosting vendors frequently backport security fixes without bumping the version string. A box that reports "8.3.21" may be patched — or may not be. You have to confirm the actual package build, not read the banner.
- The patch isn't live until the workers restart. Updating the package does not fix a running service. Long-lived PHP-FPM workers and containers keep executing the old code in memory until they're recycled or replaced. A "yum update" in your change log is not proof.
- PHP hides everywhere. It's inside containers, inside managed hosting you don't administer, bundled into third-party appliances and applications you never think of as "a PHP server". Each needs an owner and a remediation plan — including the providers you have to chase.
- Old branches are on their own. PHP 8.1 and earlier no longer get standard security releases. The project has published limited source backports for some older branches, but applying them needs build expertise most teams don't have on tap. If you're on EOL PHP, the real fix is getting off it.
On the scale of the problem: Censys's 22 October snapshot counted roughly 36.7 million internet-facing services reporting potentially affected PHP versions. Treat that as a direction-of-travel number, not gospel — as Censys itself notes, those are service counts, not unique servers or organisations, and version-based detection will include systems already fixed by silent backports. The useful takeaway isn't the headline figure; it's that this is broad enough that "we'll get to it" is not a plan.
The playbook: reducing risk while the patch rolls out
Patching takes time — testing, change windows, chasing providers. The point of the interim is to make exploitation expensive and visible now, without waiting for every box to be clean. Four steps, in order.
1. Find every affected deployment
You can't protect what you can't see. Start from the outside in: enumerate your internet-facing assets — including the ones nobody remembered — and record each PHP deployment's version, deployment model (FPM / mod_php / etc.), owner and patch status. Don't stop at the obvious web servers; include containers, managed hosting and bundled third-party apps. This is exactly what an external attack-surface pass or a focused vulnerability assessment is for, and it's the step teams most often skip — then get surprised by the forgotten staging box.
2. Put a shield in front — virtual patching
For every exposed PHP service, get a network-layer control in the path while you patch:
- Turn on IPS and WAF protection in front of the service — this is "virtual patching": blocking the exploit at the perimeter so an unpatched box behind it is far harder to reach.
- Pull non-public systems off the internet entirely — behind a VPN or SASE. If a PHP service doesn't need to face the world, this is the fastest risk reduction available.
- Restrict or isolate where you can — limit source IPs to known consumers, and temporarily take a service offline if no other control is adequate to the exposure.
- Apply the advisory's config mitigations. For CVE-2026-115560 the PHP project recommends setting
session.upload_progress.enabled=0until the next bug-fix release (subject to the conditions in the advisory). Disabling session upload progress also reduces exposure to some paths for CVE-2026-109619 — but it does not cover all variants. Updating is still the only complete fix for the two original CVEs.
3. Patch properly — and prove it
Test the update against your critical workflows, deploy the patched runtime or vendor package, then restart or replace the PHP workers and containers. Finish by verifying the running service is on the patched build and the old worker processes have actually stopped. Patch the runtime, not just WordPress and its plugins — updating the CMS leaves the vulnerable engine underneath it untouched.
4. Assume you might already be a victim
A CVSS 9.2 pre-auth RCE that's been public since 21 October is being scanned for at internet scale. Before you call it closed, look for the footprints: suspicious web requests, unexplained PHP worker crashes, process execution you can't account for, modified application files, and unusual outbound connections. If you find anything, preserve the evidence, check for persistence, and rotate any credentials that process could touch. "We patched it" and "we weren't compromised before we patched it" are two different claims.
Fortinet coverage, for those already on the stack
As an authorised Fortinet partner, the question we get first is "are my Fortinet boxes affected, and do they help?" Per Fortinet's PSIRT advisory FG-IR-26-186: FortiOS (FortiGate), FortiManager, FortiAnalyzer and FortiWeb are confirmed not affected. And FortiGuard Labs has shipped IPS signatures you can use as virtual patching in front of your PHP services:
| Signature | Name | Targets |
|---|---|---|
| 62657 | PHP.longjmp.Remote.Code.Execution | CVE-2026-109618 |
| 62658 | PHP.stale.global.state.Remote.Code.Execution | CVE-2026-109619 |
Both land in IPS database 37.317, with web-protection coverage available across FortiGate, FortiSASE, FortiProxy, FortiWeb and FortiADC. To make them count on a FortiGate: update the IPS database and confirm both signatures are present; add them to the IPS sensor on the policy carrying traffic to the PHP service; enable block and log; check the IPS entitlement for your FortiOS release; and — the step people miss — make sure inspection can actually read the request content, which for HTTPS means decryption or inspection after TLS termination. A signature that only sees ciphertext protects nothing. Then watch the detections for false positives and keep the signatures current.
IPS is a stopgap, not a cure
Virtual patching buys you time and visibility; it does not fix the vulnerable runtime. Keep the interim controls on until patching is verified everywhere, then keep them on anyway — they're good hygiene — but don't let a green IPS dashboard talk you out of finishing the patch.
Where Ogma fits
An outbreak like this is a coordination problem as much as a technical one — security, application and infrastructure teams all have to move together, and most of the failures are organisational (the asset nobody owned, the container nobody restarted). If you'd rather not run that scramble alone, that's the work we do:
- Find the exposure — a vulnerability assessment / attack-surface pass that enumerates your internet-facing PHP by version and deployment model, forgotten boxes included.
- Stand up virtual patching — deploy and tune FortiGate / FortiWeb IPS and WAF protection in front of the affected services, with inspection configured so it actually sees the traffic.
- Watch for exploitation — monitor for the indicators above and investigate the hits, so a scan that turns into a foothold doesn't sit quietly for weeks.
- Clean up if it already happened — incident response, persistence hunting and credential rotation if you find signs of compromise.
Not sure what's exposed?
Tell us roughly what you run and where. We'll help you find the internet-facing PHP, get virtual patching in front of it, and confirm the real fix is in — fast, because the clock on a public 9.2 is already running. Call +91 80 0979 0979 or get in touch.
Talk to our security teamFrequently asked
Does updating WordPress / Drupal / my CMS fix PHP2Shell?
No. The flaw is in the PHP runtime underneath the application, so you must update PHP itself (to 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12 or your vendor's backported package) and restart the workers. Updating the CMS and its plugins leaves the vulnerable engine in place.
My server reports a fixed-looking version — am I safe?
Not necessarily. Vendors often backport security fixes without changing the version string, so a version number alone proves nothing either way. Confirm the actual package build, and confirm the running PHP workers were restarted onto it — a package update that hasn't recycled the workers hasn't taken effect.
Will a WAF or IPS protect me until I patch?
It substantially reduces risk — this is virtual patching, and it's the right interim control. FortiGuard has released IPS signatures 62657 and 62658 (IPS DB 37.317). But treat it as a stopgap: keep the controls on, and still complete and verify the runtime patch, because a signature can be evaded and doesn't repair the underlying defect.
Are Fortinet products affected?
Per Fortinet PSIRT advisory FG-IR-26-186, FortiOS (FortiGate), FortiManager, FortiAnalyzer and FortiWeb are confirmed not affected. Several Fortinet products also provide IPS/web-protection coverage you can use to shield your PHP services while you patch.
We're on PHP 8.1 or older. What now?
Those branches no longer receive standard security releases. The PHP project has issued limited source backports for some older branches, but applying them requires build expertise. Practically, the durable answer is to move onto a supported branch (8.2+); in the meantime, prioritise virtual patching and reducing the service's internet exposure.
How urgent is this?
High. Two CVSS 9.2 pre-authentication RCEs in a runtime this widely deployed, public since 21 October 2026, are scanned for at internet scale. Assign an owner and a timeline to each affected service, keep interim controls active until patching is verified, and check for prior compromise rather than assuming you escaped.
Sources & further reading: FortiGuard Labs, "Understanding PHP2Shell Vulnerabilities and Reducing Risk While You Patch" (fortinet.com/blog); the PHP project's security advisories for CVE-2026-109618, CVE-2026-109619 and CVE-2026-115560; Fortinet PSIRT advisory FG-IR-26-186; Censys internet exposure snapshot, 22 October 2026. CVE identifiers, CVSS scores, fixed versions, signature IDs and exposure figures are quoted from these sources as reported. This article is Ogma's independent rewrite for Indian enterprise readers and is not affiliated with or endorsed by Fortinet or the PHP project.
Stay ahead of cyber threats
One short email a week — curated Indian cybersecurity news, Fortinet releases, DPDPA updates. No fluff.