PHP2Shell: The Internet Wasn't Broken. Something Underneath It Was.
There is something fascinating, and slightly unsettling, about the way the internet works. We tend to picture it as an enormous collection of independent systems, millions of websites spread across thousands of hosting providers and cloud platforms, built by countless developers using an almost endless combination of technologies. It feels distributed and resilient, and in many ways it is. Yet underneath all that diversity, a remarkable number of systems lean on the same few components, pieces of software that have quietly done their work for decades while we built ever more sophisticated things on top of them. Most days nobody gives them a thought. Then something happens that reminds us how much we depend on them.
On October 21, 2026, two critical vulnerabilities were disclosed in PHP, the language behind a substantial part of the web. Tracked as CVE-2026-109618 and CVE-2026-109619 and known together as PHP2Shell, both received a CVSS 4.0 score of 9.2. Researchers showed that weaknesses in how PHP manages memory could let an attacker execute code remotely without authentication, and that vulnerable systems could leak process memory containing credentials, tokens and other secrets. The flaws were not in WordPress, Drupal or Joomla but in PHP itself, beneath them, which meant that even a simple application with nothing interesting going on could be exposed just by running on a vulnerable runtime.[1][2]
What caught my attention is that none of this came from an obviously dangerous feature or a forgotten input check. It came from something more fundamental, namely how PHP handles memory when things go wrong.
Before the application gets a say¶
Picture an ordinary visit to a website. Your browser sends a request that may pass through a content delivery network, a web application firewall and a load balancer before it reaches the web server. If the site runs PHP, the request then lands in the PHP runtime, which prepares the environment the application will run in. It parses the request, allocates memory, sets up internal structures and hands everything over. Only after that does the application get to do its job, checking credentials, fetching data or building a page.
It is easy to forget how much happens before our own code is involved, and that is exactly where this story lives. A developer can write careful PHP, validate every input, use parameterized queries and follow every good practice, and none of it helps if the vulnerability is triggered inside the runtime before those controls run. It is a bit like designing an office with badge readers, cameras and properly locked doors, only to find that the mechanism operating the doors has a defect. The access policy can be excellent while the machinery enforcing it is not. PHP2Shell goes after that machinery.
Memory that has been released but not forgotten¶
To see how, it helps to talk briefly about memory, not because everyone needs to become a C developer, but because the difference between application logic and memory management sits at the heart of what happened.
A running program needs somewhere to keep things: variables, objects, request data and the many internal structures it uses to execute code. One way to imagine it is a warehouse with numbered shelves. The program asks for a shelf, puts something on it and remembers the shelf number. When it no longer needs the item, the shelf is released and can be reused. In lower-level languages, that remembered shelf number is a pointer, and the catch is that releasing a shelf does not erase every note that refers to it. If the program later follows an old note, it may find an empty shelf, someone else's belongings, or something an attacker managed to put there. This is called a use-after-free. The name sounds abstract, but the idea is simple: the program believes it is handling one thing while the memory now holds something else entirely. Sometimes that causes a crash, sometimes it leaks information, and under the right conditions it becomes a way to steer what the program does next.

PHP2Shell involves two related kinds of memory-management failure, both rooted in assumptions that stop holding when request processing is interrupted.
The first, CVE-2026-109618, concerns what happens when PHP runs out of memory at an awkward moment. To escape certain fatal errors, including out-of-memory failures, the runtime uses a mechanism called longjmp(). Normally a function finishes its work and returns to whoever called it, in a fairly predictable order. longjmp() instead jumps straight back to an earlier point, skipping parts of that sequence, rather like an emergency exit. Emergency exits are useful, but they cause trouble when the building's procedures assume everyone leaves the normal way. If PHP has allocated memory for an internal structure but not yet finished preparing it, and another allocation fails, the runtime takes its emergency exit. Later, during cleanup, it finds the half-built structure, assumes it is valid and starts making decisions based on fields that were never properly set. The failed allocation does not even need to be related to the structure that ends up corrupted; it only has to interrupt execution at the wrong moment. The researchers found several routes to these conditions through normal HTTP request processing, including multipart form handling, the same kind of traffic used every day for forms and file uploads. A request can look perfectly valid to a WAF while still forcing PHP to allocate far more memory than its size suggests, and the official advisory notes that request-body and upload limits do not always protect the way administrators might expect.[1]
The second, CVE-2026-109619, is a little different. A PHP worker usually handles many requests over its lifetime, since tearing down and rebuilding the process every time would be wasteful. That convenience comes with an obligation, which is that one request must never leave traces that affect the next. Some internal globals briefly point at memory owned by the current request and are normally reset when it finishes. In vulnerable situations, a fatal error or even certain diagnostic operations can leave those references behind, so the memory is freed while the reference survives, and a later request may use it before normal initialization tidies things up. The advisory describes it as a producer and a consumer: one operation leaves a stale reference to request-owned memory, and another, possibly in a later request, acts on it after it has been freed.[2] It is the kind of problem that can stay invisible for years, because ordinary execution rarely produces the exact sequence needed, until someone goes looking for it on purpose.
From a memory error to a remote shell¶
There is a real distance between making a program crash and making it run code of your choosing, and vulnerability write-ups sometimes skip straight from "memory corruption" to "remote code execution" as if they were the same thing. A memory error gives an attacker an opening. Turning it into something useful means shaping the state of the process, arranging how memory is allocated, freed and reused, learning something about its layout, and coordinating several operations until the runtime behaves predictably enough to exploit.
The researchers showed several ways to do this. One of the more revealing involved PHP's session upload-progress feature, which can touch session state early when handling certain multipart uploads. A failed request could leave a session-related pointer referring to freed memory, and a later request on the same worker could make PHP use it. In one scenario, the result was not code execution but memory leaking out through a PHPSESSID response cookie. That may sound milder than a shell, yet it can be just as valuable to an attacker, since process memory may hold authentication tokens, application credentials and other secrets, as well as layout details that make further exploitation more reliable. Beyond that, the researchers demonstrated remote code execution using stale fatal-error backtrace data and a separate type confusion in OPcache, the component that speeds PHP up by caching compiled code and which turned out to be another place where process-local and shared state could drift apart after an interrupted operation.[3]
None of this amounts to one magic request that compromises every PHP server. The exploit chains have different prerequisites and depend on runtime behavior, memory layout and execution mode. Apache with mod_php stood out because pre-authentication paths were demonstrated there, while PHP-FPM and other modes have their own characteristics, with some session-related paths exploitable under PHP-FPM too. The researchers showed code execution against representative WordPress, Drupal and Joomla deployments, not because those applications were uniquely weak, but to show what a runtime-level flaw means in practice.[3]
Code executed this way generally runs with the privileges of the PHP process. That is not automatically root, but it is worth thinking about what a web application process typically has within reach: database credentials, storage, API tokens, internal systems and secrets. The original weakness lives in memory management, while the consequences depend almost entirely on what that process has been trusted to do.
Many websites, one dependency¶
Wiz described PHP as powering roughly 70 percent of websites, a number that drew a lot of attention. It deserves careful reading, because it says nothing about how many sites were exploitable, let alone compromised. It measures dependency, not exposure. Still, the underlying point stands. We build millions of seemingly independent systems on a fairly small set of shared technologies, and WordPress, Drupal, Joomla and countless custom applications can look nothing alike while sharing the same runtime underneath. A defect at that level does not care about organizational boundaries.
We have been here before in different forms. Heartbleed showed how a flaw in a widely used cryptographic library could undermine systems that otherwise looked secure, and Log4Shell showed how a small dependency buried deep inside applications could become an exhausting exposure-management problem. PHP2Shell belongs to the same broad family of systemic dependency risk, even though the mechanics are entirely different. The attack surface is not only the application we can see but everything it depends on, including things we may not know are there. An organization can have a solid inventory of its business applications and still struggle to say which PHP runtimes live in its hosting environments, containers, appliances and third-party systems. A team can know exactly which version of its application is deployed without much visibility into the runtime baked into the container image, and a server can be fully patched at the operating-system level while an application image still carries a vulnerable PHP. The hard part is often not patching what we know about, but discovering what we never realized we depended on.

There is another side to this story that I keep coming back to. According to Wiz, AI-assisted research played a substantial part in finding, developing and adapting these exploit chains. The weaknesses had existed for years, but exploiting them reliably required a great deal of reasoning about PHP internals, memory layouts and how several bugs interact.[3] A vulnerability can sit for a decade without being practically exploitable by most people, its severity unchanged while the cost of exploiting it shifts dramatically. It would be premature to conclude that every obscure memory bug is about to become exploitable at scale, but it seems fair to ask how much we have been leaning on complexity as an informal security barrier. Complexity does not make a defect harmless. Sometimes it only makes exploitation expensive, and expensive is a relative term.
What a WAF can and cannot see¶
For those of us working in Azure, a practical question follows naturally: would Azure Application Gateway WAF have stopped this? We put WAFs in front of internet-facing applications for good reasons. They inspect HTTP traffic, recognize many common attack patterns, enforce request restrictions and reduce exposure to application flaws. But a WAF sees HTTP, not the inside of a PHP worker. It cannot tell whether a pointer still refers to valid memory or whether an out-of-memory error left a half-built structure behind. Since PHP2Shell can be triggered by otherwise valid requests, and a multipart request is not suspicious simply for being multipart, the dangerous behavior may only appear once PHP processes the data, hits an error and then mishandles the resulting state. From the request alone, the difference may not be visible, which makes ordinary signature-based prevention difficult.
That is what makes Microsoft's response interesting. Rather than relying only on recognizing malicious input, it introduced mitigations that look at evidence in the server's response.[4] For Application Gateway v2, Microsoft documented an interim mitigation using HTTP header rewriting, which inspects outgoing response headers for suspicious PHPSESSID patterns tied to a known exploitation scenario and strips the header before it reaches the client. By that point the request has already reached PHP and may already have triggered the vulnerable path, so the control is not preventing the attack so much as denying the attacker a useful piece of what comes back. It is not a general answer to memory corruption, but a targeted way to break one known technique.
For Azure Front Door WAF, Microsoft announced a response-side managed rule, 99006001 in the MS-ThreatIntel-Response-CVEs group. As of October 26 it is still rolling out, with completion expected on October 28, and it is disabled by default, so it has to be explicitly enabled using the rule action Microsoft specifies. Rather than blocking the whole request, it can remove suspicious elements from the response and leave the rest intact, and Microsoft notes that this mitigation does not produce conventional WAF log entries or hit counts.[4] That has a quiet operational consequence: a lack of ordinary detections is not evidence that the mitigation was never relevant. Application Gateway WAF and Front Door WAF also should not be treated as interchangeable here, since they differ in mechanism, deployment requirements and rollout timing, and Application Gateway for Containers has its own limitations and does not support the same header-rewrite approach.

So would a WAF have helped? Potentially, where the right mitigation was configured and the attempt matched a supported pattern, but an ordinary WAF deployment should not be assumed to protect against PHP2Shell, and even a working targeted rule leaves the runtime itself unchanged. I think there is a broader point hiding in this. We sometimes talk about security controls as if having them were the same as being protected by them: there is a firewall, there is a WAF, there is authentication, so the application must be safe. In practice each control works at its own layer and rests on its own assumptions. A WAF is very good at certain things, and understanding corrupted process memory is not usually one of them.
Patching fixes the bug, architecture shapes the fallout¶
The PHP project released fixed versions on October 21: 8.2.35, 8.3.36, 8.4.27 and 8.5.12. Unsupported branches need a migration to a supported release or a verified vendor-maintained fix, and those updates are the primary remediation for both CVEs. The advisories also describe temporary measures for anyone who cannot patch right away, such as reducing the attack surface around multipart processing or disabling session upload progress.[1][2] One suggestion for certain out-of-memory paths is to set memory_limit=-1, combined with strict resource controls outside PHP. Disabling a memory limit to defend against a memory bug is not the first idea most of us would reach for, but the logic holds: the PHP-level allocation failure is itself what triggers the unsafe error path, so removing that trigger lowers exposure as long as operating-system, container or service limits still keep resource use in check. It is a temporary trade-off, not an improvement, and certainly not a replacement for patching.
The story also kept moving after the initial disclosure. On October 23 the PHP security team disclosed CVE-2026-115560, a related but more narrowly exploitable issue involving session upload progress and unusual application behavior, which the October 21 updates do not address. The recommended interim step is to disable session.upload_progress.enabled until a fix is available.[5] Complex vulnerabilities rarely end with the first patch; variants appear, guidance changes and vendors find affected components in products nobody initially suspected.
For anyone running PHP, a good response therefore means more than installing a package. It means knowing where PHP actually runs and in which execution mode, making sure workers have restarted and the updated code is really serving traffic, rebuilding and redeploying containers that carry vulnerable runtimes, assessing managed applications and appliances with their vendors, and checking runtime versions in the deployed environment rather than in the build configuration. If exploitation is suspected, patching does not undo what an attacker may already have done, whether that is exposed secrets, executed commands or modified application state. Suspicious multipart requests, unexplained memory failures, unusual worker restarts, unexpected PHPSESSID behavior and child processes spawned by PHP can all help guide an investigation, though they are signals rather than proof.
The longer-term question is what happens once the PHP process has been compromised. Can it read every application secret? Does it use a database account with more privileges than it needs? Can it reach internal services freely, does its container run with unnecessary permissions, and could its credentials open doors to Azure resources beyond the workload's actual purpose? These are familiar questions from Zero Trust and least-privilege design, and PHP2Shell gives them a concrete reason to be asked. Segmentation will not repair a dangling pointer, and managed identities will not keep PHP out of a broken cleanup path, but tightly scoped identities, restricted outbound traffic, isolated workloads and limited access to secrets can shrink what an attacker can do after getting code execution. Preventing exploitation and limiting its consequences are different jobs, and we need both.
What lies underneath¶
When a vulnerability like this appears, it is tempting to focus on the severity score, the exploit mechanics or how fast we can patch. Those matter, but I find the wider picture more interesting. Over the years we have become genuinely good at protecting applications, with identity-aware access, WAFs, workload isolation, monitoring, automated pipelines and policy enforcement, and all of it has value. Yet beneath those layers sit assumptions made years or even decades ago: that a structure will always be initialized before it is cleaned up, that a global pointer will never outlive the memory it refers to, that an error shortcut leaves the runtime in a predictable state. Most of the time they hold. When they do not, the failure can surface far from where the original mistake was made, and when that mistake lives in software used across a large part of the internet, a small implementation detail becomes a problem for organizations that have never heard of the function involved.
There is something humbling in that. We can design careful architectures and invest heavily in defense while still resting on code written long before our systems existed, and that does not make the investment wasted. It just means layers of protection are not the same as guarantees. A firewall cannot make everything behind it safe, an application cannot validate input that its runtime has already processed, and a flaw that went unnoticed for ten years was not necessarily harmless for ten years. It may simply have been hard to understand, which is a different thing, and one we are getting steadily better at changing.
References¶
[1] PHP Security Team. CVE-2026-109618: Remote Code Execution via out-of-memory corruption. October 21, 2026.
[2] PHP Security Team. CVE-2026-109619: Remote Code Execution via stale global state. October 21, 2026.
[3] Wiz Research. PHP2Shell: AI uncovers critical RCE vulnerabilities affecting 70% of the Internet. October 21, 2026. The 70% figure describes PHP prevalence, not confirmed exploitation.
[4] Microsoft Azure Network Security. Addressing PHP2Shell: Upgrade guidance and Azure WAF mitigations. October 21, 2026.
[5] Canadian Centre for Cyber Security. Critical vulnerabilities impacting PHP, updated October 23, 2026.
[6] CISA. Critical Vulnerabilities in PHP Require Immediate Patching. October 21, 2026.
Editorial note: This article reflects verified public information available on October 26, 2026. Demonstrated exploitability and potential exposure should not be interpreted as evidence that individual installations have been compromised. Azure WAF mitigation availability and guidance remain subject to change.