Two critical PHP flaws were patched today. Here is how to check your server tonight.
By Hitesh Saini · 21 October 2026
At 14:00 UTC today, the PHP project released PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Each release fixes two critical vulnerabilities that allow unauthenticated remote code execution.
Both flaws have a CVSS v4.0 score of 9.2. That means someone who has never logged in to your site could send it a crafted request and end up running their own code on your server. PHP's own team was blunt about it: "There are no complete mitigations, updating is the only safe choice."
How the flaws were found is also unusual. OpenAI says a security researcher at Calif identified a critical flaw using GPT-5.6 Sol, and that Wiz independently discovered and reported both vulnerabilities. OpenAI says that, to its knowledge, AI found these bugs before attackers did, which gave defenders time to prepare fixes before disclosure. Wiz co-founder Ami Luttwak told the Wall Street Journal that it is "the most severe AI vulnerability that was ever found."
How to check your PHP version, and what we found on ours
We started with our own VPS. We scanned the running containers for PHP and found none. Our blog runs on Ghost, which is built on Node, not PHP, so there was nothing to patch. To test the tooling, we used real PHP images instead (more on that below).
Your server is probably different. Start by finding every place PHP runs. On a Docker host, this loop asks each running container for its PHP version:
for c in $(docker ps --format '{{.Names}}'); do
v=$(docker exec "$c" php -v 2>/dev/null | head -n 1)
echo "$c: ${v:-no php binary found}"
doneIf a container has no php binary on its path, try php-fpm -v inside it.
On a server without Docker, check the binaries and the installed packages:
php -v
php-fpm8.3 -v # binary name varies by distribution and version
dpkg -l | grep -i php # Debian, Ubuntu
rpm -qa | grep -i php # RHEL, Fedora, AlmaLinux, RockyCheck the PHP that serves web requests, not just the one in your shell. One machine can have several versions side by side, and the CLI can be a different build from the one in your FPM pool or Apache module.
You can also look at what your site reports from the outside:
curl -sI https://your-site.example | grep -iE 'x-powered-by|server'Many servers hide this header, so getting no result tells you nothing either way.
We also wanted to know whether the fixed builds had reached Docker Hub yet. This evening we ran the official images, showing only the first line of output:
$ docker run --rm php:8.3-cli php -v
PHP 8.3.36 (cli) (built: Oct 21 2026 15:57:19) (NTS)
$ docker run --rm php:8.4-cli php -v
PHP 8.4.27 (cli) (built: Oct 21 2026 15:53:07) (NTS)Both official images had been rebuilt with the fixed versions within about two hours of the 14:00 UTC release.
The key point is that the PHP version comes from the image or the package, not from your app. Updating WordPress or Nextcloud does nothing for this bug. If your container was built from a cached image or runs from a pinned digest, it still has the old PHP, even though the same tag on Docker Hub now points to a fixed build.
What is fixed, and what is not
The fixed versions are PHP 8.5.12, 8.4.27, 8.3.36 and 8.2.35. Two vulnerabilities are fixed.
CVE-2026-109618: RCE via OOM corruption
When PHP runs out of memory, its out-of-memory handling can interrupt it while internal request data is only partly set up. The cleanup at the end of the request can then treat that invalid data as valid. Advisory: GHSA-w8hw-w9fx-j79w.
CVE-2026-109619: RCE via stale global state
Some internal state that belongs to a request can survive after the memory behind it has been freed. A later request handled by the same worker can then use that stale state before normal initialization resets it. Advisory: GHSA-6wmf-4rv5-p3rp.
Who is at risk
According to php.net, every PHP installation that serves web requests is at risk. That includes PHP-FPM, Apache with mod_php, php-cgi, FrankenPHP and LiteSpeed. PHP's team says the risk is highest for mod_php, where they demonstrated an exploit in which an unauthenticated request can trigger the attack before any application code runs. A security plugin inside your application runs too late to see that request.
On scale: WordPress's own figure, from April 2025, is more than 40 percent of websites. One researcher estimates more than 1.3 million internet-connected hosts are exposed, an estimate rather than a measurement.
The awkward parts
PHP 8.1 and older no longer get updates from the PHP project. Minimal source backports for 7.4, 8.0 and 8.1 exist in the php-src repository for people who build from source or have no LTS vendor. If neither applies, upgrade to a supported branch.
The volunteer-maintained Remi repository (rpms.remirepo.net) will not have updates on release day. php.net urges its users to switch to distribution packages or build from source.
PHP 8.2 reaches end of life on 31 December 2026 and gets no security fixes after that, so 8.2.35 buys you about ten weeks. Plan the move to 8.3 or newer now.
How to update, by stack
Distribution packages
Linux distributions and LTS vendors received the patches in advance, so check your vendor's security notices for the fixed packages. Once they are available, update through your package manager:
sudo apt update && sudo apt upgrade # Debian, Ubuntu
sudo dnf upgrade # RHEL, Fedora, AlmaLinux, RockyThen restart PHP-FPM or Apache. Running workers keep the old code in memory until they restart.
Distribution packages often keep the upstream version number and add fixes as backports, so a patched package may not say 8.3.36. Use your vendor's notice to find the fixed version.
Official Docker images
Pull your tag again and recreate the containers, so you run the rebuilt image and not the one already cached on disk:
docker compose pull
docker compose up -dIf you pin images by digest, update the digest. Then run the version loop from earlier to confirm what is actually running.
Custom images and builds from source
If your Dockerfile starts FROM php:..., rebuild it so the build fetches the fixed base image instead of reusing a cached one:
docker build --pull --no-cache -t your-image .PHP-based application images, such as the WordPress and Nextcloud ones, depend on their maintainers rebuilding them, so check with docker run --rm <image> php -v first. If you compile PHP yourself, build from the fixed versions or the 7.4, 8.0 and 8.1 backports.
If you cannot patch tonight
php.net lists measures that reduce exposure to specific attack paths. They do not make an installation safe, so use them only until you can update.
Partial measures for CVE-2026-109618: Set memory_limit=-1 where applications cannot override it, such as php_admin_value in the FPM pool or Apache config, then check that no pool, directory or application setting puts a finite limit back. Set a low post_max_size (8M without uploads, 32M with) and keep upload_max_filesize below it. Also limit decoded request size, field count, multipart parts, header count and nesting depth at the web server or proxy, including for requests without a usable Content-Length.
With PHP's memory limit gone, something else has to cap memory: use a container or cgroup limit so a runaway request kills only the affected worker. Do not use RLIMIT_AS or RLIMIT_DATA, because those can cause the vulnerable allocation failure.
Partial measures for CVE-2026-109619: Set session.upload_progress.enabled=0 and restart every worker, in php.ini, an FPM pool or server config that applies before request parsing. ini_set() and FPM .user.ini do not count. On PHP 8.5, also set fatal_error_backtraces=0 and restart every worker. Remove invalid or deprecated per-request settings that emit diagnostics before initialization. Hiding those diagnostics with error_reporting, display_errors or @ does not help. Finally, fix any fatal errors and execution timeouts that an attacker can trigger.
Here is how some of these look in one FPM pool. Adjust the path and values:
; Partial mitigation only. Not a fix.
; e.g. /etc/php/8.3/fpm/pool.d/www.conf
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 4M
php_admin_flag[session.upload_progress.enabled] = offNone of this replaces the patch. As PHP puts it, updating is the only safe choice.
Use the checker, but read its answer
OpenAI has published a PHP version checker as a Codex skill at github.com/openai/php2shell-checker, under the Apache-2.0 license. It reads the X-Powered-By and Server HTTP headers, or the output of a local php -v, and gives one of three answers:
OK (exit code 0), VULNERABLE (exit code 1), or UNKNOWN (exit code 3). UNKNOWN covers three cases: no version was found, the branch was not recognized, or the build is a newer prerelease.
We tested it against a live server. Using the official PHP 8.3.36 image from earlier, we started PHP's built-in server (php -S) on port 8080. This is what came back:
X-Powered-By: PHP/8.3.36 # response header from our test server
OK: PHP 8.3.36 # checker output, exit code 0$ docker run --rm php:8.1-cli php -v
PHP 8.1.34 (cli) (built: Dec 29 2025 23:35:21) (NTS)
$ node skills/phpcheck-url/scripts/phpcheck.js http://localhost:8097/index.php
VULNERABLE: PHP 8.1.34 # checker output, exit code 1The second test matters if you run an older tag: the official 8.1 image has not been rebuilt since December 2025, because that branch gets no upstream fixes, so pulling it today still gives you a vulnerable build.
That result is correct, but mind what it means. The checker reports a version, not a guarantee: OK means the version string it saw belongs to a fixed release, and it cannot see other virtual hosts, other containers, or another build behind a proxy.
UNKNOWN is not a pass, and the checker's own README says so. Many servers hide X-Powered-By on purpose and proxies often strip it, so remote checks will often return UNKNOWN. When that happens, check on the server itself, and treat any non-zero exit code as a reason to investigate.
Whichever route you take, keep a simple inventory of each PHP install, its version and where it came from: a package, an image tag, a pinned digest or a host. Your browser updates itself; your runtimes, base images and certificate bundles do not. We covered that same gap in The Browser Patched Itself. Your Server Did Not.
If you would rather not own this patch cycle yourself, that is the work we do. Our services cover keeping PHP, containers and the rest of a self-hosted stack up to date. You can also reach me directly on Fiverr (hiteshsaini459) or Upwork (hiteshsaini25).
How we made this: AI-assisted draft. We checked the facts against php.net, OpenAI's post and the advisory pages, and ran the Docker version tests and the checker test described above ourselves on 21 October 2026.