PHP runs a ridiculous share of the internet, and on October 21 two critical vulnerabilities in its core request-handling engine went public. Together they're called PHP2Shell, and ThreatLabz has been watching attackers swarm them ever since. I'm a few days late on this one, which is exactly the problem: the attackers weren't. Standard disclosure: I work at Zscaler.
What's Broken
Two CVEs, both scored CVSS v4.0 9.2:
- CVE-2026-109618: out-of-memory corruption and use of uninitialized memory.
- CVE-2026-109619: use-after-free and stale memory access.
Chained together, they give an unauthenticated attacker remote code execution. No login, no special config. They can also leak process memory, which means credentials and tokens sitting in RAM are fair game.
The scope is the scary part: every PHP version from 5.4 onward. That's not a bug in a plugin. That's the engine.
The attack is clever. A first request sets up state and deliberately crashes PHP with a fatal or memory exhaustion error. A second request then reclaims the freed memory and runs attacker code. Two POSTs, and the server is theirs.
48 Hours
ThreatLabz updated the research on October 23 with what Zscaler's deception decoys saw in the first two days: 1,156 exploitation attempts from 156 unique IPs against 70 organizations. The attackers showed up with ShellFlow, Nuclei templates, and the Neo Scanner, plus payloads for command execution, webshell persistence, object injection, and deserialization chains. They're hitting the obvious paths: /, /index.php, /api/endpoint.php, /app/upload.php.
Two days. That's how long "we'll patch it in the next maintenance window" lasts now.
The EOL Trap
Patches exist: 8.5.12, 8.4.27, 8.3.36, and 8.2.35. Many Linux distros have backported fixes too, so check your release notes.
Update: Cloudflare reported on October 23 that a related issue, CVE-2026-115560, affects fully patched PHP releases too, including 8.5.12. So patching is necessary but not sufficient right now. That makes the hardening step below (turning off session.upload_progress.enabled) a lot less optional than it sounds. More details are still coming. (Cloudflare's write-up)
But PHP 8.1 and everything older is end of life. No official patch is coming. If you've got an old internal app running PHP 7 that "nobody touches because it works," it's now an unpatchable, unauthenticated RCE. Every org has one. Usually several. Usually inside an appliance or hosting panel nobody remembers is running PHP.
What To Do This Week
- Patch to a fixed version, or turn PHP off until you can.
- Inventory everything. Bundled apps, appliances, hosting panels, that WordPress site marketing spun up in 2019.
- Migrate EOL branches. If you truly can't, isolate those systems from untrusted networks. That's a temporary measure, not a strategy.
- Watch your logs for segfaults, crash dumps, and weird memory spikes. The exploit's first request crashes PHP on purpose, so crashes are your tell.
- Harden while you patch: review
memory_limitandmax_execution_time, setsession.upload_progress.enabledto Off, and block multipart uploads at the edge where you can.
The Zero Trust Angle
This is the textbook case for not exposing apps you don't have to. If an internal PHP app sits behind ZPA instead of on the internet, attackers can't send it two POSTs, because they can't reach it at all. For the ones that must be reachable, ThreatLabz published signatures for both internet-facing servers (Advanced Threat Protection) and internal apps (ZPA App Protection), plus deception decoys for WordPress, Drupal, Joomla, phpMyAdmin, and Nextcloud. Zscaler says its own platforms weren't affected.
Bottom Line
You can't patch an EOL branch, and you can't out-patch attackers who show up in 48 hours. What you can do is make sure your vulnerable PHP isn't reachable by the people scanning for it. Patch what you can, hide what you can't, and go find the PHP you forgot you had.
Full research: CVE-2026-109618 and CVE-2026-109619: PHP2Shell (Zscaler ThreatLabz)