PHP2Shell Explained: Two Engine Bugs That Put Millions of PHP Servers at Risk
A defender's breakdown of CVE-2026-109618 and CVE-2026-109619, the unauthenticated RCE flaws in the PHP engine itself. What happened, what it affects, and the exact steps to patch, mitigate, and detect it.
On October 21, 2026 the PHP project rushed out an emergency update for two serious security holes, CVE-2026-109618 and CVE-2026-109619, together nicknamed PHP2Shell. Both score 9.2 out of 10 on the severity scale, and both let an attacker run their own code on your server without logging in. The scary part is where the bugs live. They are not in WordPress or Laravel or any single app. They are in PHP itself, the engine that runs those apps. So even a one-line "Hello World" page on a vulnerable version is a target.
The idea in one picture
You do not need to understand the low-level details. Here is the whole attack as a simple flow. A booby-trapped request makes PHP trip over itself, and the attacker takes advantage of the mess that gets left behind.
What actually broke
Think of PHP handling a web request like a chef following a recipe. Normally the chef finishes every step and cleans up. With these bugs, the attacker forces the chef to stop in the middle, usually by sending a request so big that PHP runs out of memory. The kitchen is left with a half-made dish and dirty counters.
The problem is that PHP later comes back and treats that half-finished mess as if it were a real, completed dish. It reads leftover scraps of memory as if they were valid data. An attacker who knows how to arrange those scraps can either read private information that was sitting in memory, or trick PHP into running a command of their choosing.
That is the common thread behind both bugs. In security terms these are "use-after-free" and "uninitialized memory" problems in PHP's core code, and the vulnerable code has been there since PHP 5.4.
- CVE-2026-109618 abuses the step where PHP assembles the data you submit in a form. It can leak memory belonging to other people's requests, like session tokens, API keys, even private keys, and in the right conditions leads to code execution.
- CVE-2026-109619 abuses leftover state that lingers after an error, so the next request handled by the same worker can stumble onto stale data.
One detail matters a lot: a PHP feature called session upload progress is switched on by default and runs even if your app never uses sessions. That is why plain, default installations are at risk, not just fancy apps.
Timeline
- Oct 17: Amazon's trap servers catch the first attack attempt, four days before anyone announced the bug.
- Oct 20: Details leak on social media before the planned Oct 22 announcement, so PHP moves its release up.
- Oct 21: The fixed versions and advisory go out. Cloudflare sees attacks starting at 07:43 UTC.
- Oct 22 to 23: Attack attempts climb into the millions, some trying to install a Mirai botnet variant called Potassium. Around 34 million PHP servers are still estimated to be unpatched. Researchers caution that turning this into a reliable exploit is harder than the raw numbers suggest.
What is affected
Because the flaw is in the engine, the reach is huge. Every way of running PHP as a website is affected: Apache mod_php, PHP-FPM, php-cgi, LiteSpeed, and FrankenPHP. Apache mod_php is the riskiest, because an attack can land before your own code even runs.
Any app built on PHP inherits the problem no matter how clean its own code is. That includes Magento, Shopware, WooCommerce, and the usual CMS platforms.
Fixed versions
- PHP 8.2: upgrade to 8.2.35 or later
- PHP 8.3: upgrade to 8.3.36 or later
- PHP 8.4: upgrade to 8.4.27 or later
- PHP 8.5: upgrade to 8.5.12 or later
- PHP 7.4, 8.0, 8.1: no upstream release. Use distribution or LTS backports from vendors such as Debian (deb.sury.org), Ubuntu, Remi, AlmaLinux, or CloudLinux, which may keep the original version number.
- PHP 7.3 and older: no fix. Migrate to a supported branch.
One gotcha: containers and apps that bundle their own interpreter can differ from your CLI PHP version. Check the runtime that actually serves traffic, and note the official Docker php image receives no fix for 8.1 or older.
How to fix it, step by step
- Find every copy of PHP you run. Note the version and where it came from, and do not forget copies hidden inside Docker images.
- Update to the safe version for your branch (see the list above).
- Rebuild your containers instead of trying to patch a running one.
- Restart every PHP process. This is the step people miss. The fix does nothing until the old workers are replaced, so reload or restart the service.
- Double-check. Confirm the running version is the patched one.
If you cannot patch this very minute
PHP is clear that nothing short of the update fully closes the hole. These steps only buy you time:
- Turn off the risky feature: set
session.upload_progress.enabled=0, and on PHP 8.5 also setfatal_error_backtraces=0. Then restart your workers. - Put a size limit on incoming requests at your reverse proxy or firewall, since the attack relies on very large requests.
- Take any server you cannot update off the public internet until you can patch it.
Signs someone is trying
No single clue is proof on its own, so look at a few together:
- The strongest sign: your web server process suddenly launching a shell or a download command. That is not normal and deserves immediate investigation.
- Error logs: repeated "Allowed memory size exhausted" errors reported at a strange location (
Unknown on line 0), often next to crashes. On its own it can be innocent, but paired with huge requests it is suspicious. - Traffic: form submissions with absurdly long or oddly shaped field names.
The short version
PHP2Shell is a reminder that the engine running your code is part of your security, not just the libraries you install. Update PHP to the fixed version, restart every worker, and confirm the live version is the patched one. Treat the config tweaks as a stopgap for the next few hours, never as a replacement for the update.