Yesterday I wrote about the critical PHP security release of 21 October. This is the follow up: what has happened since, what I think it means for anyone running PHP, and what I did about it.
The two flaws in plain terms
CVE-2026-109618 (PHP advisory) is about running out of memory at the wrong moment. If PHP runs out of memory while it is part way through setting up request data, the clean up at the end of the request can treat half built data as real. An attacker can control when that happens by sending a carefully shaped multipart form, the kind of request a browser sends when you upload a file.
CVE-2026-109619 (PHP advisory) is about state left behind. A request that dies at the wrong moment can leave internal state in a PHP worker after its memory has been freed, and a later request on that worker can use it. The confirmed path runs through session upload progress, a PHP feature that reports how far a file upload has got. It is on by default, and the page does not need to use sessions.
PHP rates both at 9.2 out of 10. Because they trigger before your application code runs, a well maintained WordPress site on a vulnerable PHP is still exposed.
Timeline of the public record
This is the public picture as I understand it on 22 October, not anything specific to my own systems.
- 20 October. The PHP security team posts advance notice of a security release for 21 October at 14:00 UTC, and says it may release earlier if details become public.
- 20 October. Details of the flaws leak. Huntress and Threat Landscape describe a Hacker News post naming them, later taken down, with the leak confirmed by researchers the same day.
- 21 October. PHP publishes the fixed releases and both advisories. It scheduled them for 14:00 UTC; Huntress puts them at roughly 13:30 UTC. Either way, they were public that afternoon.
- 21 October. Researchers report scanning and exploitation attempts in the wild within hours of the release, and some probes before it. Tenable also cites attempts seen before the flaws were public.
- 21 and 22 October. Several vendors, including Huntress and Tenable, have reported attempts to run commands, download loaders and plant web shells on vulnerable servers.
I will extend this timeline as my own monitoring produces results, in general terms, under What I am seeing below.
What that means in practice
Patch the same day
The lesson from that timeline is how little time there was. Attempts started around the fix itself, and serious payloads followed within a day. For a flaw like this, a weekly patch window is too slow. I covered the routine side of this in keep PHP up to date and why it matters, including checking that the PHP actually serving requests is the one you updated.
Check your version
According to PHP's supported versions page, as of October 2026:
- PHP 8.5 has active support until 31 December 2027 and security fixes until 31 December 2029.
- PHP 8.4 has active support until 31 December 2026 and security fixes until 31 December 2028.
- PHP 8.3 gets security fixes only, until 31 December 2027.
- PHP 8.2 gets security fixes only, and reaches end of life on 31 December 2026.
- PHP 8.1 and older, including every PHP 7 and PHP 5 release, are end of life and get no normal releases.
For this release only, PHP made minimal fixes for 7.4, 8.0 and 8.1 available as source code branches for people who build PHP themselves. They are not normal releases. Some vendors provide backports; if you rely on that, check you are actually receiving them.
Cheap hardening that still helps after patching
PHP recommends some limits that cost very little and stay useful once you are patched. A patched PHP still stops a worker that runs out of memory, so huge or deliberately awkward requests remain a nuisance.
- Cap request size. PHP suggests
post_max_size = 8Mwhere nothing uploads files, or 32M where uploads are needed, plus limits at the web server or proxy on body size, field count, multipart parts, headers and nesting depth. - Block multipart where nothing uploads. If an application never accepts file uploads, it has no reason to receive multipart form data.
- Turn off features you do not use. Very few sites rely on session upload progress. Setting
session.upload_progress.enabled = 0removes the confirmed path into the second flaw, and PHP suggests keeping it off after patching. On PHP 8.5, PHP also advisesfatal_error_backtraces = 0. These belong in the main PHP configuration, not inini_set()or.user.ini, because they must apply before the request is parsed. Restart every worker afterwards. - Do not limit memory with RLIMIT_AS or RLIMIT_DATA. PHP warns these can trigger the very allocation failure the first flaw relies on.
PHP is clear that none of these make an unpatched installation safe. They narrow the door; the update closes it.
The memory_limit = -1 workaround
One workaround needs more care than the rest. For the memory flaw only, PHP lists setting memory_limit=-1, which removes the per request memory limit altogether. Its advisory calls this "the primary configuration mitigation" because it "removes the predictable per-request limit that enables the confirmed exploit". The attacker can no longer steer a request into an out of memory failure at a moment of their choosing.
PHP attaches conditions. The announcement says to set it "in a way applications can't override", to "check that no pool, directory or application setting puts a finite limit back", and to restart every worker. It also says "an underlying allocation failure can still reach the vulnerable path" and that this "is not a complete mitigation". It does nothing for the second flaw.
The trade off is real. The PHP manual explains that the memory limit exists to stop "poorly written scripts" from "eating up all available memory on a server". With no limit, one runaway or hostile request can take everything else down with it. That is why PHP pairs the advice with a container or cgroup memory limit as "an availability boundary", ideally one that kills only the affected worker.
I have not used this setting and would not keep it long term. It is for people who cannot patch yet. If you can update, update.
Restrict what a compromised process can do
Much of what has been reported downloads a second stage and then calls home. Allowing outbound DNS and connections only where a site genuinely needs them makes that harder for anything that does get in.
Watch what PHP starts
A PHP worker that starts a shell, another interpreter or a network tool is worth an alert almost anywhere. So are bursts of workers dying on memory errors. Both are cheap signals that catch this attack and many others.
Checking your own server
These are examples to adapt. Replace 8.x with your PHP version, and run them on your own server only.
Which PHP is serving the web
The command line PHP and the PHP behind your web server can be different versions, so check both.
php -v
systemctl list-units 'php*-fpm*'
ps -eo pid,lstart,args | grep '[p]hp-fpm: master'
php-fpm8.x -vphp -v is the command line version. The ps line shows when each FPM master process started, and php-fpm8.x -v prints the version of the FPM binary on disk. Installing a package changes the binary, but the running process keeps the old code until the service restarts. If the master started before you updated, it is still running the old version. On RHEL-family systems the binary is usually just php-fpm.
Is it patched
The fixed versions are 8.5.12, 8.4.27, 8.3.36 and 8.2.35. This compares a version against the minimum for its branch.
# Set min to the fixed release for your branch
v=$(php -r 'echo PHP_VERSION;'); min=8.4.27
if [ "$(printf '%s\n' "$min" "$v" | sort -V | head -n1)" = "$min" ]; then echo "patched ($v)"; else echo "UPDATE NEEDED ($v)"; fiFor the web side, swap the first command for v=$(php-fpm8.x -v | awk 'NR==1{print $2}'). On Debian and Ubuntu, dpkg --compare-versions "$v" ge "$min" && echo patched does the same job.
Updating
On Debian and Ubuntu, update only the PHP packages rather than everything at once, then restart and check again.
sudo apt update
apt-cache policy php8.x-fpm
apt list --upgradable 2>/dev/null | grep -i php
sudo apt install --only-upgrade 'php8.x*'
sudo systemctl restart php8.x-fpm
ps -eo pid,lstart,args | grep '[p]hp-fpm: master'apt-cache policy shows the candidate version your repository actually offers. Third party PHP repositories can be a release behind, so if the candidate is older than the fixed version, wait for it or change source rather than assuming you are covered. A reload asks FPM to replace its workers gracefully; a restart stops and starts the whole service. After a security update I restart, so nothing old is left running, and then confirm the master start time is new.
# RHEL, Rocky, Alma (dnf)
sudo dnf check-update 'php*'
sudo dnf upgrade 'php*'
sudo systemctl restart php-fpm
# Docker: pull a fixed tag, then recreate the container
docker pull php:8.x-fpm
docker compose up -d --force-recreate example-siteWith Docker, restarting an old container does nothing; it has to be recreated from the new image. Check docker exec example-site php -v afterwards.
Check the hardening settings
Read the values the FPM side actually uses, not the command line ones.
php-fpm8.x -i | grep -E 'session.upload_progress.enabled|fatal_error_backtraces|post_max_size|upload_max_filesize|memory_limit'To switch off session upload progress, add a small file to the FPM configuration directory, for example /etc/php/8.x/fpm/conf.d/99-hardening.ini on Debian and Ubuntu or /etc/php.d/99-hardening.ini on RHEL-family systems.
session.upload_progress.enabled = 0
; PHP 8.5 only
fatal_error_backtraces = 0This must go in php.ini, conf.d or the pool configuration, not ini_set() or .user.ini, and it needs a restart. If memory_limit shows -1, read the warning above before leaving it that way.
Watch for trouble
for p in $(pgrep -f '^php-fpm: pool'); do ps --ppid "$p" -o ppid=,pid=,user=,args=; done
sudo grep -iE 'allowed memory size|exited on signal' /var/log/php*-fpm.log | tailThe first command lists anything a PHP worker has started. Normally it prints nothing; a shell, curl, wget or another interpreter is worth a look. The second shows recent memory exhaustion and crashed workers; log paths differ by system. A match is a prompt to look, not proof of an attack.
Cap request bodies at the web server
Examples to adapt to your own limits and uploads.
# nginx, in the http, server or location block
client_max_body_size 32m;
# Apache, in the server or directory context (bytes)
LimitRequestBody 33554432What I did
- Updated PHP to the fixed releases on 21 October, the day they were published.
- Applied the hardening PHP recommends.
- Checked for the published indicators of attack and found none.
- Started setting up monitoring for this traffic.
That is what I did and what I checked. It is not a claim that nothing could have happened before the fix; nobody running PHP could honestly say that about the hours before the release on 21 October. It is the reason I check rather than assume.
What I am seeing
The timeline above will be extended from this section as my monitoring produces results.
I am setting up monitoring for this traffic and will add a short summary here: weekly totals of probing requests that carried known scanner markers or hit known exploit paths, and whether any of them reached application code. The first update will follow after a week.
If you run PHP yourself
- Confirm the PHP serving each site is on 8.5.12, 8.4.27, 8.3.36 or 8.2.35 or later, and restart every worker.
- Plan a move off PHP 8.1 and earlier now, and off 8.2 before 31 December 2026.
- Turn off session upload progress unless you know you need it, and on 8.5 turn off fatal error backtraces.
- Cap request body size, and parts and headers per part where you can.
- Block multipart requests to applications that take no uploads.
- Treat
memory_limit=-1as a short stopgap only if you truly cannot update yet, with a memory limit around each worker. - Restrict outbound traffic to what each site needs.
- Alert when a PHP worker starts a shell or network tool.
- Search your logs using the indicators in the vendor write-ups below.
If you are not sure whether your sites are on a fixed PHP, or you want a second pair of eyes on the limits above, get in touch and I will take a look.