DEV Community

ServBay
ServBay

Posted on

What PHP2Shell Taught Me About Verified Code and the Runtime Underneath

I spent the PHP2Shell weekend reading postmortems instead of shipping features, and the part that stuck with me was not the exploit. It was the gap between how we test code and how we actually run it.

Government sites across several European countries went offline at roughly the same time. In the Netherlands the digital identity system and personal government portals were shut down in an emergency. Major platforms in Japan posted maintenance notices one after another. Even a sushi order ran into it, with customers told the mobile mini-program was paused for a system upgrade. A cabinet official posted late at night urging everyone responsible for ops to respond immediately, and added that if a site could not be patched in time, temporarily pulling the network cable and taking it offline was still a necessary way to reduce risk.

The two flaws the security community calls PHP2Shell are tracked as CVE-2026-109618 and CVE-2026-109619, with a CVSS score of 9.2. Under default configurations, an unauthenticated external request can trigger a low-level exception through specific parameters and gain code execution on the server.

Around the disclosure, technical discussion about AI-assisted code verification was also running hot. OpenAI announced that its latest frontier models had helped find related vulnerabilities and billed it as a major win for its security program. In open-source and academic circles, Anthropic's newly released research drew plenty of admiration, with the paper claiming AI can already generate formal proofs for open-source codebases that pass independent checkers. The popular reading was that AI is wiping out human coding mistakes at unprecedented speed, and an age of essentially bug-free code is within reach.

Then came the split screen. On one side, Silicon Valley lab code that math supposedly proves airtight. On the other, production sites around the world yanking cables to save themselves.

The lesson I took is narrow and useful. Even when application code passes strict unit tests, static analysis, or formal proofs, a flaw in the interpreter that runs it can still take the whole system down.

01

The discovery story is less about models than the retelling suggested

The trail runs through 2004 and 2020

The person who first found the bug and produced a working proof of concept was independent researcher Ryan Austin. As early as early August he had already posted clues that included a discovery hash. In his later write-up he stressed that the initial find did not depend on any special tooling framework, and certainly not on some magical model ordinary people cannot touch.

What decided the find was decades of accumulated knowledge of the target's underbelly. The trail led back to a memory-limit defect publicly recorded in 2004, and to a 2020 issue report that never got enough attention. Those historical records set the main direction of the hunt.

Where the model actually paid off

AI's main job in this process was speeding up verification. Turning a theoretical memory flaw into a stable, reliable PoC often takes weeks of iterative debugging in traditional security research. With LLM-assisted analysis, that verification cycle shrank to a matter of days.

That division of labour reflects where the tech sits today. Research direction and intuition still rely on human understanding of system history and code context. Models mainly raise the throughput of retrieval and verification.

One caveat I keep in mind. As the bar for building exploits drops, defenders feel more time pressure. Once a patch is public, reverse-engineering the fix and extracting attack signatures gets much cheaper, and the window for operators to find and upgrade systems gets tighter.

Both CVEs sit below the application layer

What the two bugs actually do

CVE-2026-109618 ties to abnormal memory handling. When a request hits out-of-memory, the runtime uses a longjmp-style interrupt to abort. If that happens before some internal data is fully initialized, later cleanup can treat uninitialized data as valid objects, which opens a path to run attacker-chosen instructions.

02

CVE-2026-109619 involves leftover state after variable-scope teardown. Memory not fully cleaned from a prior request gets reused by a later one, with the risk of data leaks or diverted control flow.

Along the request path, a malicious payload can already affect the interpreter during web-server and FastCGI parsing, before any application script is read and run. Because the defect fires before app code executes, no amount of careful business logic can intercept the low-level runtime failure.

Why the scope matters more than the payload

Most website compromises in the past came from upper-layer application bugs, missing input sanitization that let malicious SQL into the database, or upload handlers that did not check extensions and let a trojan land on the server. What made this incident so widely watched is that the trigger is not in application business logic at all. It sits in the PHP interpreter's kernel.

W3Techs statistics say that among sites with a known server-side language, about 69% still run PHP. In a typical setup the web server takes the request and hands it to PHP-FPM over FastCGI, then a C-implemented interpreter handles memory allocation, request parsing, and code execution. Both critical bugs sit deep in that C implementation. A site that only prints Hello World is still exposed.

What formal proofs can and cannot say

Three tools, three scopes

Tool What it checks
Unit tests Expected outputs on fixed examples
Property tests Random inputs thrown at edge cases
Formal proofs A function meets its spec, shown with mathematical logic

All three target logical consistency in application code. Their reasoning assumes an ideal world, where hardware, OS, standard libraries, and the language interpreter all behave to spec with no surprise side effects.

Proof-checked is not deployment-verified

Anthropic's technical appendix explores using large models to help generate proofs and shows how mathematical specs can rigorously probe logical boundaries. On Hacker News many developers praised the progress in deterministic verification while also focusing on where it stops scaling for large production systems.

03

A formal proof can show that a payment debit function has no algorithmic hole and still say nothing about whether the PHP interpreter will overrun memory while allocating. Correctness at the app layer does not replace security review of the runtime. Security people keep repeating the same sentence, proof-checked is not deployment-verified.

Production is not the ideal world those proofs assume. As long as the C runtime has cracks, a green proof at the code layer is not the same as production safety. Treating a clean logical check as evidence that the whole system is solid is one of the biggest blind spots teams carry right now.

The patching gap nobody budgets for

The version spread

Public stats show about 65% of remaining PHP sites run on the 8.x branch. Of the rest, about 27% still use PHP 7.x, and about 7% run PHP 5.x, branches that hit end-of-life years ago.

Official fixes for this issue only cover the active 8.2, 8.3, 8.4, and 8.5 branches. That leaves roughly a third of the world's PHP sites unable to get an official security patch pushed to them.

Why upgrading is not a one-liner

For teams keeping those systems online, jumping major versions often means expensive rework. Old projects lean on deprecated syntax or stale extensions, and bumping the interpreter cold can break the app outright. Inside a tight window, a full rewrite is not realistic and easily imports new risk.

Telling those companies to tear everything down and rebuild, or hoping AI will overnight rewrite hundreds of thousands of lines of legacy code without breakage, is pure fantasy. The more workable engineering move is to keep major-version compatibility while back-porting the necessary patches into the runtime.

04

Where runtime-level maintenance fits

Back-porting into older branches

Handling low-level security updates like this means extending attention from code tests alone to a full evidence chain covering runtime, service config, and process state.

On the development and multi-version maintenance side, tools such as ServBay, an AI development management tool that keeps several runtimes available side by side, proved especially useful in this incident. For these vulnerabilities, ServBay not only synced fix packages for the mainstream PHP 8.x branches right away, but also back-ported security patches for officially unmaintained older PHP, including 7.x and even the 5.6 line.

That gives teams on legacy systems a pragmatic buffer. Developers do not have to risk rewriting the app. They can stay on their existing major branch and update to an interpreter build that already includes the fix, which clears the runtime hole.

Keeping the two layers apart

Paired with per-project PHP version binding, different sites can run in isolated environments matched to their dependencies. New projects take the newest PHP, old systems stay on a patched older build, and no single global environment steps on another. While developers use AI to write code or run tests, the infrastructure underneath offers a version-clear, config-transparent runtime base, so upper-layer logic verification and lower-layer environment security each stay in their lane.

What changed in my own checklist

  • When a test suite goes green, I ask which runtime it ran on and whether that runtime is still on a supported branch.
  • When I read a proof or a static analysis pass, I check whether it covers the language interpreter or stops at my own code.
  • For older services, I look for patched builds of end-of-life branches before I put a rewrite on the roadmap.

AI in security really does speed up discovery, assisted analysis, and logical proof. Tooling progress does not rewrite the basics of software engineering. Logical consistency in application code still rests on a stable interpreter underneath. For defects like CVE-2026-109618 that live in the basement, app-layer tests or math proofs alone are not enough. Facing the limits of verification, pairing them with controlled, reproducible infrastructure, and applying security patches promptly to both active and legacy systems, is the solid path to keeping services running.

Top comments (0)