The honeypots caught something over the weekend. Adel Ka, detection and response lead at Perplexity, reported Saturday that six of his honeypots recorded 35 successful in-the-wild exploits of PHP2Shell from two different IP addresses. Root access followed 1.6 milliseconds after the exploit landed. That is not a lab. That is a production-shaped server being taken apart remotely.
The target was an Apache server running PHP 8.4.26. Marcus Hutchins called it "by far the highest quality confirmation of in-the-wild php2shell exploitation I've seen so far." Given how much bad vulnerability intelligence circulated last week, that qualifier matters.
The vulnerability in plain terms
Two CVEs, CVE-2026-109618 and CVE-2026-109619, collectively PHP2Shell. Unauthenticated remote code execution on PHP installations serving web requests. The affected surface is broad: Apache with mod_php, PHP-FPM, php-cgi, FrankenPHP, LiteSpeed. PHP itself flags mod_php as the highest risk configuration.
Patched versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. CISA published a SIGMA detection rule alongside its alert. If you cannot patch, CISA's guidance is to take the system offline. I know how that lands in a room full of availability-focused engineers. It is still the right call for anything internet-facing running mod_php.
How the exploit actually works
The public proof of concept is a single Python file, stealmybitcoin_poc.py, written by Jaydip Modhwadia. He goes by KillaBoi on GitHub. It uses only the standard library, needs at most 20 HTTP requests, and builds each request itself while checking every leak before moving forward.
The sequence is worth understanding because it explains why naive WAF rules will miss it. It starts with a 32 KB upload script that issues a multipart upload-progress request. That request makes PHP consume far more memory than the wire size suggests, which is deliberate: the goal is to reach PHP's 128 MiB limit during session upload-progress parsing. When PHP runs out of memory partway through, it leaves stale state in a worker that gets reused. Shape the follow-up requests correctly and bytes leak out through session cookies. Modhwadia reads those bytes as a heap address and a PHP code address.
Both addresses have to come from the same run. The OS randomizes code and heap placement independently, so a heap leak from one attempt paired with a code leak from another will fail. With both addresses in hand, a final request drops a forged PHP structure where cleanup will find it, and cleanup calls zif_system. Command execution follows, with the response coming back over a connection that is still open.
He also had to work around Nginx's 1 MB request limit. Alon Gal at Hudson Rock called the whole thing impressive, and he is right. This is not a spray-and-pray exploit. It is a careful piece of memory manipulation.
The CTF that settled the argument
James Pavur ran a capture-the-flag contest called Steal My Bitcoin. The rules were simple: get one live PHP callback against the challenge, start to finish, in a single standard-library Python file, and take the wallet.
Modhwadia won it Sunday at 15:47 UTC with 0.02511679 BTC, roughly $2,000. There was a hiccup where he handed over the parent wallet key instead of the sub-wallet key, which Pavur fixed before paying out. The server had been live for about 19 hours.
The contest had a rough start. The site initially resolved to a static HTML page, and the dynamic PHP pages were not obviously reachable. Pavur changed the URL around 13:00 UTC. Less than four hours later the wallet was gone.
Matthew Hickey's take, posted publicly: the exploit adapts easily to most environments, needs a few server-side conditions, and those conditions exist in production. "Game over, patch this urgently."
The retractions were the right call
Sophos and Palo Alto Networks both reported working exploits last week and then retracted the claims. That is the correct behavior and it deserves saying out loud, because the alternative is worse. Daniel Card put it well: "Being first isn't a good goal when it comes to vulnerability intelligence, being accurate probably is."
Ian Thornton-Trump at Inversion6 noted that when patches dropped Wednesday, the people who succeeded first were expert vulnerability researchers. "Turns out there was a bit more to it than just smashing AI at it." That is a useful counterpoint to the AI-assisted-attack narrative floating around. Language models help with reverse-engineering patches. They do not hand you a reliable ASLR bypass on a randomized heap.
A proof of concept also is not a skeleton key. Card pointed out that architecture diversity, WAFs, IDS/IPS, EDR and actual monitoring all raise the cost of exploitation per target. The CTF box was built to be exploitable. Your estate probably is not.
There is a third PHP bug now
On Friday, PHP disclosed CVE-2026-115560, another RCE. The project says exploitation needs "an unusual combination of configuration and application code" that it considers unlikely in practice, so no emergency fix. It will land in the next regular release. Interim mitigation: set session.upload_progress.enabled=0. That happens to be one of the mitigations PHP recommended for CVE-2026-109619. If you already applied it, leave it alone.
Brotli is not just a Node.js problem
Separately, the Node.js Brotli bug nicknamed node2shell turned out to live in the Brotli library itself. CVE-2026-111497, CVSS4 9.2, is a buffer overflow in Google's Brotli decoder across versions 1.0.3 through 1.2.0. Crafted Brotli streams can cause memory errors, read sensitive memory, trigger denial of service, or inject and execute code. The internet has already named it Broccoli2Shell and given it a domain.
Fixed Brotli versions are 1.0.10, 1.1.1 and 1.2.1. Node.js patched in 26.12.1, 24.22.1 and 22.23.4. Python's brotli package is fixed at 1.2.1, and brotlicffi at 1.2.1.0. Anything binding Brotli through C is exposed: Java JNI wrappers, PHP and Ruby extensions, Go cgo bindings. Bun is affected too. If your dependency tree pulls in a Brotli binding without pinning the version, audit it this week.
What to do
Patch PHP to 8.5.12, 8.4.27, 8.3.36 or 8.2.35. Set session.upload_progress.enabled=0 where you can, and treat it as a permanent setting rather than a temporary one until you have confirmed it does not break your upload flows. Deploy CISA's SIGMA rule. Hunt for multipart upload-progress requests that push workers toward the memory limit followed by cookie-based leaks in the same session.
For Brotli, find every place the decoder sits behind a network boundary and check the version. Node.js applications are the obvious ones. The Python service that decompresses a request body is the one people forget.