
October 23, 2026
15 min read
On 21 October 2026 the PHP project published two advisories within minutes of each other, both rated 9.2 on the CVSS v4.0 scale, both allowing remote code execution without any login. Within two days, government departments in several countries had pulled websites offline rather than risk running unpatched. For anyone in Nepal running a Laravel application, a WordPress site, or a custom PHP system on a VPS — whether built in-house or through web development work — this was not an abstract advisory. It was a same-week patching job on every server you own.
I run roughly twenty sites across two servers, so I spent the morning after disclosure doing exactly that work. This article is what I learned doing it: what the two flaws actually are, who found them, which servers are genuinely at risk and which are not, and the precise commands that fix it. If you would rather hand that work to somebody else, my Linux system administration service covers it.
Quick answer: CVE-2026-109618 and CVE-2026-109619 are memory-corruption bugs in PHP's error and request-cleanup paths. An unauthenticated attacker can reach them over normal HTTP and, in confirmed cases, run code as your web user.
Fix: upgrade to PHP 8.2.35, 8.3.36, 8.4.27 or 8.5.12. If you cannot patch within the hour, set session.upload_progress.enabled=0 and, on PHP 8.5, fatal_error_backtraces=0, then restart every worker.
What exactly are CVE-2026-109618 and CVE-2026-109619?
They are two separate bugs that share a root cause: PHP holds internal state that belongs to a single request, and in certain error paths that state outlives the memory it points at. Both were published by Jakub Zelenka on 21 October 2026, and both carry the same CVSS v4.0 vector — AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H — meaning network-reachable, no privileges, no user interaction, with high impact on confidentiality, integrity and availability.
The "high attack complexity" rating is the only comfort, and it is thinner than it sounds. It reflects that an attacker needs several attempts and some luck with memory layout, not that the attack needs special access.
CVE-2026-109618 — remote code execution via OOM corruption
PHP has historically handled out-of-memory conditions with longjmp(), a C mechanism that abandons the current call stack and jumps elsewhere. That works when the code it abandons was in a consistent state. The bug is that in some contexts, request memory is reachable before it has been fully initialised. If an allocation fails at that moment, PHP leaves the request structures half-built, and the shutdown routine then reads or branches on values that were never valid.
The advisory lists several ways an attacker can force that allocation failure using nothing but a crafted HTTP request:
- Multipart requests without a usable declared content length can slip past the initial
post_max_sizecheck. - Each multipart part may carry an unlimited number of headers, and PHP's in-memory representation of a header costs far more than its bytes on the wire.
- Empty file controls do not count towards
max_file_uploads, yet each one still builds a file metadata array. Nested field names multiply that work.
The weakness classes recorded against it are CWE-416 (use after free), CWE-755, CWE-824, CWE-1325 and CWE-1419 — a list that reads like a summary of everything that can go wrong when cleanup code trusts a pointer it should not.
CVE-2026-109619 — remote code execution via stale global state
The second flaw is the more elegant of the two, and in practice the more dangerous, because it bridges two separate HTTP requests.
A PHP worker process serves many requests in its lifetime. Between them, global state — error state, session state, object state, extension state — must be reset before the memory backing it is released. The advisory frames the bug as a producer and a consumer:
- A producer is any operation that leaves request-owned state published somewhere global. Fatal errors do it: a parse failure, an uncaught error, a failed
require, a script hittingmax_execution_time. So do out-of-memory conditions, and diagnostics raised during extension cleanup. - A consumer is any operation that runs before normal request initialisation and reads that state. Two are confirmed: diagnostics raised while applying certain INI settings, and session upload-progress handling.
Chain them and the attack is: send one request that fatally errors in the right place, then send a second request to the same worker that reads the freed memory before PHP has reset it. The second request is where code execution happens.
Two specifics are worth committing to memory. On PHP 8.5, fatal_error_backtraces stores request-owned backtrace state that is never reset on shutdown — this is the basis of the confirmed Apache mod_php exploit. And session upload progress is enabled by default and requires no session_start() call in your code, which means an application that never knowingly uses sessions is still exposed through it.
Who found these flaws, and who fixed them?
Both advisories credit the same four reporters: lcfr-hax, sagitzadik-wiz (Wiz), tomash-areion (Areion) and amyb-oai (OpenAI). The remediation was written by four PHP core developers: Arnaud Le Blanc, Ilija Tovilo, Jakub Zelenka and Niels Dossche.
That attribution matters, because a great deal of inaccurate commentary circulated in the first forty-eight hours. I received two separate forwarded messages about this before I checked the primary sources, and both contained errors — one stated flatly that no patch existed on a day when patches had already shipped, and both attached a severity score that did not match the published vector. One even credited the discovery to AI model names rather than to the researchers and vendors above.
The lesson generalises well beyond this incident: when a security claim reaches you through a forwarded message, read the vendor advisory before you act on it. For PHP that means php.net and the php/php-src GitHub security advisories, not a screenshot. I nearly dismissed a genuine critical advisory because the message describing it was wrong in its details.
Is my server actually vulnerable?
Not every PHP deployment is equally exposed, and the difference comes down to which SAPI you run.
| Server setup | CVE-2026-109618 | CVE-2026-109619 |
|---|---|---|
Apache mod_php | Confirmed. Full RCE demonstrated over HTTP with default limits. | Affected. .htaccess and php_value settings apply before request startup, creating the early consumer. |
| PHP-FPM (nginx or Apache proxy) | Source analysis suggests exposure; no complete exploit demonstrated. | Conditional, but the session upload path works on a normal dynamic pool. |
| FrankenPHP | Likely susceptible. | No early consumer found in default config, which the advisory stresses is not immunity. |
| LSAPI | Likely, unvalidated. | Conditional candidate. |
If you run shared hosting in Nepal — and most small business sites here do — you are almost certainly on Apache with mod_php or LiteSpeed. That is the worst column of that table, and you cannot patch it yourself. Ask your host, in writing, which PHP build they are running and when they patched.
If you run your own VPS with PHP-FPM behind nginx, you are in better shape on the first bug and still exposed on the second through the upload-progress path.
A short history of PHP, and why this keeps happening
PHP began in 1994 as a set of CGI binaries Rasmus Lerdorf wrote to track visits to his online CV. He called them Personal Home Page Tools and released the source in 1995. PHP/FI 2.0 followed in 1997. The language as anyone would recognise it arrived in 1998, when Andi Gutmans and Zeev Suraski rewrote the parser for PHP 3 and renamed it to the recursive "PHP: Hypertext Preprocessor".
PHP 4 arrived in 2000 on the new Zend Engine. PHP 5 followed in 2004 with a real object model, and stayed in service for over a decade. PHP 6 was abandoned entirely — an ambitious attempt to make Unicode native to the language that collapsed under its own complexity, and the reason the version number jumps. PHP 7 landed in 2015 with the rewritten Zend Engine 3 and roughly doubled performance. PHP 8 followed in 2020 with the JIT compiler, named arguments, attributes and union types. PHP 8.5 shipped in November 2025.
Thirty years of continuous development, written in C, running on an enormous share of the web. Memory-safety bugs in that codebase are not a sign of neglect; they are the standing cost of the architecture. The notable ones form a recognisable pattern:
- CVE-2012-1823 — the
php-cgiquery-string flaw that let an attacker pass command-line switches to the interpreter. Exploited at scale for years after the patch. - CVE-2019-11043 — a buffer underflow in PHP-FPM reachable through a common nginx configuration, giving remote code execution on a very large number of servers.
- CVE-2024-4577 — CGI argument injection on Windows, caused by character-encoding conversion, and mass-exploited within days.
- CVE-2026-109618 / 109619 — the current pair, and the first to target the error and cleanup paths that nearly every request touches.
What links them is that none required an application bug. Your code could be flawless and you would still be exposed, because the vulnerability is in the engine beneath it. That is the single most important thing for a site owner to understand: updating WordPress, or Laravel, or your own code, does nothing for this class of flaw. Only the PHP package itself matters.
How do I patch this properly?
The sequence below is what I ran on production servers running Ubuntu with the Sury PPA, which is the usual setup for a Nepali VPS.
Step 1 — find out what you are actually running
php -v
dpkg-query -W -f='${Version}\n' php8.3-fpm
apt-cache policy php8.3-fpm
Compare the installed version against the fixed list: 8.2.35, 8.3.36, 8.4.27, 8.5.12. Note that distributions backport fixes without changing the upstream version number, so Ubuntu's 8.5.4-0ubuntu1.5 may or may not contain the patch. Check the package changelog rather than guessing:
apt-get changelog php8.5-fpm | grep -i 109618
One caveat if you land on 8.2. It is patched, but the branch is on security-only support, and that ends on 31 December 2026 — about ten weeks after this advisory. 8.2.35 closes these two CVEs and then 8.2 stops receiving fixes at all. If you are choosing a branch to move onto rather than patching in place, choose 8.3 (security fixes until the end of 2027) or 8.4 (actively supported until the end of 2028). Patching to 8.2 today is correct; still being on it in 2027 is this same problem again, with the date already known.
Step 2 — apply the interim mitigations first
Do this before the upgrade, because it takes seconds and protects you if the upgrade turns out to need a maintenance window. Create a file that sorts last in your conf.d directory — ordering matters, and a later file will silently undo an earlier one:
; /etc/php/8.3/fpm/conf.d/99-zz-cve-2026-109618-109619.ini
session.upload_progress.enabled = 0
fatal_error_backtraces = 0
Then restart — not reload. The advisory is explicit that every worker must be replaced, and a reload can leave old workers alive:
sudo systemctl restart php8.3-fpm
fatal_error_backtraces only exists on PHP 8.5; it is harmless elsewhere. Upload progress is a progress-bar feature almost no modern framework uses, so disabling it is close to free.
Step 3 — upgrade the packages
sudo apt-get update
sudo apt-get install --only-upgrade $(apt-get -s upgrade | awk '/^Inst php8.3/ {print $2}')
sudo systemctl restart php8.3-fpm
php -v
That awk line exists for a practical reason. A naive apt-get install --only-upgrade php8.3-* fails on this stack, because transitional packages like php8.3-json have no installation candidate, and apt aborts the whole transaction rather than part of it. The upgrade appears to run and nothing changes. Taking the package list from apt's own upgrade plan avoids it.
Step 4 — verify, and do not trust the exit code
php -v
curl -s -o /dev/null -w "%{http_code}\n" https://yourdomain.com/
Check the version actually moved and your sites still answer. A package manager reporting success is not the same as a running service on the new build.
There is a subtlety worth knowing here. php -v reports the command-line binary, which is a different process from the one serving your website. FPM workers load their configuration at startup and keep it until they are replaced, so a server can genuinely be running patched code on the command line while every live request is still handled by a worker started before the upgrade. The only reliable confirmation is to check the running service rather than the CLI:
systemctl show php8.3-fpm -p ActiveEnterTimestamp
sudo systemctl restart php8.3-fpm
If that timestamp predates your upgrade, the workers serving traffic are still the old ones and the patch has not taken effect where it matters. This is the same reasoning behind restarting rather than reloading for the interim mitigation, and it is the step most commonly skipped.
What about PHP 8.1 and older?
There are no official releases for PHP 8.1, 8.0 or 7.4. The PHP security team published minimal source backports in branches named PHP-<version>-CVE-2026-109618-109619, intended for people building from source. They are not releases, and most site owners should not be compiling PHP.
If you are on 8.1 or older on a public server, you have three options, in order of preference: move the application to a supported branch, get a backported build from your distribution vendor, or take the service offline. Running an unsupported PHP branch on a public web server during an actively discussed RCE is not a position to be in.
Stuck on an old PHP version with no patch coming?
This is the position a lot of Nepali businesses are in this week: an application built on PHP 7.4 or 8.1 a few years ago, running fine, and suddenly on the wrong side of a critical advisory with no official fix available. There is no safe way to leave it there.
Moving an application onto a supported branch is usually smaller work than people expect. Of the two Laravel applications I migrated this week, both ran on PHP 8.3 unchanged — the work was testing each page and switching the handler, not rewriting code. Where a rewrite genuinely is needed, it is far better to learn that from a test run than during an incident.
This is work I do: website migration to move an application onto a supported branch, custom software development where the code itself has to change, and support and maintenance if you would rather someone else watched the advisories. Not sure which applies? Tell me what you are running and I will tell you honestly — including if the answer is that you do not need me.
On my own older server this was straightforward to resolve. Two Laravel 8 applications were still bound to PHP 8.1. Both ran cleanly on 8.3 once tested, so the handler socket moved from php8.1-fpm.sock to php8.3-fpm.sock, the sites were verified page by page, and the entire 8.1, 8.0 and 7.4 package set was then removed. Fifteen packages went, three supported branches remained, and every site stayed up.
One hard-won caution from that work: before removing a PHP branch, search your Apache configuration with grep -R, not grep -r. The lowercase form does not follow symbolic links, and sites-enabled is a directory of symlinks. It will tell you nothing uses the version you are about to delete, and it will be wrong. I took two sites down for several hours learning that.
Checklist for a Nepali business owner
- Find out who controls your PHP version. On shared hosting it is your provider. On a VPS it is you. There is no third option.
- Ask your host in writing which PHP version they run and the date they patched. A host that cannot answer within a day is telling you something.
- If you self-host, patch today and apply the two INI settings while you do it.
- Check for an unsupported branch. Anything on 8.1 or below needs a plan this week, not this quarter.
- Review your logs afterwards with
error_reporting=E_ALLin a staging environment. The advisory specifically suggests looking for warnings raised before your script runs, diagnostics during cleanup, and requests that hit the execution timeout.
If any of that is beyond what you want to take on, support and maintenance is the service that covers it, and you can get in touch directly.
The part most coverage missed
Almost everything written about this incident in its first week focused on severity — the 9.2 score, the percentage of the web running PHP, the government sites taken offline. Very little of it addressed the question an actual site owner has, which is whether their specific setup is reachable and what single command fixes it.
The honest summary is narrower than the headlines. If you run Apache with mod_php, this is as serious as it has been described and you should treat it as an emergency. If you run PHP-FPM, the confirmed path is the session upload-progress one, which a single INI line closes while you schedule the upgrade properly. If you are on managed hosting, your exposure is entirely a function of how quickly your provider moves, and your job is to ask them.
And if you are still on PHP 8.1 because an old application was never migrated, this advisory is the moment that decision stops being deferrable. There is no patch coming.
Written by Kokil Thapa, full-stack developer, Kathmandu. Sources: the PHP project advisories GHSA-w8hw-w9fx-j79w and GHSA-6wmf-4rv5-p3rp, and php.net release announcements. Last reviewed: October 2026.
Frequently Asked Questions
0 Comments
Leave a comment
Your email is not published. Comments appear once they have been read. Sign in to have your details filled in.

