PHP2Shell: what QA (and AI) could have changed
Two critical PHP flaws, spotted as early as 2024 but never fixed. This is not only a security story: it is the cost of QA that checks features and neglects non-functional quality. And a lesson in what AI really changes.
On October 21, 2026, PHP shipped emergency fixes for two critical vulnerabilities, quickly nicknamed PHP2Shell (CVE-2026-109618 and CVE-2026-109619). A single unauthenticated request could be enough to run code on the server. Given how much of the web still runs on PHP, it is no surprise that the US cybersecurity agency CISA urged everyone to patch immediately, starting with internet-facing servers.
You could file this story under “security”. I think it is also, and mostly, a story about software quality.
What happened, in plain words
Both flaws affect how PHP handles memory when something goes wrong:
- The first happens when a request hits the allowed memory limit (
memory_limit) at exactly the wrong moment. PHP leaves half-built data behind, then later treats it as valid. - The second happens after a fatal error: PHP keeps a pointer to memory that belonged to a previous request and has already been freed. One way to get there goes through file upload progress tracking, an option that is on by default.
In other words, no “normal” scenario triggered these bugs. You had to push PHP into its failure paths. The detailed analysis by Datadog Security Labs shows that one well-crafted POST request was enough.
According to Amazon’s teams, exploitation attempts were seen as early as October 17, four days before the fixes came out.
The detail that changes everything: the problem was known in 2024
In 2024, PHP went through a security audit carried out by Quarkslab and funded by Germany’s Sovereign Tech Agency. While fixing one of the reported issues, a PHP core developer spotted the first signs of the memory bug behind the first flaw.
It was not fixed. The reasons, as given by the PHP Foundation itself:
- an exploit was considered possible, but nobody found a concrete attack path;
- the candidate fixes risked breaking compatibility, opening new denial-of-service risks, or forcing many extensions to change;
- the team could not agree on the right approach.
The Foundation calls it a “structural failure”, caused both by its decision process and by its lack of resources. Its executive director told Reuters the foundation has one full-time employee and fourteen contractors, most of them part time, and that proposed code changes nearly doubled in a year while the number of contributors did not. Even the 2024 audit had focused on the most critical components because of budget constraints.
Where QA could have made a difference
People often picture QA as the team that checks “the button does what it should”. That is exactly the problem. In most teams I meet, most of the testing effort goes into functional testing: user journeys, business rules, visible regressions. Non-functional quality (security, robustness, performance, behaviour under stress) comes later, when there is time left. There rarely is.
PHP2Shell shows the price of that imbalance. Here are four QA practices aimed at exactly this kind of defect.
1. Test the failure paths, not just the happy paths
Both flaws appear when something fails: a memory allocation, a fatal error. A robust test strategy plans to trigger those failures on purpose:
- fault injection: simulate full memory, a full disk, a dependency that stops answering, and check that the system stays in a healthy state;
- boundary tests: send requests that take the application right up to
memory_limit, the maximum upload size, the execution time limit; - memory error detectors (sanitizers) wired into CI, which flag freed memory being reused even when the test “passes”.
None of this is exotic. It is classic non-functional testing that rarely gets funded.
2. Treat a known defect as a risk, not a ticket
To me, this is the main lesson. The bug was found. It was set aside because nobody yet knew how to exploit it.
In QA, we rate a defect on two axes: likelihood and impact. A memory corruption bug in the engine of a language that runs a large part of the web has the highest possible impact. “We found no exploit” does not mean “there is none”. It means “we have not looked hard enough yet”. Good defect management would have set a deadline, an owner, and a mitigation while waiting for the proper fix.
3. Think like an attacker from the design stage
The second flaw went through a feature that is on by default and reachable without logging in. Threat modelling (asking, for each feature, “how would a malicious user use this?”) is a practice QA can bring into backlog refinement. Any surface reachable without logging in deserves abuse tests, not just usage tests.
4. Monitor what you do not control
For a team testing a website, PHP is a dependency, just like a library or a hosting provider. Keeping an inventory of those dependencies and their versions, getting alerts when flaws are published, and checking in production that the fixed version is actually deployed: that is quality work too. On release day, the teams that could tell within minutes which servers ran which PHP version had a head start.
What AI changed (and what it did not)
It was AI-assisted audits that surfaced these flaws in 2026. Several research teams (Calif, Wiz, OpenAI) reported the same root cause over the summer, as part of an open source audit programme. The bug humans had glimpsed in 2024 without being able to exploit it, researchers equipped with AI turned into a concrete attack. And that proof is what unblocked the fix.
Two takeaways for QA:
- AI lowers the cost of exploration. Exploring thousands of failure paths, imagining abuse scenarios, reviewing code for dangerous patterns: what was too expensive for a small team becomes affordable. That is good news for non-functional testing, long sacrificed for lack of time.
- AI does not replace expertise. The Calif team said it plainly: no model finds “all bugs” with a simple prompt, and most of the work relied on knowledge the researchers already had. AI amplifies a testing approach. It does not invent one.
And there is a flip side: the same tools are available to attackers. If AI makes exploration cheaper for defenders, it makes it cheaper for those looking for flaws to exploit too. The time between “a bug exists” and “someone exploits it” will keep shrinking. One more reason not to let known defects sleep.
How to anticipate, in practice
If you own quality for a website or a web application, here is where I would start:
- Reserve a fixed share of testing effort for non-functional quality, written into the test strategy and into every sprint, instead of waiting for “when we have time”.
- Add failure scenarios to your automated tests: size, memory and time limits, deliberate server errors. Check that the application stays healthy, not just that it shows an error message.
- Plug a security scanner into CI that attacks the application deployed in a test environment, plus a tool that watches for flaws in your dependencies.
- Use AI to widen coverage: generate abuse cases from user stories, have sensitive code areas reviewed, propose boundary test data. Then have a human validate.
- Review defect management: every security or robustness defect gets a deadline and an owner, even with no known exploit.
- Keep an inventory of versions in production, so that on the day a flaw comes out, you know what is exposed.
In short
The PHP2Shell flaws are not just a technical accident. They are partly the result of chronic underinvestment in quality for a project a large part of the web depends on, and the PHP Foundation admits as much. That is not the whole story, though. The C language, where this kind of memory error is easy to make, and the need not to break millions of existing sites also played a part.
For us QA practitioners, the message is simple: security and robustness are quality requirements, just like a form that works. AI finally gives us the means to test them seriously. It is up to us to get the time and budget to do it.


