PHP2Shell: Update PHP Now, Then Check That Your Web Server Is Actually Using It

#security #self-hosting #php

Two critical PHP bugs, CVE-2026-109618 and CVE-2026-109619, can let an attacker run code on your server without logging in. Fixes are out and attacks have started. Here's what's affected, how to check the PHP your web server really runs, and how to update common self-hosted setups.

Close-up of colourful source code on a dark screen, partly out of focus
Photo: Markus Spiske, CC0, via Wikimedia Commons

If you run anything built on PHP, like Nextcloud, WordPress, phpMyAdmin or a small Laravel app, today is a patch day. On 21 October, PHP released 8.5.12, 8.4.27, 8.3.36 and 8.2.35. They fix two critical bugs that, in PHP's own words, "can allow unauthenticated remote code execution". The researchers have named the pair PHP2Shell.

The releases landed at about 13:35 UTC on 21 October (around 12:35 am AEDT on 22 October). They came a day early because details had leaked, and exploitation attempts started almost straight away.

What the two bugs are

Both bugs come down to PHP mishandling memory when a request goes wrong partway through.

CVE-2026-109618, "RCE via OOM corruption". When PHP runs out of memory, it bails out of whatever it's doing. If that happens while request data is only half set up, the cleanup code later treats the junk as valid. The advisory lists several ways an attacker can force that out-of-memory condition while staying inside your normal limits, mostly with crafted multipart (file upload) requests. On Apache with mod_php, this happens "entirely before any application-specific code begins execution". Your app doesn't have to be badly written. It just has to be running on PHP.

CVE-2026-109619, "RCE via stale global state". A fatal error can leave PHP holding pointers into memory that belonged to the previous request. The next request handled by the same worker then uses that freed memory. The confirmed routes go through session upload progress, which is on by default and doesn't need your app to call session_start(), and through the fatal_error_backtraces setting on PHP 8.5. The researchers also say the bugs can leak memory, which could include passwords and API tokens from other requests.

Both are rated 9.2 on CVSS v4. PHP says every install that serves web requests is at risk, including "PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed", and that the risk is highest on mod_php. PHP-FPM is less exposed, but not safe. PHP confirmed remote code execution on FPM through the upload-progress route when the app does something that triggers a fatal error at the right moment.

The advisories don't name the oldest affected version. The researchers at Calif and Wiz say it goes back to PHP 5.4. Calif reports working pre-auth exploits against WordPress, Nextcloud, phpMyAdmin and others.

How it leaked, and where AI comes in

According to the PHP Foundation's account, the CVE numbers started circulating at about 15:00 UTC on 20 October, with a wrong score and a wrong date. That evening Calif "mistakenly published a video" of its proof of concept, and copies spread. On the morning of the 21st, Chainguard shipped fixed images more than six hours before the coordinated release. That let anyone compare the old and new binaries.

The discovery story is very much an AI story. Calif started auditing PHP on 30 July through OpenAI's Patch the Planet program and reported the first bug on 11 August. OpenAI says Calif found it using GPT-5.6 Sol. OpenAI reported the same root cause the same day, and Wiz reported more attack paths later.

The uncomfortable part is that the Foundation says the out-of-memory problem was first spotted in 2024. The fix then stalled for months because no exploit path was known and every option had trade-offs. In its own words, "the delay was a structural failure". AI didn't create this bug. It made an old one practical to exploit.

Is anyone being hit?

Attempts, yes. Confirmed successful break-ins, not that I've found.

  • watchTowr reports exploitation attempts against both CVEs. Its threat intel lead told The Record the tools were "vibecoded with the help of AI" and that "nobody has been successful to date, we don't expect that will last long."
  • Cloudflare saw attack traffic and attackers tweaking payloads to get past its rules. Its WAF protections have been on by default, including on the Free plan, since 03:02 UTC on 21 October.
  • One firm, Hadrian, told SecurityWeek it was seeing exploitation "at scale". I'd treat that as one company's view until others back it up.
  • As of about 12:30 pm AEDT on 22 October, neither CVE is in CISA's Known Exploited Vulnerabilities catalog.

Check the PHP your web server actually runs

This is the step people get wrong. php -v tells you about the command-line PHP, not necessarily the PHP that serves your site. They can be different binaries, different versions, or sit inside a container.

  • PHP-FPM: run the FPM binary itself, e.g. php-fpm8.3 -v on Debian/Ubuntu or php-fpm -v on RHEL-family systems.
  • Any setup: a temporary phpinfo() page served through the web server shows exactly what's handling requests. Delete it afterwards.
  • Docker: docker exec <container> php -v checks the PHP inside the container, which is the one that matters.
  • Ubuntu and Red Hat backport fixes, so the version number may not change. Ubuntu 24.04's patched PHP is still "8.3.6". Check the package version against USN-8948-1 (for 24.04 that's 8.3.6-0ubuntu0.24.04.13) instead.

How to update common setups

  • Debian: fixed in 8.4.27-1~deb13u1 (trixie) and 8.2.35-1~deb12u1 (bookworm), per the security tracker. On Debian or Ubuntu, run sudo apt update && sudo apt install --only-upgrade 'php8.3*', using your PHP version.
  • Ubuntu: fixed for 22.04, 24.04, 26.04 and 26.10. Older LTS releases get fixes through Ubuntu Pro.
  • RHEL, Alma and Rocky: Red Hat says RHEL 8, 9 and 10 are affected and fixes are rolling out. sudo dnf upgrade 'php*' picks them up. Mattias Geniar's live tracker reports Remi's repo and Ondřej Surý's packages have the fixed releases. I haven't confirmed those against each vendor's own notice.
  • Docker: the official php images are rebuilt. I checked php:apache, which reports 8.5.12. So are nextcloud:latest (PHP 8.5.12) and wordpress:latest (PHP 8.3.36). Pull the new image, rebuild your own images with docker build --pull, and recreate the containers. Restarting the old one isn't enough.
  • Nextcloud AIO: v14.4.1 ships the patched PHP. The release note says "Upgrade to this version now!"
  • Bundled PHP: anything with its own copy of PHP (FrankenPHP, control panels, local dev stacks) needs its own update. apt and dnf won't touch it.

Then restart. A running process keeps the old PHP in memory. Restart PHP-FPM (systemctl restart php8.3-fpm or php-fpm). For Apache with mod_php, do a full systemctl restart apache2 or httpd. In Geniar's testing, a graceful reload left the old PHP loaded.

If you're stuck on PHP 8.1 or older, the PHP project has no release for you. Look for a distro or vendor backport, use PHP's temporary settings (disable session.upload_progress.enabled, set memory_limit=-1), and plan the move to 8.4 or 8.5. PHP is clear that those settings "do not make an installation safe". Only updating fixes it.

My take

I wrote about GitLab's regex bug the other day, and this has the same shape on a much bigger scale. It's an old, deep bug in shared plumbing. AI turned it from theoretical into exploitable, and the window between disclosure and attack was effectively zero. For self-hosters, the lesson is to know where your PHP comes from and how to update it quickly. If you've been meaning to put Nextcloud or WordPress behind a VPN instead of the open internet, this is a good week to do it.

DampSnag