Blog

PHP2Shell: AI Helped Find Two Critical Bugs in PHP Itself. Patch Today.

Two CVEs in the PHP engine allow unauthenticated remote code execution. The fixes shipped October 21, exploits are getting better, and most PHP installs still aren't patched. Here's how to check yours.

Caleb Dorris 21 October 2026, 8:06 PM Updated 28 October 2026 18 min

PHP2SHELL title card on a dark screen of source code, with the line Are you secure?

Update, October 28, 2026

The federal deadline is tomorrow, and most PHP servers that show their version still aren't patched

CISA's deadline for federal agencies to fix both PHP2Shell bugs is tomorrow, Thursday, October 29. That deadline only binds federal civilian agencies, but it's a sensible one for everyone else too. The details are in yesterday's update below.

How many servers are still exposed? You may have seen "76% of servers are still unpatched" going around. I couldn't find who measured that exact number or how, so treat it as a reported figure, not a fact. The best primary data I've found is Censys's public PHP2Shell tracker. As of 6:15 AM Central on October 28, it counted about 32.7 million internet-facing PHP services reporting an affected version and about 11.8 million reporting a fixed one. That's 73.4% still affected, down from about 78% on October 23. Patching has slowed a lot. Censys saw more than 4 million services move to a fixed version on each of October 22 and 23, and only a few hundred thousand a day since.

Two caveats. Censys can only count services that reveal their PHP version, and it couldn't see the version on about 64.8 million more. A distro package can also carry the fix under an older version number. Still, about 24.2 million of the affected services run PHP 8.1 or older, which has no official fixed release. For those, the fix is an upgrade.

One more KEV entry. On October 27, CISA added CVE-2026-111497, the Brotli bug that PHP's rebuilt Windows packages fix, to the same catalog, with a November 10 federal deadline. Brotli is a compression library used well beyond PHP, so update it in your OS packages too, not just PHP on Windows.

If you're not sure which of your servers are in that 73%, get a free quote and I'll check them with you.

Update, October 27, 2026

CISA now lists PHP2Shell as exploited in the wild. Patch this week.

On October 26, CISA added both PHP2Shell bugs, CVE-2026-109618 (OOM corruption) and CVE-2026-109619 (stale request state), to its Known Exploited Vulnerabilities catalog. CISA only adds a bug there when it has reliable evidence of active exploitation in the wild. Federal civilian agencies have until Thursday, October 29 to fix them, and CISA flagged both as requiring forensic triage under its patching directive. It lists ransomware use as unknown. When I wrote my October 23 update, neither CVE was on that list. Now both are.

The fix hasn't changed. PHP's October 21 releases are still the ones to get: 8.5.12, 8.4.27, 8.3.36 or 8.2.35, which covers every supported branch. There's no fix for 8.1 or older. Those branches are end of life, so the real fix is an upgrade to a supported version. If you run PHP on Windows, PHP also posted rebuilt Windows packages on October 26 (8.5.12-1, 8.4.27-1, 8.3.36-1 and 8.2.35-1) that fix a separate Brotli bug, CVE-2026-111497. The PHP version number stays the same.

Patch now:

  1. Check your version. Run php -v, and check what the web server runs too (php-fpm -v), because they can differ.
  2. Upgrade to the fixed release for your branch, or a distro package whose changelog lists both CVEs.
  3. Restart PHP-FPM, Apache or LiteSpeed, and any long-running workers. Old processes keep running the old code until they restart.
  4. On shared hosting, check the PHP version in your control panel (in cPanel, MultiPHP Manager) or in WordPress under Tools > Site Health > Info > Server. If it's below the fixed release, ask your host whether it's patched for CVE-2026-109618 and CVE-2026-109619.
  5. Verify. Run the check again after the restart. The full five-minute version is in the patch check below.

If you have servers you're not sure about, get a free quote and I'll check them with you.

Update, October 25, 2026

Four days in: a better exploit, a botnet, a public challenge lost, and slow patching

Here's what changed since my last update, and what didn't.

The exploits got better. VulnCheck built an exploit for CVE-2026-109618 that gets past ASLR on x86-64 with display_errors=off, the setup PHP recommends for production. As of October 23 it covers PHP 8.4.10 through 8.4.26 and 8.5.10 and 8.5.11 on mod_php, including a default WordPress Docker install. It's only available to VulnCheck's paying customers, and VulnCheck says it doesn't know of any other public or commercial exploit that does this across multiple PHP versions. It also expects that to change. Most of the hundreds of "PoC" repos on GitHub don't work.

Someone took the "prove PHP2Shell is hard" bitcoin. Over the weekend, Steal My Bitcoin put a small bitcoin wallet key on an internet-facing PHP 8.4.26 server — one release short of the 8.4.27 fix — and dared people to take it. LiveOverflow had looked at the challenge earlier on Sunday and described it as aiming to show PHP2Shell isn't realistically exploitable; his fingerprinting put the server at X-Powered-By: PHP/8.4.26. Later that evening he posted that @killa had solved it a few hours earlier, and that killa was confused because the wallet address was wrong — the server didn't have the right wallet. The challenge creator, James Pavur, confirmed the same: killa solved it hours earlier, and Pavur had put the root wallet WIF on the server instead of the branch WIF. The winner page at stealmybitcoin.com lists the win at 15:47 UTC on October 25, about 19 hours after the challenge started, and links killa's write-up. That write-up walks a multipart PHP_SESSION_UPLOAD_PROGRESS OOM path into a forged structure that ends in zif_system against Alpine PHP 8.4.26 — the same family of bugs the October 21 releases closed. A CTF isn't a production break-in, but an unpatched 8.4.26 box on the public internet lost. If you're still on 8.4.26, or any build below the fixed releases, patch and restart. If you're still on 8.1 or older, read why you should upgrade to PHP 8.5.

A botnet tried it. Sophos says attackers tried to use PHP2Shell to install a Mirai-derived botnet it calls Potassium, per SecurityWeek. That attempt was stopped, and the malware never reached the server. Hunt.io tied the traffic it saw to an established Mirai-style botnet that recently added these bugs to how it spreads.

One reported break-in, with an asterisk. On October 23, Unit 42 added a case to its PHP2Shell threat brief: attackers hit a government entity in Europe and, within two hours of the initial exploitation, had password hashes and multi-factor secrets, set up an NTLM-relay tunnel into Active Directory, and tried for Domain Admin. Unit 42 says the follow-up work looked like an agentic AI platform at the wheel: command batches wrapped in markers like ==SHELLUSER== and ==PASSWD== so a model can parse the output, short tasks one after another, and quick pivots after failures. The brief doesn't spell out which bug got them in, and it doesn't name the government. Earlier that same day, SecurityWeek reported that no successful compromise had been confirmed, citing Hunt.io, Wordfence and Sophos, and that as of that morning CISA hadn't added either CVE to its Known Exploited Vulnerabilities catalog. So: lots of attempts, one vendor-reported intrusion, no confirmed wave.

Patching is slow. Qualys looked at anonymized data from more than 10,000 customers. Of 156 million PHP installations with a known version, only 199,000 were patched in the first 12 hours, and 581,000 in the first 24. About 110 million (71%) run PHP 5.4 through 8.1, which are past end of life, and Qualys says most of those will never get a vendor patch. Another 44 million run supported branches where the fix exists but hadn't been applied.

The third CVE still has no fix. PHP hasn't shipped a release for CVE-2026-115560 yet. Its advice is still to set session.upload_progress.enabled=0 until the next regular bugfix release. If you set it on October 21, leave it.

My read hasn't changed: exploiting this on a hardened server is hard, and it's getting easier. Patch and restart.

PHP2Shell patch check

Is my site patched? Five minutes to find out

  1. Check the version. On the server, run php -v. You want 8.5.12, 8.4.27, 8.3.36, 8.2.35 or newer on your branch.
  2. Check what the web server actually runs. The CLI and PHP-FPM can be different builds. Run php-fpm -v (on Debian or Ubuntu it's usually php-fpm8.3 -v or similar), or drop a one-line <?php echo PHP_VERSION; file in the webroot, load it once, and delete it right away.
  3. Distro packages can hide the fix. Backported fixes may keep the older version number, so a lower number isn't proof you're exposed. Search the package changelog for the CVE: apt changelog php8.3-fpm | grep CVE-2026-109618 on Debian or Ubuntu, rpm -q --changelog php-fpm | grep CVE-2026-109618 on RHEL-family systems.
  4. Check the stopgap setting. Run php -i | grep session.upload_progress and look for session.upload_progress.enabled => Off => Off. Check the FPM side too, because the pool config can override php.ini. If it's on, set it in php.ini or the FPM pool, not ini_set() or .user.ini, and restart every worker.
  5. On shared hosting or WordPress, you probably can't run commands. In WordPress, go to Tools > Site Health > Info > Server. In cPanel, check MultiPHP Manager. If the version is below the fixed release, open a ticket and ask your host directly whether it's patched for CVE-2026-109618 and CVE-2026-109619 and whether session.upload_progress.enabled is off.

If any box comes back old, update it, restart PHP-FPM, Apache and any long-running workers, then run the check again. If you'd like a second set of eyes on your servers, get a free quote.

Update, October 23, 2026

A new PHP advisory, and where exploitation actually stands

One more setting. PHP published an informational advisory today for a separate, unlikely RCE path, CVE-2026-115560. It recommends session.upload_progress.enabled=0 until the next regular bugfix release. The October 21 releases don't fix this one, and PHP says the full advisory comes with that next release. It only happens when an app sets session.serialize_handler to php_binary or php_serialize and then switches it to php with ini_set() before session_start(), with upload progress still on. If you already turned upload progress off on October 21, you're covered. If not, set it in php.ini or the FPM pool, not with ini_set() or .user.ini, and restart every worker.

Exploitation: lots of attempts, no confirmed break-ins yet. Amazon saw its first attempt on October 17 and reports broad exploitation attempts since. watchTowr's FAQ, updated today, lists exploitation "in the wild" based on its honeypot network. Sophos reported a successful compromise on October 22, then corrected itself the same day: its telemetry showed scanning and failed attempts, with no evidence of a successful compromise, and it says building a working exploit is proving hard. As of October 22, no successful break-in had been publicly confirmed, and neither CVE was on CISA's Known Exploited Vulnerabilities catalog. That's a window, not a reason to wait. (See the October 25 and October 27 updates above for what changed.)

Update, October 22, 2026

Attacks started before the patch. Update now.

A day after I wrote this, the picture is worse. SecurityWeek reports that Amazon saw the first exploitation attempt on October 17, while the bugs were still under embargo. Details leaked online on October 20, and more proof-of-concept exploits showed up within hours of the release. Amazon says PHP2Shell is now being broadly exploited. watchTowr says the exploits it has seen so far don't actually get code execution. I wouldn't bet a server on that lasting.

CISA told organizations to patch immediately, prioritize internet-facing systems, and take PHP systems offline if they can't update or are still on 8.1 or older. CERT-EU rates both CVEs 9.2, notes that proofs of concept are circulating, and says to isolate internet-facing apps you can't patch yet.

Nothing about the fix has changed. The fixed releases are still 8.5.12, 8.4.27, 8.3.36 and 8.2.35, and php.net says there's no complete mitigation. Every PHP setup that serves web requests is affected: PHP-FPM, mod_php, php-cgi, FrankenPHP and LiteSpeed. mod_php is the highest risk.

What to do now

Five steps, in order

  1. Check your version. Run php -v on every box, and check what each web SAPI is actually running. The CLI and FPM aren't always the same build.
  2. Upgrade to the fixed release for your branch: 8.5.12, 8.4.27, 8.3.36 or 8.2.35. Then restart PHP-FPM, Apache and any long-running workers.
  3. If you use remirepo, don't wait on it. PHP says those packages weren't updated on release day. Switch to your distro's packages or build from source.
  4. Plan your way off 8.1 and older. The PHP project has no official fix for them. Some distros and LTS vendors have backports, and PHP posted minimal source backports for 7.4, 8.0 and 8.1. Treat those as a bridge, not a plan.
  5. Turn on WAF rules as a stopgap. Cloudflare enabled php2shell rules by default on every plan with the WAF deployed, Free included. A WAF is not a substitute for patching. Cloudflare says to patch anyway, and so do I.

Most PHP security news I read is about a plugin, a framework, or somebody's custom code. This one is different. The bugs are in PHP itself, in the engine under WordPress, Drupal, Laravel and whatever admin panel you inherited three years ago.

The PHP team shipped emergency releases on October 21, 2026, for two critical vulnerabilities grouped under the name PHP2Shell. The researchers who found them say frontier AI models were a big part of how they did it. I spend my days in PHP, so here's what happened, kept as close to the sources as I can, and what I'm doing about it.

What the bugs are

There are two CVEs. Both are rated critical, CVSS v4.0 9.2, in PHP's own advisories.

CVE-2026-109618, RCE via OOM corruption (GHSA-w8hw-w9fx-j79w). PHP handles out-of-memory errors by jumping out of the current context. Sometimes that happens while request data is only partly set up. Request shutdown can then treat the half-built data as valid.

CVE-2026-109619, RCE via stale global state (GHSA-6wmf-4rv5-p3rp). Internal state that belongs to one request can outlive the memory behind it. The next request handled by the same worker can use that stale state before normal setup resets it. That's a use-after-free.

The researchers demonstrated unauthenticated remote code execution against WordPress, Drupal and Joomla. Calif says it also confirmed pre-auth RCE on MediaWiki, Magento, phpMyAdmin, Nextcloud, Laravel, Symfony and WHMCS, among others. Wiz also showed a separate memory leak where a crafted request can pull chunks of worker memory back out in a PHPSESSID cookie. That memory can hold passwords, tokens and API keys from other requests.

The important part: the flaw isn't in those apps. Per Wiz, even a plain "Hello World" script on an affected PHP version can be vulnerable. PHP lists PHP-FPM, Apache mod_php, php-cgi, FrankenPHP and LiteSpeed as at risk, and it rates mod_php highest. On mod_php, one unauthenticated request can trigger the attack before any of your code runs.

The affected code goes back a long way. Wiz and Calif both trace affected paths to PHP 5.4.

How AI found it

Three groups reported the issues.

Calif reported first, as part of OpenAI's Patch the Planet initiative. OpenAI says Calif found a critical flaw using GPT-5.6 Sol. Calif's write-up says its researcher started the audit by pointing an LLM at a known bug pattern: a memory-limit bug Stefan Esser found back in 2004, plus an out-of-memory issue from 2020 that a 2024 audit rediscovered but never proved exploitable. Calif says that initial discovery used no special harness and only models anyone could get. The model then helped turn the pattern into a reusable check, which surfaced more variants.

Wiz Research found and reported the bugs independently, using Wiz Atlas, Gemini 4 Argon and GPT-5.6 Sol. In Wiz's telling, the hard part was never spotting the weird code. It was chaining several memory bugs into a working exploit and adapting that exploit to each target's memory layout, and that's what the models made practical. OpenAI's Codex Security team is also credited.

The timeline so far

Aug 1
Calif's first findings.
Aug 11
Calif reports to the PHP security team.
Aug 19
Wiz's first finding.
Aug 24
Wiz reports.
Oct 17
First exploitation attempt seen by Amazon, while the bugs were still under embargo.
Oct 20
PHP gives advance notice of a critical release. Details leak online the same day.
Oct 21
Patched releases and public disclosure.
Oct 21
CISA urges immediate patching. Mass exploitation attempts follow the release.
Oct 22
Sophos reports, then retracts, a successful compromise. No confirmed break-ins publicly reported.
Oct 23
PHP's informational advisory on session upload progress (CVE-2026-115560).
Oct 22
VulnCheck has an exploit that gets past ASLR in PHP's recommended production setup.
Oct 23
Unit 42 reports a government intrusion with AI-driven follow-up. Qualys: 199,000 of 156M installs patched in the first 12 hours.
Oct 25
Steal My Bitcoin challenge on PHP 8.4.26 solved by @killa (~19 hours after it went up). LiveOverflow and James Pavur confirmed the solve; winner write-up uses the upload-progress OOM path.
Oct 26
CISA adds both CVEs to its Known Exploited Vulnerabilities catalog, with an October 29 deadline for federal agencies.
Oct 27
CISA adds the Brotli bug fixed in PHP's Windows rebuilds (CVE-2026-111497) to its KEV catalog, with a November 10 federal deadline.
Oct 28
Censys's tracker shows 73.4% of PHP services that reveal their version still on an affected release (32.7M affected, 11.8M fixed).

Distros and LTS vendors got the patches early.

What PHP devs should do

Update PHP, then restart it.

Fixed versions

8.5
8.5.12
8.4
8.4.27
8.3
8.3.36
8.2
8.2.35

The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Distros and LTS vendors got the patches early, so check your vendor's packages first. The volunteer-run remirepo packages weren't updated on release day, and PHP says to switch to distro packages or build from source instead. After you upgrade, restart Apache, PHP-FPM and any long-running workers. Updating WordPress or running composer update does nothing here. This is the runtime, not your app.

Check every runtime, not just the main one. The staging box, the cPanel account running an old client site, the container image nobody has rebuilt since spring. Run php -v on all of them, and check what each web SAPI is actually serving.

If you're on 8.1 or older, you're off the main road. The PHP project doesn't support those versions anymore. Some distros and LTS vendors have backports. Given how bad this is, the PHP security team also put out minimal source backports for 7.4, 8.0 and 8.1, for people who build from source. Also on the calendar: 8.2 hits end of life on December 31, 2026.

If you can't patch today

Reduce exposure

None of these settings fix the bugs. They block some known paths while you schedule the real fix. Set them in php.ini, the FPM pool or the server config, not with ini_set() or .user.ini, and restart every worker:

  • session.upload_progress.enabled=0. Most apps don't use it, and PHP suggests leaving it off even after you patch.
  • fatal_error_backtraces=0 on PHP 8.5.
  • memory_limit=-1, enforced so apps can't override it, with a container or cgroup limit as the backstop. Don't use RLIMIT_AS or RLIMIT_DATA. PHP warns those can trigger the vulnerable path.
  • A lower post_max_size, and limits on request size and multipart parts at the proxy.

Cloudflare says its php2shell WAF rules are on by default for every plan with the WAF deployed, Free included. Treat that as a speed bump, not a fix. If you can't patch or add these settings, CISA and CERT-EU both say to take the system offline or isolate it until you can.

Look for signs. Wiz suggests watching for odd multipart requests followed by memory-limit errors or bursts of HTTP 500s, oversized or malformed PHPSESSID cookies, and workers crashing or restarting for no clear reason. CISA adds PHP worker processes launching shells or downloading tools. A clean log doesn't prove you're safe. These paths run below the app, so there may be little to see.

What the governments and WordPress are saying

  • NSA: inventory everything that relies on PHP, replace end-of-life versions, and take a system offline if an immediate update isn't feasible.
  • UK NCSC: find every PHP install, not only old ones, and patch internet-facing systems first. If you can't update or take a system offline right away, check it for signs of compromise and backdoors before you upgrade or apply mitigations. Patching doesn't remove a backdoor that's already there.
  • WordPress.org: no WordPress update is needed for PHP2Shell. It's a PHP fix from your host or server. You can see your PHP version under Tools > Site Health > Info > Server. Separately, WordPress 7.1.3 (October 6) has its own seven security fixes, so make sure you have that too.

Website security checklist for October 2026

If you look after a WordPress or PHP site and just want the list, here's the order I'm working in:

  1. Find every PHP version you run, including staging and old client sites.
  2. Get onto a fixed release (8.5.12, 8.4.27, 8.3.36 or 8.2.35, or a vendor backport) and restart PHP-FPM, Apache and workers.
  3. On 8.1 or older, make a plan today. CISA's advice for systems you can't update is to take them offline.
  4. Set session.upload_progress.enabled=0 in php.ini or the FPM pool, and restart. It covers both the October 21 path and today's CVE-2026-115560.
  5. Turn on WAF rules as a stopgap, not a fix.
  6. Update WordPress to 7.1.3 and your plugins.
  7. Look back to October 17. Amazon says to investigate from then, including POST requests with PHP_SESSION_UPLOAD_PROGRESS in a form upload. Sophos lists the user agent php2shell-scan/109618 and a file at /tmp/nfs as signs of exploit traffic. Also check for admin accounts or plugins you didn't add. If anything looks off, my guide to auditing a WordPress site after a plugin compromise walks through what to check next.
  8. Confirm you have a backup you can actually restore, stored somewhere other than the server.

The short version

Upgrade PHP, restart it, and check the servers you forgot you had.

I'm working through mine today.

Sources

Need a web developer in Nashville?

PHP, WordPress and Next.js. Free quote, and I usually reply within a couple of hours.

caleb@calebdorris.com and call or text (615) 914-1073

All posts