ARTICLE

Two Critical PHP Flaws: How I Checked My Services

PHPDockersecurity

On October 21 the Italian CERT-AGID flagged two critical vulnerabilities in the PHP core, CVE-2026-109618 and CVE-2026-109619, both leading to remote code execution. Fixed releases came out the same day. I checked my services: one was already up to date, one does not use PHP, one ran PHP-FPM in a Docker container whose image had not been rebuilt in weeks. The third one is the story worth telling.

What the official sources say

I took the facts from the CERT-AGID bulletin (in Italian) and checked them against the PHP project's GitHub advisories (GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp), the php.net announcements and NVD.

The CERT-AGID bulletin also mentions proofs of concept in circulation, "of not fully verified effectiveness". The official advisories do not say they are public: I am reporting that as CERT-AGID's claim, I have not verified it. It changes little: you update anyway, and now.

If you want a quick check from the outside without opening a terminal, I built a free PHP version check: it reads what your site declares and tells you whether it is patched. It cannot see everything, so the commands below are still the real check.

First question: which PHP is actually running

From a shell:

php -v
php -r 'echo PHP_VERSION, " ", PHP_SAPI, PHP_EOL;'

Careful: this gives you the version and SAPI of the command line, which is cli. The PHP serving your pages can be a different binary with a different php.ini. On an FPM server, ask it directly:

php-fpm -v
php-fpm -i | grep -i upload_progress

The binary name depends on the distribution (php-fpm8.3 on Debian and Ubuntu, for instance). In a Docker container: docker compose exec app php-fpm -v, where app is your service name.

From the outside, many servers just tell you:

curl -sI https://yoursite.example | grep -i x-powered-by

If the response contains something like X-Powered-By: PHP/8.x.y, your server is announcing its exact version to everyone. On the day a CVE lands, that is the first filter anyone hunting for targets uses. Turn it off with expose_php = Off in php.ini. It is not a defense, a vulnerable version stays vulnerable, but there is no reason to give the information away.

Second question: mod_php or FPM

The SAPI changes the risk, so check it on the web PHP, not on the CLI. The most direct way is a temporary file with an unguessable name, deleted as soon as you have read it:

<?php
echo PHP_VERSION, ' ', PHP_SAPI, ' upload_progress=', ini_get('session.upload_progress.enabled');

apache2handler means mod_php. fpm-fcgi means PHP-FPM. The same value shows up as "Server API" in phpinfo(), which exposes a lot more: if you use it, remove it right away.

The temporary mitigation, and its limits

The bulletin lists session.upload_progress.enabled = 0 as a temporary measure. The official advisory is more precise: it applies to CVE-2026-109619, blocks the demonstrated attack path through the session and not the others, and needs a worker restart to take effect. For CVE-2026-109618 the advisory suggests different measures, worth reading there. Every source agrees the only complete fix is updating.

If you need a few hours before you can update, turn it off and restart FPM or Apache. If your code uses the session-based upload progress bar, that will stop working: better to know in advance.

The container case: FROM php:8.x-fpm does not patch itself

The service with the problem was a Docker application whose Dockerfile starts from a branch tag, something like:

FROM php:8.x-fpm

The common belief is that a branch tag like 8.4-fpm or 8.3-fpm keeps you current. That is only half true. On the registry the tag moves to the latest patch (official images with the fixed versions were available by the evening of the 21st). But on your server Docker uses the image it has cached, pulled on the day of the last build. Until you rebuild, the container keeps running the PHP from weeks ago, whatever the Dockerfile says.

And a plain docker compose build is not enough: without --pull it reuses the local base image. What you need is:

docker compose build --pull
docker compose up -d
docker compose exec app php-fpm -v

The last command is the check: until you see the fixed version, you are not done. For services that run a prebuilt image with no build step, the equivalent is docker compose pull followed by up -d.

To stop relying on memory next time, two approaches work well together:

A container gives you reproducibility, and reproducibility includes the old PHP, faithfully reproduced until you change it. If you run Laravel in Docker, I also wrote about queue timeouts and crashes in containers.

On shared hosting

Here the work belongs to the provider, but the check is still yours. Many shared hosts still run Apache with mod_php, which is the most exposed case. In the control panel, find the PHP version selector: check that the branch is supported and the patch level is the fixed one. If the panel only shows "8.3" without the patch, the temporary file above gives you the exact version. If it is still old after a day or two, contact support and cite both CVEs.

Checklist

The check took me less than an hour. The vulnerable service was not the one I would have thought of first: it was the one that had been running fine for weeks and that nobody touched.

← All articles