PHP2Shell Vulnerability: Patch PHP, Not Just Settings
PHP2Shell affects PHP request handling, and disabling upload progress is not enough. Check affected versions, patch safely, and verify every runtime today.

PHP2Shell: Why the Workaround Is Not Enough
A security workaround can feel like momentum: turn off the setting mentioned in a leaked write-up, restart PHP, and move on. With PHP2Shell, that pattern leaves a real gap. PHP's security team says the workaround people are sharing doesn't fully fix the underlying issues - and that another vulnerable path can still be hit even after the referenced session feature is disabled.
PHP2Shell refers to two unauthenticated remote code execution vulnerabilities in PHP core: CVE-2026-109618 and CVE-2026-109619. They affect PHP request handling across common web-serving setups. PHP also shipped patched releases on October 21, 2026, after researchers reported exploitation attempts before the fixes were public. The takeaway is simple: if your web-facing PHP runtime isn't updated to a fixed release, a configuration change isn't a substitute.
This guide walks through what the vulnerabilities do, why the leaked mitigation doesn't close the problem, and how to check PHP installations without missing workers, containers, or less-updated internal tools. It also covers what to do when you're stuck on older branches that depend on distribution backports or are already end of life.
What PHP2Shell breaks
PHP2Shell isn't a "one app, one plugin" problem. It's in PHP's core request-handling behavior, which is why the risk shows up across a wide range of web-facing PHP services. PHP's advisory covers two separate bugs, and each one fails in a different way.
CVE-2026-109618: a failure during out-of-memory handling
This issue shows up when a request goes past PHP's memory limit. PHP aborts the request partway through some operation. In certain code paths, that interruption can leave internal request data only partially initialized. Later cleanup then treats that half-state like it's valid.
The critical operational detail: PHP's advisory says this can lead to remote code execution over HTTP against Apache's mod_php with default settings, before application code runs. That means the usual "we block it in the app" protections - route checks, form validation, requiring a logged-in user - don't reliably stop this specific path. The vulnerable behavior happens inside PHP before the application ever gets to decide.
That's why this should be treated as a runtime patching problem, not just an application review task. You should still review your endpoints, but it doesn't replace updating the interpreter.
CVE-2026-109619: a use-after-free between request operations
The second flaw is a use-after-free. In plain terms: internal state tied to a request can remain reachable after the memory backing it has been freed. Then a later operation can read that stale memory before normal initialization resets things.
PHP's advisory describes more than one way that state can end up in the wrong place. The issue was confirmed using the fatal-error backtrace feature enabled by default in PHP 8.5, and separately via session lifecycle handling under both mod_php and PHP-FPM. The advisory also points to an additional vulnerable path in the SAPI-level multipart form parser. That extra path is exactly why the "leaked workaround" doesn't fully hold up.
The two CVEs are rated CVSS v4.0 9.2 (per the referenced source). The number helps communicate severity, but it's not the best tool for deciding what to do. The real questions are: is your PHP runtime exposed to the internet, is it on a fixed build, and have all of the processes that serve PHP actually been updated?
Why the deployment model matters
PHP can run under different setups, including Apache's mod_php and PHP-FPM. A real fleet also often has mixed versions: a production service, an admin interface, staging, scheduled jobs, and a container image that hasn't been rebuilt in a while.
So it's risky to say "our PHP server is patched" without checking what each workload is actually using. You need to know which runtime version each service runs, and whether the service processes restarted onto the patched binary. Updating a package on one host doesn't guarantee a long-running worker, a separate container, or an internal app is running the same fixed code.
Why the leaked workaround fails
The leaked write-up blamed PHP's session upload-progress feature and implied that disabling it would be enough. PHP's own advisory says that's incomplete. It describes a separate use-after-free in the SAPI-level multipart form parser - one that can be triggered by a multipart/form-data POST request whether or not session.upload_progress is enabled.
That changes what "safe" means. Turning off the feature might block the specific path described in the leak, but it doesn't close the independent parser path. You can disable upload progress, see the expected behavior improve, and still be vulnerable.
A mitigation is not the same as a fix
A mitigation reduces risk in a narrow scenario. A fix removes the vulnerable behavior from the software itself. Those are different outcomes, and you shouldn't treat them as interchangeable in an incident response plan.
For PHP2Shell, the config change people shared after the leak didn't fully resolve either vulnerability. PHP's security team is explicit: configuration changes do not fully close the issues, and updating to a patched version is the actual fix. If patching can't happen right away, a temporary mitigation may still help lower risk - but you shouldn't log it as "resolved."
A clearer status model is:
- Exposed: the affected runtime is serving requests and is not patched.
- Temporarily mitigated: a control is in place, but the vulnerable runtime is still installed.
- Patched and verified: the workload is running a fixed version, and you checked what's actually deployed after the change.
This can feel like process work. It's not. It's how you prevent "disable a setting" from quietly turning into "done" just because a ticket was closed.
Why the leak created confusion
The source account says the leaked details also overstated the severity (CVSS 10.0 instead of the official CVSS v4.0 9.2) and pointed to one feature when a second independent path existed. That's a useful reminder when dealing with early security information: speed matters, but leaked technical details aren't guaranteed to be complete or correct.
Teams that applied the workaround weren't necessarily careless. They were working with what they had at the time. The failure mode is assuming an interim response based on incomplete details proves the underlying bug is gone.
And this isn't unique to PHP. The pattern shows up everywhere: disable a feature, flip a flag, restart a service - fixing the obvious exploit sample without addressing the vulnerable behavior underneath. So the right question isn't only, "Did we apply the suggested setting?" It's, "Does the vendor say this closes every affected path?" In this case, the answer is no.
How to check and patch your PHP installations
Do this in three phases: inventory, patch, verify. When people rush and do it in a different order, it's easy to miss an admin tool, an internal service, or a container that keeps running the old image.
1. Find every PHP runtime that handles requests
Inventory where PHP runs, not just the servers someone labeled "web." Include:
- Public websites and APIs.
- Internal admin tools and staff portals.
- PHP-FPM pools and Apache
mod_phpinstallations. - Containers, virtual machines, and managed hosting environments.
- Staging systems that are reachable from a network or contain sensitive data.
- Long-running workers or services that may not restart automatically after a package update.
Then confirm the runtime used by each workload. A php -v command can show the command-line PHP version on a host, but that doesn't always tell you what Apache modules, PHP-FPM pools, or containers are using. Validate against service configuration, package manager state, container images, and deployment tooling.
2. Compare each runtime with the fixed releases
PHP's security team lists these fixed releases for the branches mentioned in the advisory:
| PHP branch | Fixed release |
|---|---|
| PHP 8.5 | 8.5.12 |
| PHP 8.4 | 8.4.27 |
| PHP 8.3 | 8.3.36 |
| PHP 8.2 | 8.2.35 |
If you're on a version earlier than the fixed release for that branch, treat it as affected. Confirm the deployed package or image version rather than relying on a generic "up to date" label. It's common for a base image to look current while a separately pinned PHP package is not (or vice versa).
For current release info, use the official PHP downloads page and the PHP supported versions page. For this incident, compare against the fixed versions in PHP's advisory - not just whatever is newest on a package dashboard.
3. Handle older branches through the right channel
The source account notes that PHP 7.4 through 8.1 get only minimal source backports directly from the PHP project. If you run one of those versions, check your Linux distribution's security tracker and package changelog. The package version string may not match the upstream version even when the vendor backported the fix - so verify the distribution advisory instead of inferring from the number.
PHP 7.3 and earlier are end of life, and the PHP project isn't expected to ship fixes. For public-facing systems on those branches, treat remediation as a separate problem: upgrade to a supported, patched runtime. If you can't migrate immediately, document the exposure, limit access where you can, and assign an owner and deadline for the upgrade. Don't assume "it's been running fine" means it's safe.
4. Patch the deployed service and verify it
Install the fixed package, or rebuild your application image using a patched PHP release. Then restart or redeploy so new processes load the updated runtime.
Verify the version from the same context that actually serves the application. For PHP-FPM, check the pool and service; for containers, inspect the running image and deployment revision; for Apache, confirm the loaded PHP module.
Here's a simple checklist that's actually auditable:
- List each affected workload and its current PHP branch and version.
- Identify the fixed package or image for that environment.
- Apply the update and restart or redeploy the service.
- Confirm the running process uses the fixed release (or a documented distribution backport).
- Re-run inventory to catch anything you missed.
- Mark any temporary mitigation as temporary until patch verification is complete.
One incident account mentions an internal admin tool staying on PHP 8.2.33 because its automatic update job ran weekly. They patched it manually and changed the update check to run hourly, matching the cadence used for customer-facing services. The point isn't "update every hour forever." It's that your update cadence has to match the real consequence of missing a critical fix. Weekly can be fine for routine updates - and still be too slow for something being actively targeted.
5. Treat exploitation reports as a reason to move quickly
The source reports that watchTowr and Cloudflare saw exploitation attempts before the official patch was released on October 21, 2026. Patchstack also reported attempts against multiple hosting partners in the hours before the fix shipped. That doesn't prove every vulnerable installation was compromised, but it does remove the comfort of assuming this is purely theoretical.
Prioritize internet-facing workloads and systems with sensitive data, but don't stop there. Internal tools can still be reachable via VPNs, shared networks, or stolen credentials. If you find a vulnerable service exposed during that period, follow your incident process: preserve logs, review suspicious activity, and assess whether credentials or downstream systems need attention. Patching closes the software flaw; it doesn't tell you whether exploitation happened earlier.
Conclusion
PHP2Shell is a clear example of how a mitigation can fix one visible path and still leave another one open. Disabling session.upload_progress does not address the independent multipart-parser issue described in PHP's advisory.
The right response is to find every web-facing PHP runtime, move each one to a fixed release (or a verified distribution backport), restart the service, and confirm what's actually running.
Start with your inventory today. If your status is "we disabled the setting," the vulnerability isn't resolved yet. If you run PHP services, use the original incident report alongside PHP's official release information, assign an owner to every outstanding runtime, and close the loop only after patch verification.