PHP2Shell: Patch Tonight, Then Figure Out What Your Web Servers Can Reach

PHP2Shell vulnerability means patch ASAP

Schedule a Portnox Cloud demo today.

Contents

TL;DR:

PHP2Shell (CVE-2026-109618 and CVE-2026-109619) is unauthenticated remote code execution in the PHP runtime itself. It affects every version from 5.4 through 8.5.11 on mod_php, PHP-FPM, php-cgi, FrankenPHP and LiteSpeed, and it’s already being exploited. Upgrade to 8.5.12, 8.4.27, 8.3.36 or 8.2.35 now.

If you’re on 8.1 or older, get backported packages from your distro or LTS vendor, or build from the PHP team’s source backports for 7.4, 8.0 and 8.1. If you can’t patch today, set session.upload_progress.enabled=0 and restart every worker (that only blocks CVE-2026-109619), and pull anything internet-facing offline until it’s fixed.

Once you’re patched, assume anything exposed since Tuesday’s leak may already be owned: check worker logs for memory-exhaustion fatals and segfaults, look for shells spawned by php-fpm or httpd and new PHP files in your webroots, and rotate any credentials sitting in config files on those boxes. Then figure out what your web tier can actually reach, because that’s what decides how bad a compromise gets.

 

If you run anything on PHP, you probably had a long Tuesday night, and today isn’t shaping up much better. The PHP team pushed emergency fixes Wednesday for a pair of bugs the researchers are calling PHP2Shell (CVE-2026-109618 and CVE-2026-109619), and the Wall Street Journal ran it with the kind of headline that gets forwarded to CISOs by board members with a single question mark attached. For once the headline isn’t overselling it by much. Unauthenticated remote code execution in the runtime itself, going all the way back to PHP 5.4, in the language sitting under a huge chunk of the web, is about as bad as this stuff gets.

So yes, patch. If you stop reading here, go do that first. I want to spend a few minutes on what comes after patching, though, because that’s where I think most of the real damage from this one is going to land.

What’s actually broken in PHP2Shell

Both bugs live in the same ugly corner of PHP: what happens to memory when a request dies badly.

CVE-2026-109618 is the out-of-memory bug. The attacker steers a request into PHP’s memory limit at a very specific moment, while the engine is partway through setting up internal request data. PHP bails out, and the cleanup code that runs afterward trusts structures that never finished initializing. Getting from that to code execution is a chain, and according to the Imperva write-up it runs through session handling, OPcache class caching and the backtrace generation PHP does on fatal errors.

CVE-2026-109619 is the stale global state bug. Some per-request state outlives the memory backing it, so when the same worker picks up the next request, it can touch that freed memory before normal initialization resets things. That’s a textbook use-after-free, and along the way to RCE it hands you memory disclosure. The disclosure piece deserves more attention than it’s getting. A long-lived worker that has been serving other people’s requests is holding their session data, tokens and sometimes credentials, and a bug that leaks that memory is useful to an attacker even if they never land a shell.

The detail that made me wince: on mod_php, the PHP security team says a single unauthenticated request can kick this off before any application code runs. Your WordPress hardening, your input validation and your carefully reviewed framework code don’t get a vote. PHP-FPM, php-cgi, FrankenPHP and LiteSpeed are all affected as well, and per the WSJ, the Calif researcher needed as few as a few dozen requests, taking seconds on smaller systems and hours on bigger ones. Code runs as your web worker account, which matters a lot for the rest of this post.

Fixed builds are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. PHP 8.1 and older are out of support, but the PHP team published minimal source backports for 7.4, 8.0 and 8.1, and your distro or LTS vendor may have backported packages already (if you pull from the volunteer-run Remi repo, it won’t have updates today, so build from source or switch to distro packages). If you truly can’t patch yet, setting session.upload_progress.enabled=0 and restarting every worker closes off the 109619 path, but it does nothing for 109618, so treat it as a tourniquet. The UK NCSC is telling people to pull unpatchable systems offline, and for anything internet-facing that’s the right call.

The AI part, minus the hype

I’ll push back a little on how this is being covered. The AI angle is real, but a lot of the framing makes it sound like a model conjured a brand new class of bug out of thin air. Per the WSJ, a researcher flagged this area of PHP as sketchy decades ago, and everyone wrote it off as too hard to exploit. What GPT-5.6 Sol, Calif, Wiz and OpenAI’s own team did was grind through an exploitation chain complex enough that humans had looked at it and decided it wasn’t worth the effort. Wiz said basically the same thing: the flaws sat there for decades, and AI lowered the bar for proving they could actually be exploited.

That matters for defenders because it tells you where the change really happened. The cost of turning a known-ugly bug into a working exploit dropped, and so did the cost of diffing a patch to figure out what got fixed. This patch touched close to 200 files and more than 10,000 lines of code. A few years ago a diff that size bought defenders some time, since somebody had to wade through it by hand; now you point a model at it and go get coffee. Sansec was predicting mass exploitation within fifteen minutes of the release, and Cloudflare says it saw attack traffic against its customers after the leak and before the official patch landed.

I spent a good chunk of my career writing exploits, and I’ll be honest about what this means. The patch window we’ve all been quietly leaning on, those days or weeks between disclosure and reliable weaponization, is mostly gone for bugs like this. Any internet-facing PHP box that sat unpatched after the Hacker News leak on Tuesday should be treated as possibly compromised until you’ve actually checked.

Patch, then go hunting

Patching closes the door, and it does nothing about anyone who already walked through it. If you had exposed PHP between Tuesday and whenever your fix landed, spend some time looking.

Start with your PHP-FPM and Apache error logs. These exploits lean on memory exhaustion and memory corruption, so a burst of “Allowed memory size exhausted” fatals or workers dying on signal 11 from a handful of source IPs is worth a hard look, especially if nothing in your app changed. Then check process trees, because php-fpm or httpd spawning sh, bash, curl, wget, python or base64 is almost never your application doing its job. Look in your webroots for PHP files that appeared or changed recently, since a webshell is still the cheapest persistence there is, and pull outbound connection logs for the web tier. A web server that suddenly starts talking to an IP it has never talked to before is about as loud a signal as you’re going to get.

One practical note: patching restarts the workers, and that wipes whatever in-memory evidence was sitting there. If you already have reason to think a box is owned, image it before you bounce it. If you don’t, patch first and hunt after; don’t let forensics become the excuse for leaving it exposed.

What can your web server reach?

Here’s the part I actually care about. Say one of your web servers got popped tonight, and the attacker is now running as www-data, apache or whatever your worker account happens to be. What do they do next?

Every web-tier incident I worked looked roughly the same from that point on. They read config files: wp-config.php, .env, settings.php, wherever the database password and API keys live. On a cloud instance they hit the metadata service at 169.254.169.254 to see whether an IAM role is attached and grab its temporary credentials. They go looking for SSH keys, LDAP bind accounts and SMTP creds, and they start scanning to see what the box can talk to. The web server was rarely what they were after; it was just the easiest thing to get onto, and the real damage came from the lateral movement that followed.

So the question I’d want every security team asking this week is pretty simple: if this box is owned, what can it reach, and what can its credentials reach? If the honest answer is the database, the file shares, a domain-joined service account, the internal admin panels and the rest of the server VLAN, then patching PHP2Shell fixes this bug and leaves the bigger problem sitting there for the next one.

This is where the boring stuff earns its keep: authentication, authorization and accounting. Your web server is an identity on your network, even though nobody ever logs into it with a password and an MFA prompt. It should authenticate as something specific, it should be authorized to reach exactly what it needs (its database on its port, maybe a cache, maybe one upstream API) and nothing more, and there should be a record of what it does so you notice when it starts doing something new. In practice that means the web tier can’t initiate connections into the rest of your network, egress is locked to the destinations the app genuinely uses, and the database account is scoped to the one schema the app needs without anything resembling DBA rights. It means secrets get pulled from a vault at runtime with short lifetimes instead of sitting in a plaintext file next to the code, IMDSv2 is enforced with instance roles scoped tight, and there’s no domain-joined service account on a public-facing box because it was convenient back in 2017. And for the humans who administer these boxes, SSH should run through certificate-based, passwordless access tied to a named person, so there’s no long-lived private key sitting in a home directory waiting for an attacker to find it.

None of that is new, and most of it is advice we’ve been giving since Heartbleed. What AI-assisted exploitation changes is the gap between “the bug exists” and “the bug is being used against you,” which is now short enough that your patching speed can’t be the only thing standing between an internet-facing box and everything behind it. Limiting what a compromised server can touch, and how fast you can cut it off once it starts misbehaving, are the controls that still work on the nights you lose the race.

The short version

Patch tonight, check whether somebody beat you to it, and then pull the firewall rules and service account permissions for your web tier and read them the way an attacker would. If the answer to “what can this box reach” makes you a little queasy, congratulations, you just found your real project for next quarter. With the volume of bugs AI-assisted research is shaking loose right now, PHP2Shell probably won’t even be the last one like this before the end of the year.

Share

About the Author

Picture of Garrett Gross

Garrett Gross

Garrett Gross is Field CISO at Portnox, where he leads pre- and post-sales strategy and serves as the company's public-facing voice, representing Portnox through speaking engagements and press commentary on identity, access, and zero trust.

About the Author

Picture of Garrett Gross

Garrett Gross

Garrett Gross is Field CISO at Portnox, where he leads pre- and post-sales strategy and serves as the company's public-facing voice, representing Portnox through speaking engagements and press commentary on identity, access, and zero trust.

Related Reading

IoT SecuritySecurity Trends

French Supermarket Fridge Hack: IoT Device Security Lessons

October 14, 2026
Artificial Intelligence

Who’s Left Holding the Bag When AI Agents Go Rogue?

October 9, 2026
Artificial Intelligence

Access Is Not Authorization: Someone Needs to Explain That to AI Agents

October 8, 2026

Portnox Closes the Gap on Shadow AI

X