A $500 Bitcoin Bounty Fell to PHP2Shell in 19 Hours, and the Organizer's Logs Show What It Took
Security / analysis
A $500 Bitcoin Bounty Fell to PHP2Shell in 19 Hours, and the Organizer's Logs Show What It Took
James Pavur's post-mortem counts 13 million requests, 2,075 PHP crashes and one working exploit against an unpatched PHP 8.4.26 server, and says an LLM did not make the attack easy.
An unauthenticated remote attacker (against PHP 8.4.26 or any release before the Oct. 21 fixes, behind a standard web worker setup) can run commands on the server, and a public challenge showed it can be done in under a day. Security researcher James Pavur put a roughly $500 bitcoin wallet on a $2.50-a-month virtual server running unpatched PHP 8.4.26, and one entrant took it 19.28 hours after launch.
Pavur described the run in a post dated Oct. 30. His aim was to learn whether PHP2Shell, the name given to two PHP flaws, could be exploited outside a lab, and whether relatively unskilled attackers using AI could manage it. He answers "Yes" to the first and "Not yet" to the second.
What PHP fixed on Oct. 21
The PHP project shipped 8.5.12, 8.4.27, 8.3.36 and 8.2.35 on Oct. 21. Each release note calls the update a security release for two critical vulnerabilities that "can allow unauthenticated remote code execution", and says "there are no complete mitigations, updating is the only safe choice."
CVE-2026-109618, a tracking number for one specific flaw, covers out-of-memory handling that can interrupt PHP while request data is only partly initialized, so cleanup treats invalid data as valid. CVE-2026-109619 covers request state that survives after its memory is freed, and a later request on the same worker can use it. We covered which PHP branches still have no fix in an earlier piece. PHP 8.2 reaches end of life on Dec. 31, 2026.
The challenge timeline, in UTC
Pavur's setup was a Debian server with one virtual CPU, 500 MB of memory and a 10 GB disk, running a basic PHP site built with Codex behind an nginx proxy. The key sat in a file readable only by a print-flag binary, so an attacker needed code execution, not just a file read.
| When (UTC) | Event |
|---|---|
| Oct. 24, 20:30 | Timer starts, Hacker News post goes live |
| Oct. 25, 01:14 | First recorded PHP crash (SIGSEGV) |
| Oct. 25, 12:59 | Pavur adds php-version.php |
| Oct. 25, 15:44 | Pavur adds ping.php |
| Oct. 25, 15:47 | A PHP process runs print-flag, the winning exploit |
| Oct. 25, 22:38 | Solve announced on Hacker News |
The last two edits matter. Pavur wrote that he added both pages to make exploitation more feasible, so the server that fell was not the static page that went up on Oct. 24. The winning run came three minutes after ping.php appeared.
What 13 million requests looked like
Pavur reports more than 13 million HTTP requests and a peak above 18,000 a minute. About 120,000 distinct IP addresses visited, about 20,000 engaged substantively, and only two triggered print-flag. One of those two exfiltrated the key.
- Distinct visitors120K IPs
- Engaged substantively20K IPs
- Triggered print-flag2 IPs
- Exfiltrated the key1 IPs
Source: James Pavur, pavursec.com, accessed 2026-11-05
The crashes are the more useful signal for defenders. Pavur counted about 2,075 distinct PHP segmentation faults over roughly 26 hours. A crash storm on a PHP worker pool is the visible footprint of an attempt, and it appeared more than 14 hours before the first success.
The operator also lost control of his own box. Heavy traffic left the server unable to hold SSH sessions, the host suspended the site once for suspected denial of service and once for CPU use, and logs rotated about every four minutes early on, so only partial web logs survive. Reliable request logging covers about 10 hours of a run of roughly 1,600 minutes.
What the public exploit does and does not show
The winner, who goes by KillaBoi on GitHub, published a proof of concept: one standard-library Python file. Its README describes four stages, a health check, a heap address leak, a code address leak and a final callback. Oversized multipart uploads exhaust PHP's memory limit, leave stale state in a reused worker, and a forged structure reaches PHP's internal system handler during cleanup. The README names no CVE.
Its own caveats are blunt. Offsets are calibrated for PHP 8.4.26 on Alpine Linux with four workers, and the README warns that a Debian build of the same version has a different binary layout and fails the code-leak stage. In ten lab runs, seven succeeded with no retries. The repository showed about 1,000 stars and 146 forks when we read it.
That is the gap between a score and real risk. The advisory language says unauthenticated code execution, which is accurate, but the public exploit needs a matching build and a known worker count. The preconditions lower the odds of drive-by exploitation against arbitrary servers. They do not help an operator who runs exactly that stack, and a team with a binary of its own can recalibrate offsets.
Pavur's conclusion on AI is a single observer's reading of one event. He wrote that inexpert attackers with an LLM cannot yet reliably turn a difficult CVE into a working attack, while he now sees PHP2Shell as a real threat: "If you are running a PHP-based website today, you should absolutely patch PHP2Shell."
The prize was not quite what it looked like
Pavur says the exported key was a parent key in a derivation path, so the winner could not move funds from the actual wallet branch. He still treated the run as a win after matching the winner's key hash, timestamp, IP address and payload against server logs, and he moved the wallet contents on Oct. 25 at about 22:59 UTC after reviewing the write-up. A future challenge, he said, would use the full wallet recovery key.
The fix for operators is the same one PHP gave on Oct. 21: move to 8.5.12, 8.4.27, 8.3.36 or 8.2.35. The 8.2 branch stops receiving security updates on Dec. 31, 2026. Operators who also run Debian or Ubuntu should read our report on the apt root code execution flaw as well.
Sources
More in Security
- 01Apple's CVE-2024-27834, Patched in May 2024, Reaches CISA's KEV on Sekoia's Report of an APT29-Linked Web CampaignA 5.5-rated pointer authentication bypass is the second stage of an iOS 16.7 cookie-stealing chain. Sekoia's report also names a Plerdy SEO Tracker compromise and an Android chain.
- 02Plex2Shell Reached CISA's KEV 78 Days After Plex Shipped the FixFour Plex Media Server flaws chain into unauthenticated code execution on any server older than 1.43.3.10861, and attackers were installing miners within a day of a public proof of concept.
- 03CISA's BOD 26-04 Deadlines: 120 of 152 KEV Entries Since June Carry a Three-Day DeadlineThe directive that replaced BOD 22-01 on June 10 sets deadlines by exposure and exploit risk, and the catalog shows the short window is now the norm.
- 04VMware ESX Flaw Rated 2.7 Was a Zero-Day for Warp PandaCVE-2026-41709 lets an administrator skip the audit log, CrowdStrike says a China-nexus group used it in early 2026, and ESXi 7.0 has no listed fix.